From 168201c755f50cefb633598e5488a88bf48068d9 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 15 Jul 2024 12:25:54 +0200 Subject: [PATCH 001/150] refs #7283 itemList table --- src/pages/Item/ItemList.vue | 477 +++++------------------------------- 1 file changed, 68 insertions(+), 409 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index f1e3629cd..334ef2604 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -3,114 +3,48 @@ import { onMounted, ref, computed, reactive, onUnmounted } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; -import FetchData from 'components/FetchData.vue'; -import FetchedTags from 'components/ui/FetchedTags.vue'; -import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue'; -import VnInput from 'src/components/common/VnInput.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; -import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue'; -import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; -import ItemSummary from '../Item/Card/ItemSummary.vue'; -import VnPaginate from 'components/ui/VnPaginate.vue'; -import ItemListFilter from './ItemListFilter.vue'; - +import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDateFormat } from 'src/filters/date.js'; import { dashIfEmpty } from 'src/filters'; -import { useSummaryDialog } from 'src/composables/useSummaryDialog'; -import { useVnConfirm } from 'composables/useVnConfirm'; import axios from 'axios'; -import RightMenu from 'src/components/common/RightMenu.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; -import VnImg from 'src/components/ui/VnImg.vue'; const router = useRouter(); const stateStore = useStateStore(); const { t } = useI18n(); -const { viewSummary } = useSummaryDialog(); -const { openConfirmationModal } = useVnConfirm(); +const tableRef = ref(); -const paginateRef = ref(null); -const itemTypesOptions = ref([]); -const originsOptions = ref([]); -const buyersOptions = ref([]); -const intrastatOptions = ref([]); -const itemCategoriesOptions = ref([]); -const visibleColumns = ref([]); -const allColumnNames = ref([]); - -const exprBuilder = (param, value) => { - switch (param) { - case 'category': - return { 'ic.name': value }; - case 'buyerFk': - return { 'it.workerFk': value }; - case 'grouping': - return { 'b.grouping': value }; - case 'packing': - return { 'b.packing': value }; - case 'origin': - return { 'ori.code': value }; - case 'typeFk': - return { 'i.typeFk': value }; - case 'intrastat': - return { 'intr.description': value }; - case 'name': - return { 'i.name': { like: `%${value}%` } }; - case 'producer': - return { 'pr.name': { like: `%${value}%` } }; - case 'id': - case 'size': - case 'subname': - case 'isActive': - case 'weightByPiece': - case 'stemMultiplier': - case 'stems': - return { [`i.${param}`]: value }; - } +const itemFilter = { + include: [ + { + relation: 'trainingCourseType', + scope: { + fields: ['id', 'name'], + }, + }, + { + relation: 'trainingCenter', + scope: { + fields: ['id', 'name'], + }, + }, + ], }; - -const params = reactive({ isFloramondo: false, isActive: true }); - -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' - ? { 'update:modelValue': () => applyColumnFilter(col) } - : { - 'keyup.enter': () => applyColumnFilter(col), - }; -}; - const columns = computed(() => [ { label: '', name: 'picture', align: 'left', - columnFilter: null, }, { label: t('item.list.id'), name: 'id', field: 'id', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, + isId: true, + chip: { + condition: () => true, }, }, { @@ -118,101 +52,41 @@ const columns = computed(() => [ field: 'grouping', name: 'grouping', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, - format: (val) => dashIfEmpty(val), }, { label: t('item.list.packing'), field: 'packing', name: 'packing', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, - format: (val) => dashIfEmpty(val), }, { label: t('globals.description'), field: 'name', name: 'description', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, + create: true, }, { label: t('item.list.stems'), field: 'stems', name: 'stems', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, }, { label: t('item.list.size'), field: 'size', name: 'size', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, }, { label: t('item.list.typeName'), field: 'typeName', name: 'typeName', align: 'left', - sortable: true, - columnFilter: { - component: VnSelect, - filterParamKey: 'typeFk', - type: 'select', - filterValue: null, - event: getInputEvents, - attrs: { - options: itemTypesOptions.value, - 'option-value': 'id', - 'option-label': 'name', - dense: true, - }, + component: 'select', + attrs: { + url: 'ItemType', + fields: ['id', 'name'], }, }, @@ -221,18 +95,10 @@ const columns = computed(() => [ field: 'category', name: 'category', align: 'left', - sortable: true, - columnFilter: { - component: VnSelect, - type: 'select', - filterValue: null, - event: getInputEvents, - attrs: { - options: itemCategoriesOptions.value, - 'option-value': 'name', - 'option-label': 'name', - dense: true, - }, + component: 'select', + attrs: { + url: 'ItemCategory', + fields: ['id', 'name'], }, }, @@ -241,18 +107,10 @@ const columns = computed(() => [ field: 'intrastat', name: 'intrastat', align: 'left', - sortable: true, - columnFilter: { - component: VnSelect, - type: 'select', - filterValue: null, - event: getInputEvents, - attrs: { - options: intrastatOptions.value, - 'option-value': 'description', - 'option-label': 'description', - dense: true, - }, + component: 'select', + attrs: { + url: 'Intrastat', + fields: ['id', 'description'], }, }, { @@ -260,18 +118,10 @@ const columns = computed(() => [ field: 'origin', name: 'origin', align: 'left', - sortable: true, - columnFilter: { - component: VnSelect, - type: 'select', - filterValue: null, - event: getInputEvents, - attrs: { - options: originsOptions.value, - 'option-value': 'code', - 'option-label': 'code', - dense: true, - }, + component: 'select', + attrs: { + url: 'Origin', + fields: ['id', 'name'], }, }, { @@ -279,36 +129,13 @@ const columns = computed(() => [ field: 'userName', name: 'userName', align: 'left', - sortable: true, - columnFilter: { - component: VnSelect, - filterParamKey: 'buyerFk', - type: 'select', - filterValue: null, - event: getInputEvents, - attrs: { - options: buyersOptions.value, - 'option-value': 'id', - 'option-label': 'nickname', - dense: true, - }, - }, }, { label: t('item.list.weightByPiece'), field: 'weightByPiece', name: 'weightByPiece', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, + component: 'input', format: (val) => dashIfEmpty(val), }, { @@ -316,16 +143,7 @@ const columns = computed(() => [ field: 'stemMultiplier', name: 'stemMultiplier', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, + component: 'input', format: (val) => dashIfEmpty(val), }, { @@ -333,40 +151,26 @@ const columns = computed(() => [ field: 'isActive', name: 'isActive', align: 'left', - sortable: true, - columnFilter: null, + component: 'checkbox', }, { label: t('item.list.producer'), field: 'producer', name: 'producer', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, + component: 'select', + attrs: { + url: 'Producer', + fields: ['id', 'name'], }, - format: (val) => dashIfEmpty(val), }, { label: t('item.list.landed'), field: 'landed', name: 'landed', align: 'left', - sortable: true, + component: 'date', format: (val) => dashIfEmpty(toDateFormat(val)), - columnFilter: null, - }, - { - label: '', - name: 'actions', - align: 'left', - columnFilter: null, }, ]); @@ -388,49 +192,11 @@ const cloneItem = async (itemFk) => { } }; -onMounted(async () => { - stateStore.rightDrawer = true; - const filteredColumns = columns.value.filter( - (col) => col.name !== 'picture' && col.name !== 'actions' - ); - allColumnNames.value = filteredColumns.map((col) => col.name); -}); - onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> - <FetchData - url="ItemTypes" - :filter="{ fields: ['id', 'name'], order: 'name ASC' }" - auto-load - @on-fetch="(data) => (itemTypesOptions = data)" - /> - <FetchData - url="ItemCategories" - :filter="{ fields: ['name'], order: 'name ASC' }" - auto-load - @on-fetch="(data) => (itemCategoriesOptions = data)" - /> - <FetchData - url="Intrastats" - :filter="{ fields: ['description'], order: 'description ASC' }" - auto-load - @on-fetch="(data) => (intrastatOptions = data)" - /> - <FetchData - url="Origins" - :filter="{ fields: ['code'], order: 'code ASC' }" - auto-load - @on-fetch="(data) => (originsOptions = data)" - /> - <FetchData - url="TicketRequests/getItemTypeWorker" - :filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }" - auto-load - @on-fetch="(data) => (buyersOptions = data)" - /> - <VnSubToolbar> + <!-- <VnSubToolbar> <template #st-data> <TableVisibleColumns :all-columns="allColumnNames" @@ -439,135 +205,28 @@ onUnmounted(() => (stateStore.rightDrawer = false)); @on-config-saved="visibleColumns = ['picture', ...$event, 'actions']" /> </template> - </VnSubToolbar> - <RightMenu> - <template #right-panel> - <ItemListFilter data-key="ItemList" /> - </template> - </RightMenu> - <QPage class="column items-center q-pa-md"> - <VnPaginate - ref="paginateRef" - data-key="ItemList" - url="Items/filter" - :order="['isActive DESC', 'name', 'id']" - :limit="12" - :expr-builder="exprBuilder" - :user-params="params" - :keep-opts="['userParams']" - :offset="50" - auto-load - > - <template #body="{ rows }"> - <QTable - :rows="rows" - :columns="columns" - row-key="id" - :pagination="{ rowsPerPage: 0 }" - class="full-width q-mt-md" - :visible-columns="visibleColumns" - :no-data-label="t('globals.noResults')" - @row-click="(_, row) => redirectToItemSummary(row.id)" - > - <template #top-row="{ cols }"> - <QTr> - <QTd - v-for="(col, index) in cols" - :key="index" - style="max-width: 100px" - > - <component - :is="col.columnFilter.component" - v-if="col.columnFilter" - v-model="col.columnFilter.filterValue" - v-bind="col.columnFilter.attrs" - v-on="col.columnFilter.event(col)" - dense - /> - </QTd> - </QTr> - </template> - <template #body-cell-picture="{ row }"> - <QTd> - <VnImg - size="50x50" - :id="row.id" - height="50px" - width="50px" - class="image" - /> - </QTd> - </template> - <template #body-cell-id="{ row }"> - <QTd @click.stop> - <QBtn flat color="primary"> - {{ row.id }} - </QBtn> - <ItemDescriptorProxy :id="row.id" /> - </QTd> - </template> - <template #body-cell-userName="{ row }"> - <QTd @click.stop> - <QBtn flat color="primary" dense> - {{ row.userName }} - </QBtn> - <WorkerDescriptorProxy :id="row.buyerFk" /> - </QTd> - </template> - <template #body-cell-description="{ row }"> - <QTd class="col"> - <span>{{ row.name }} {{ row.subName }}</span> - <FetchedTags :item="row" :max-length="6" /> - </QTd> - </template> - <template #body-cell-isActive="{ row }"> - <QTd> - <QCheckbox :model-value="!!row.isActive" disable /> - </QTd> - </template> - <template #body-cell-actions="{ row }"> - <QTd> - <QIcon - @click.stop=" - openConfirmationModal( - t(`All it's properties will be copied`), - t('Do you want to clone this item?'), - () => cloneItem(row.id) - ) - " - class="q-ml-sm" - color="primary" - name="vn:clone" - size="sm" - > - <QTooltip> - {{ t('globals.clone') }} - </QTooltip> - </QIcon> - <QIcon - @click.stop="viewSummary(row.id, ItemSummary)" - class="q-ml-md" - color="primary" - name="preview" - size="sm" - > - <QTooltip class="text-no-wrap"> - {{ t('Preview') }} - </QTooltip> - </QIcon> - </QTd> - </template> - </QTable> - </template> - </VnPaginate> - - <QPageSticky :offset="[20, 20]"> - <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" /> - <QTooltip class="text-no-wrap"> - {{ t('New item') }} - </QTooltip> - </QPageSticky> - </QPage> + </VnSubToolbar> --> + <VnTable + ref="tableRef" + data-key="ItemList" + url="Items" + url-create="Items" + save-url="Items/crud" + :create="{ + urlCreate: 'Items', + title: 'Create Item', + onDataSaved: () => tableRef.redirect(), + formInitialData: { + editorFk: entityId, + }, + }" + order="id ASC" + :columns="columns" + auto-load + :right-search="false" + :is-editable="false" + :use-model="true" + /> </template> <i18n> From 16b5b5d9a1111f31a8c07f53272a8f6f77ae71ba Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 15 Jul 2024 13:48:59 +0200 Subject: [PATCH 002/150] refs #7283 filter --- src/pages/Item/ItemList.vue | 64 +++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 334ef2604..4c49f068f 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -1,30 +1,39 @@ <script setup> import { onMounted, ref, computed, reactive, onUnmounted } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRouter } from 'vue-router'; +import { useRouter, useRoute } from 'vue-router'; +import VnImg from 'src/components/ui/VnImg.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; -import { toDateFormat } from 'src/filters/date.js'; +import { toDate } from 'src/filters'; import { dashIfEmpty } from 'src/filters'; import axios from 'axios'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; +const entityId = computed(() => route.params.id); const router = useRouter(); const stateStore = useStateStore(); const { t } = useI18n(); const tableRef = ref(); +const route = useRoute(); const itemFilter = { include: [ { - relation: 'trainingCourseType', + relation: 'itemType', scope: { fields: ['id', 'name'], }, }, { - relation: 'trainingCenter', + relation: 'intrastat', + scope: { + fields: ['id', 'description'], + }, + }, + { + relation: 'origin', scope: { fields: ['id', 'name'], }, @@ -34,8 +43,18 @@ const itemFilter = { const columns = computed(() => [ { label: '', - name: 'picture', + name: 'image', align: 'left', + columnField: { + component: VnImg, + attrs: (id) => { + return { + id, + width: '50px', + }; + }, + }, + columnFilter: false, }, { label: t('item.list.id'), @@ -88,6 +107,10 @@ const columns = computed(() => [ url: 'ItemType', fields: ['id', 'name'], }, + columnField: { + component: null, + }, + create: true, }, { @@ -100,6 +123,9 @@ const columns = computed(() => [ url: 'ItemCategory', fields: ['id', 'name'], }, + columnField: { + component: null, + }, }, { @@ -112,6 +138,10 @@ const columns = computed(() => [ url: 'Intrastat', fields: ['id', 'description'], }, + columnField: { + component: null, + }, + create: true, }, { label: t('item.list.origin'), @@ -123,6 +153,10 @@ const columns = computed(() => [ url: 'Origin', fields: ['id', 'name'], }, + columnField: { + component: null, + }, + create: true, }, { label: t('item.list.userName'), @@ -136,7 +170,9 @@ const columns = computed(() => [ name: 'weightByPiece', align: 'left', component: 'input', - format: (val) => dashIfEmpty(val), + columnField: { + component: null, + }, }, { label: t('item.list.stemMultiplier'), @@ -144,7 +180,9 @@ const columns = computed(() => [ name: 'stemMultiplier', align: 'left', component: 'input', - format: (val) => dashIfEmpty(val), + columnField: { + component: null, + }, }, { label: t('item.list.isActive'), @@ -163,6 +201,9 @@ const columns = computed(() => [ url: 'Producer', fields: ['id', 'name'], }, + columnField: { + component: null, + }, }, { label: t('item.list.landed'), @@ -170,7 +211,10 @@ const columns = computed(() => [ name: 'landed', align: 'left', component: 'date', - format: (val) => dashIfEmpty(toDateFormat(val)), + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.landed)), }, ]); @@ -209,9 +253,10 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <VnTable ref="tableRef" data-key="ItemList" - url="Items" + url="Items/filter" url-create="Items" save-url="Items/crud" + :filter="itemFilter" :create="{ urlCreate: 'Items', title: 'Create Item', @@ -223,6 +268,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); order="id ASC" :columns="columns" auto-load + redirect="Item" :right-search="false" :is-editable="false" :use-model="true" From afbcd2ebda00ec2487ebc090940f83405ce54f1f Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 15 Jul 2024 14:31:33 +0200 Subject: [PATCH 003/150] refs #7283 item filters --- src/pages/Item/ItemList.vue | 14 ++-- src/pages/Item/ItemTypeList.vue | 140 ++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 4c49f068f..e2c5f4bb3 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -7,9 +7,7 @@ import VnImg from 'src/components/ui/VnImg.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'src/filters'; -import { dashIfEmpty } from 'src/filters'; import axios from 'axios'; -import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; const entityId = computed(() => route.params.id); const router = useRouter(); @@ -104,7 +102,7 @@ const columns = computed(() => [ align: 'left', component: 'select', attrs: { - url: 'ItemType', + url: 'ItemTypes', fields: ['id', 'name'], }, columnField: { @@ -112,7 +110,6 @@ const columns = computed(() => [ }, create: true, }, - { label: t('item.list.category'), field: 'category', @@ -120,14 +117,13 @@ const columns = computed(() => [ align: 'left', component: 'select', attrs: { - url: 'ItemCategory', + url: 'ItemCategories', fields: ['id', 'name'], }, columnField: { component: null, }, }, - { label: t('item.list.intrastat'), field: 'intrastat', @@ -135,7 +131,7 @@ const columns = computed(() => [ align: 'left', component: 'select', attrs: { - url: 'Intrastat', + url: 'Intrastats', fields: ['id', 'description'], }, columnField: { @@ -150,7 +146,7 @@ const columns = computed(() => [ align: 'left', component: 'select', attrs: { - url: 'Origin', + url: 'Origins', fields: ['id', 'name'], }, columnField: { @@ -198,7 +194,7 @@ const columns = computed(() => [ align: 'left', component: 'select', attrs: { - url: 'Producer', + url: 'Producers', fields: ['id', 'name'], }, columnField: { diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 125672d60..c02f9bb43 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -1,20 +1,13 @@ <script setup> import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; - -import VnPaginate from 'src/components/ui/VnPaginate.vue'; -import VnLv from 'src/components/ui/VnLv.vue'; -import CardList from 'src/components/ui/CardList.vue'; -import ItemTypeSummary from 'src/pages/ItemType/Card/ItemTypeSummary.vue'; -import ItemTypeFilter from 'src/pages/ItemType/ItemTypeFilter.vue'; +import { ref, computed } from 'vue'; import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue'; -import { useSummaryDialog } from 'src/composables/useSummaryDialog'; -import RightMenu from 'src/components/common/RightMenu.vue'; +import VnTable from 'components/VnTable/VnTable.vue'; const router = useRouter(); const { t } = useI18n(); -const { viewSummary } = useSummaryDialog(); - +const tableRef = ref(); const redirectToItemTypeSummary = (id) => { router.push({ name: 'ItemTypeSummary', params: { id } }); }; @@ -56,52 +49,91 @@ const exprBuilder = (param, value) => { } } }; + +const columns = computed(() => [ + { + align: 'left', + name: 'id', + label: t('id'), + isId: true, + cardVisible: true, + }, + { + align: 'left', + name: 'code', + label: t('code'), + isTitle: true, + cardVisible: true, + create: true, + }, + { + align: 'left', + name: 'name', + label: t('name'), + cardVisible: true, + create: true, + }, + { + align: 'left', + name: 'worker', + label: t('worker'), + create: true, + component: 'select', + attrs: { + url: 'Workers', + fields: ['id', 'firstName'], + }, + cardVisible: true, + }, + { + align: 'left', + name: 'ItemCategory', + label: t('ItemCategory'), + create: true, + component: 'select', + attrs: { + url: 'ItemCategories', + fields: ['id', 'name'], + }, + cardVisible: true, + }, + { + align: 'left', + name: 'Temperature', + label: t('Temperature'), + create: true, + component: 'select', + attrs: { + url: 'Temperatures', + fields: ['id', 'name'], + }, + cardVisible: true, + }, +]); </script> <template> <ItemTypeSearchbar /> - <RightMenu> - <template #right-panel> - <ItemTypeFilter data-key="ItemTypeList" /> - </template> - </RightMenu> - <QPage class="column items-center q-pa-md"> - <div class="vn-card-list"> - <VnPaginate - data-key="ItemTypeList" - url="ItemTypes" - :order="['name']" - auto-load - :expr-builder="exprBuilder" - > - <template #body="{ rows }"> - <CardList - v-for="row of rows" - :key="row.id" - :title="row.code" - @click="redirectToItemTypeSummary(row.id)" - :id="row.id" - > - <template #list-items> - <VnLv :label="t('Name')" :value="row.name" /> - </template> - <template #actions> - <QBtn - :label="t('components.smartCard.openSummary')" - @click.stop="viewSummary(row.id, ItemTypeSummary)" - color="primary" - type="submit" - /> - </template> - </CardList> - </template> - </VnPaginate> - </div> - </QPage> - <QPageSticky :offset="[20, 20]"> - <QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> - <QTooltip> - {{ t('New item type') }} - </QTooltip> - </QPageSticky> + <VnTable + ref="tableRef" + data-key="ItemTypeList" + :url="`ItemTypes`" + :url-create="`ItemTypes`" + save-url="ItemTypes/crud" + :filter="courseFilter" + :create="{ + urlCreate: 'ItemTypes', + title: 'Create ItemTypes', + onDataSaved: () => tableRef.reload(), + formInitialData: { + workerFk: entityId, + }, + }" + order="id DESC" + :columns="columns" + auto-load + :right-search="false" + :is-editable="false" + :use-model="true" + /> </template> From daf99f47306f2ae346e9fd023bce9e8b8c222c7b Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 15 Jul 2024 15:00:43 +0200 Subject: [PATCH 004/150] refs #7283 itemRequestList --- src/pages/Item/ItemRequest.vue | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index ae6638953..10cb6c2a6 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -18,6 +18,7 @@ import useNotify from 'src/composables/useNotify.js'; import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js'; import axios from 'axios'; import RightMenu from 'src/components/common/RightMenu.vue'; +import { toDate } from 'src/filters'; const { t } = useI18n(); const { notify } = useNotify(); @@ -46,21 +47,28 @@ const columns = computed(() => [ name: 'id', field: 'id', align: 'left', - sortable: true, + isId: true, + chip: { + condition: () => true, + }, + cardVisible: true, }, { label: t('item.buyRequest.shipped'), field: 'shipped', name: 'shipped', align: 'left', - sortable: true, + component: 'date', + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.shipped)), }, { label: t('globals.description'), field: 'description', name: 'description', align: 'left', - sortable: true, }, { label: t('item.buyRequest.requester'), @@ -80,29 +88,31 @@ const columns = computed(() => [ field: 'price', name: 'price', align: 'left', - sortable: true, - format: (val) => toCurrency(val), + format: (row) => toCurrency(row.price), }, { label: t('item.buyRequest.attender'), field: 'attender', name: 'attender', align: 'left', - sortable: true, + attrs: { + url: 'Workers', + fields: ['id', 'firstName'], + }, }, { label: t('item.buyRequest.item'), field: 'item', name: 'item', align: 'left', - sortable: true, + component: 'input', }, { label: t('item.buyRequest.achieved'), field: 'achieved', name: 'achieved', align: 'left', - sortable: true, + component: 'input', }, { label: t('item.buyRequest.concept'), From b7ba8eec3da2524c3fc2834dc2e4ffb64dbfc20d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 16 Jul 2024 08:24:57 +0200 Subject: [PATCH 005/150] refs #7283 fix searchbar --- src/pages/Item/ItemList.vue | 41 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index e2c5f4bb3..17a62c5b9 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -3,11 +3,12 @@ import { onMounted, ref, computed, reactive, onUnmounted } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRouter, useRoute } from 'vue-router'; import VnImg from 'src/components/ui/VnImg.vue'; - +import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'src/filters'; import axios from 'axios'; +import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; const entityId = computed(() => route.params.id); const router = useRouter(); @@ -212,16 +213,21 @@ const columns = computed(() => [ }, format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.landed)), }, + { + align: 'right', + label: '', + name: 'tableActions', + actions: [ + { + title: t('Clone item'), + icon: 'vn:clone', + action: cloneItem, + isPrimary: true, + }, + ], + }, ]); -const redirectToItemCreate = () => { - router.push({ name: 'ItemCreate' }); -}; - -const redirectToItemSummary = (id) => { - router.push({ name: 'ItemSummary', params: { id } }); -}; - const cloneItem = async (itemFk) => { try { const { data } = await axios.post(`Items/${itemFk}/clone`); @@ -236,16 +242,11 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> - <!-- <VnSubToolbar> - <template #st-data> - <TableVisibleColumns - :all-columns="allColumnNames" - table-code="itemsIndex" - labels-traductions-path="item.list" - @on-config-saved="visibleColumns = ['picture', ...$event, 'actions']" - /> - </template> - </VnSubToolbar> --> + <VnSearchbar + data-key="ItemList" + :label="t('Search Item')" + :info="t('You can search by id')" + /> <VnTable ref="tableRef" data-key="ItemList" @@ -265,7 +266,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :columns="columns" auto-load redirect="Item" - :right-search="false" + :right-search="true" :is-editable="false" :use-model="true" /> From 85b030c7bc07fa7e2abf7e1c7e0d969c7000322d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 16 Jul 2024 08:31:04 +0200 Subject: [PATCH 006/150] refs #7283 view summary --- src/pages/Item/ItemList.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 17a62c5b9..0e444254f 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -9,8 +9,10 @@ import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'src/filters'; import axios from 'axios'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; +import { useSummaryDialog } from 'src/composables/useSummaryDialog'; const entityId = computed(() => route.params.id); +const { viewSummary } = useSummaryDialog(); const router = useRouter(); const stateStore = useStateStore(); const { t } = useI18n(); @@ -224,6 +226,11 @@ const columns = computed(() => [ action: cloneItem, isPrimary: true, }, + { + title: t('view Summary'), + icon: 'preview', + action: (row) => viewSummary(row.id), + }, ], }, ]); From cce1d891fb67b738a45d6f3d59cbcda6dd1dde5d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 16 Jul 2024 08:48:50 +0200 Subject: [PATCH 007/150] refs #7283 fix viewSummary --- src/pages/Item/ItemList.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 0e444254f..273374150 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -10,6 +10,7 @@ import { toDate } from 'src/filters'; import axios from 'axios'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; +import ItemSummary from '../Item/Card/ItemSummary.vue'; const entityId = computed(() => route.params.id); const { viewSummary } = useSummaryDialog(); @@ -229,7 +230,7 @@ const columns = computed(() => [ { title: t('view Summary'), icon: 'preview', - action: (row) => viewSummary(row.id), + action: (row) => viewSummary(row.id, ItemSummary), }, ], }, From 339f6e810bf87a9115691c69f447b43988b08e2d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 16 Jul 2024 10:34:00 +0200 Subject: [PATCH 008/150] refs #7283 fix descriptorproxy --- src/pages/Item/ItemList.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 273374150..6893d55a1 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -11,6 +11,7 @@ import axios from 'axios'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import ItemSummary from '../Item/Card/ItemSummary.vue'; +import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; const entityId = computed(() => route.params.id); const { viewSummary } = useSummaryDialog(); @@ -277,7 +278,14 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :right-search="true" :is-editable="false" :use-model="true" - /> + > + <template #column-userName="{ row }"> + <span class="link" @click.stop> + {{ row.userName }} + <WorkerDescriptorProxy :id="row.buyerFk" /> + </span> + </template> + </VnTable> </template> <i18n> From f29d873ed42abad1c45d641a51aa02bc2ab02db1 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 16 Jul 2024 12:15:39 +0200 Subject: [PATCH 009/150] refs #7283 fix request --- src/pages/Item/ItemRequest.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 10cb6c2a6..e5faff6b2 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -120,13 +120,13 @@ const columns = computed(() => [ name: 'concept', align: 'left', sortable: true, + component: 'input', }, { label: t('item.buyRequest.state'), field: 'state', name: 'state', align: 'left', - sortable: true, }, { label: '', @@ -252,7 +252,6 @@ onBeforeMount(() => { <TicketDescriptorProxy :id="row.ticketFk" /> </QTd> </template> - <template #body-cell-shipped="{ row }"> <QTd> <QBadge From 86ae827fda7a8c45b346fdfaa490225b10039cbd Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 16 Jul 2024 13:48:32 +0200 Subject: [PATCH 010/150] refs #7283 clone --- src/pages/Item/ItemList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 6893d55a1..c44438087 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -239,7 +239,7 @@ const columns = computed(() => [ const cloneItem = async (itemFk) => { try { - const { data } = await axios.post(`Items/${itemFk}/clone`); + const { data } = await axios.post(`Items/${itemFk.id}/clone`); if (!data) return; router.push({ name: 'ItemTags', params: { id: data.id } }); } catch (err) { From 4d394a98a4f3d2b23bd580fcbe0a0186892e9b0c Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 22 Jul 2024 08:24:59 +0200 Subject: [PATCH 011/150] refs #7283 fix yml list basicData --- src/i18n/locale/en.yml | 21 +++++++++++- src/i18n/locale/es.yml | 21 +++++++++++- src/pages/Item/Card/ItemBasicData.vue | 47 ++++++++++++++++----------- src/pages/Item/ItemList.vue | 1 - 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 2f1209a3a..a3d7362b3 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 @@ -1194,6 +1193,26 @@ item: stemMultiplier: Multiplier producer: Producer landed: Landed + basicData: + type: Type + reference: Reference + relevancy: Relevancy + stems: Stems + multiplier: Multiplier + generic: Generic + intrastat: Intrastat + expense: Expense + weightByPiece: Weight/Piece + boxUnits: Units/Box + recycledPlastic: Recycled Plastic + nonRecycledPlastic: Non recycled plastic + isActive: Active + hasKgPrice: Price in kg + isFragile: Fragile + isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...) + isPhotoRequested: Do photo + isPhotoRequestedTooltip: This item does need a photo + description: Description fixedPrice: itemId: Item ID groupingPrice: Grouping price diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index c5c4fab66..71b2c5919 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 @@ -1175,6 +1174,26 @@ item: stemMultiplier: Multiplicador producer: Productor landed: F. entrega + basicData: + type: Tipo + reference: Referencia + relevancy: Relevancia + stems: Tallos + multiplier: Multiplicador + generic: Genérico + intrastat: Intrastat + expense: Gasto + weightByPiece: Peso (gramos)/tallo + boxUnits: Unidades/caja + recycledPlastic: Plastico reciclado + nonRecycledPlastic: Plático no reciclado + isActive: Activo + hasKgPrice: Precio en kg + isFragile: Frágil + isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...) + isPhotoRequested: Hacer foto + isPhotoRequestedTooltip: Este artículo necesita una foto + description: Descripción fixedPrice: itemId: ID Artículo groupingPrice: Precio grouping diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue index eb486f551..07242635b 100644 --- a/src/pages/Item/Card/ItemBasicData.vue +++ b/src/pages/Item/Card/ItemBasicData.vue @@ -73,7 +73,7 @@ const onIntrastatCreated = (response, formData) => { <template #form="{ data }"> <VnRow class="row q-gutter-md q-mb-md"> <VnSelect - :label="t('basicData.type')" + :label="t('item.basicData.type')" v-model="data.typeFk" :options="itemTypesOptions" option-value="id" @@ -92,17 +92,20 @@ const onIntrastatCreated = (response, formData) => { </QItem> </template> </VnSelect> - <VnInput :label="t('basicData.reference')" v-model="data.comment" /> - <VnInput :label="t('basicData.relevancy')" v-model="data.relevancy" /> + <VnInput :label="t('item.basicData.reference')" v-model="data.comment" /> + <VnInput + :label="t('item.basicData.relevancy')" + v-model="data.relevancy" + /> </VnRow> <VnRow class="row q-gutter-md q-mb-md"> - <VnInput :label="t('basicData.stems')" v-model="data.stems" /> + <VnInput :label="t('item.basicData.stems')" v-model="data.stems" /> <VnInput - :label="t('basicData.multiplier')" + :label="t('item.basicData.multiplier')" v-model="data.stemMultiplier" /> <VnSelectDialog - :label="t('basicData.generic')" + :label="t('item.basicData.generic')" v-model="data.genericFk" :options="itemsWithNameOptions" option-value="id" @@ -129,7 +132,7 @@ const onIntrastatCreated = (response, formData) => { </VnRow> <VnRow class="row q-gutter-md q-mb-md"> <VnSelectDialog - :label="t('basicData.intrastat')" + :label="t('item.basicData.intrastat')" v-model="data.intrastatFk" :options="intrastatsOptions" option-value="id" @@ -156,7 +159,7 @@ const onIntrastatCreated = (response, formData) => { </VnSelectDialog> <div class="col"> <VnSelect - :label="t('basicData.expense')" + :label="t('item.basicData.expense')" v-model="data.expenseFk" :options="expensesOptions" option-value="id" @@ -168,61 +171,67 @@ const onIntrastatCreated = (response, formData) => { </VnRow> <VnRow class="row q-gutter-md q-mb-md"> <VnInput - :label="t('basicData.weightByPiece')" + :label="t('item.basicData.weightByPiece')" v-model.number="data.weightByPiece" :min="0" type="number" /> <VnInput - :label="t('basicData.boxUnits')" + :label="t('item.basicData.boxUnits')" v-model.number="data.packingOut" :min="0" type="number" /> <VnInput - :label="t('basicData.recycledPlastic')" + :label="t('item.basicData.recycledPlastic')" v-model.number="data.recycledPlastic" :min="0" type="number" /> <VnInput - :label="t('basicData.nonRecycledPlastic')" + :label="t('item.basicData.nonRecycledPlastic')" v-model.number="data.nonRecycledPlastic" :min="0" type="number" /> </VnRow> <VnRow class="row q-gutter-md q-mb-md"> - <QCheckbox v-model="data.isActive" :label="t('basicData.isActive')" /> - <QCheckbox v-model="data.hasKgPrice" :label="t('basicData.hasKgPrice')" /> + <QCheckbox + v-model="data.isActive" + :label="t('item.basicData.isActive')" + /> + <QCheckbox + v-model="data.hasKgPrice" + :label="t('item.basicData.hasKgPrice')" + /> <div> <QCheckbox v-model="data.isFragile" - :label="t('basicData.isFragile')" + :label="t('item.basicData.isFragile')" class="q-mr-sm" /> <QIcon name="info" class="cursor-pointer" size="xs"> <QTooltip max-width="300px"> - {{ t('basicData.isFragileTooltip') }} + {{ t('item.basicData.isFragileTooltip') }} </QTooltip> </QIcon> </div> <div> <QCheckbox v-model="data.isPhotoRequested" - :label="t('basicData.isPhotoRequested')" + :label="t('item.basicData.isPhotoRequested')" class="q-mr-sm" /> <QIcon name="info" class="cursor-pointer" size="xs"> <QTooltip> - {{ t('basicData.isPhotoRequestedTooltip') }} + {{ t('item.basicData.isPhotoRequestedTooltip') }} </QTooltip> </QIcon> </div> </VnRow> <VnRow> <VnInput - :label="t('basicData.description')" + :label="t('item.basicData.description')" type="textarea" v-model="data.description" fill-input diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index c44438087..169ee93d6 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -226,7 +226,6 @@ const columns = computed(() => [ title: t('Clone item'), icon: 'vn:clone', action: cloneItem, - isPrimary: true, }, { title: t('view Summary'), From f678c6304313cbc6d976c9df21f7f0e33c958dac Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 22 Jul 2024 13:35:41 +0200 Subject: [PATCH 012/150] refs #7283 request --- src/i18n/locale/en.yml | 2 +- src/pages/Item/ItemList.vue | 6 +---- src/pages/Item/ItemRequest.vue | 40 +++++++++++++++++----------------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index a3d7362b3..33862f766 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -1235,7 +1235,7 @@ item: requester: 'Requester' requested: 'Requested' price: 'Price' - attender: 'Atender' + attender: 'Attender' item: 'Item' achieved: 'Achieved' concept: 'Concept' diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 169ee93d6..2532a6df2 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -1,9 +1,8 @@ <script setup> -import { onMounted, ref, computed, reactive, onUnmounted } from 'vue'; +import { ref, computed, onUnmounted } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRouter, useRoute } from 'vue-router'; import VnImg from 'src/components/ui/VnImg.vue'; -import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'src/filters'; @@ -245,8 +244,6 @@ const cloneItem = async (itemFk) => { console.error('Error cloning item', err); } }; - -onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> @@ -274,7 +271,6 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :columns="columns" auto-load redirect="Item" - :right-search="true" :is-editable="false" :use-model="true" > diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index e5faff6b2..ed6f623aa 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -1,24 +1,14 @@ <script setup> import { ref, computed, onMounted, onBeforeMount, watch } from 'vue'; import { useI18n } from 'vue-i18n'; -import FetchData from 'components/FetchData.vue'; -import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; -import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; -import VnInput from 'src/components/common/VnInput.vue'; -import ItemRequestDenyForm from './ItemRequestDenyForm.vue'; -import ItemRequestFilter from './ItemRequestFilter.vue'; -import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; -import VnSelect from 'components/common/VnSelect.vue'; import { useStateStore } from 'stores/useStateStore'; import { useArrayData } from 'composables/useArrayData'; -import { toDateFormat } from 'src/filters/date'; import { toCurrency } from 'filters/index'; import useNotify from 'src/composables/useNotify.js'; -import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js'; import axios from 'axios'; -import RightMenu from 'src/components/common/RightMenu.vue'; import { toDate } from 'src/filters'; +import VnTable from 'components/VnTable/VnTable.vue'; const { t } = useI18n(); const { notify } = useNotify(); @@ -72,16 +62,15 @@ const columns = computed(() => [ }, { label: t('item.buyRequest.requester'), - name: 'requester', + field: 'requesterName', + name: 'requesterFk', align: 'left', - sortable: true, }, { label: t('item.buyRequest.requested'), field: 'quantity', name: 'requested', align: 'left', - sortable: true, }, { label: t('item.buyRequest.price'), @@ -95,10 +84,6 @@ const columns = computed(() => [ field: 'attender', name: 'attender', align: 'left', - attrs: { - url: 'Workers', - fields: ['id', 'firstName'], - }, }, { label: t('item.buyRequest.item'), @@ -218,7 +203,7 @@ onBeforeMount(() => { </script> <template> - <FetchData + <!-- <FetchData url="Workers" :filter="{ where: { role: 'buyer' } }" order="id" @@ -350,7 +335,22 @@ onBeforeMount(() => { @on-data-saved="onDenyAccept" /> </QDialog> - </QPage> + </QPage> --> + <VnTable + ref="tableRef" + data-key="itemRequest" + url="ticketRequests" + order="id DESC" + :columns="columns" + auto-load + > + <template #column-attender="{ row }"> + <span class="link" @click.stop> + {{ row.attenderFk }} + <WorkerDescriptorProxy :id="row.attenderFk" /> + </span> + </template> + </VnTable> </template> <i18n> From b668d07e2dc4fb453efcd1050abbc0f6fd570687 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 1 Aug 2024 13:20:33 +0200 Subject: [PATCH 013/150] refs #7283 fetchedTags --- src/pages/Item/ItemList.vue | 46 +++++++++++++++++++++++----------- src/pages/Item/ItemRequest.vue | 17 ++++++++++--- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 2532a6df2..a3b70372f 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -7,6 +7,7 @@ import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'src/filters'; import axios from 'axios'; +import FetchedTags from 'src/components/ui/FetchedTags.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import ItemSummary from '../Item/Card/ItemSummary.vue'; @@ -43,21 +44,21 @@ const itemFilter = { ], }; const columns = computed(() => [ - { - label: '', - name: 'image', - align: 'left', - columnField: { - component: VnImg, - attrs: (id) => { - return { - id, - width: '50px', - }; - }, - }, - columnFilter: false, - }, + // { + // label: '', + // name: 'image', + // align: 'left', + // columnField: { + // component: VnImg, + // attrs: (id) => { + // return { + // id, + // width: '50px', + // }; + // }, + // }, + // columnFilter: false, + // }, { label: t('item.list.id'), name: 'id', @@ -280,9 +281,24 @@ const cloneItem = async (itemFk) => { <WorkerDescriptorProxy :id="row.buyerFk" /> </span> </template> + <template #column-description="{ row }"> + <div class="row column full-width justify-between items-start"> + {{ row?.name }} + <div v-if="row?.subName" class="subName"> + {{ row?.subName.toUpperCase() }} + </div> + </div> + <FetchedTags :item="row" :max-length="6" /> + </template> </VnTable> </template> +<style lang="scss" scoped> +.subName { + text-transform: uppercase; + color: var(--vn-label-color); +} +</style> <i18n> es: New item: Nuevo artículo diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index ed6f623aa..0389ba864 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -13,7 +13,6 @@ import VnTable from 'components/VnTable/VnTable.vue'; const { t } = useI18n(); const { notify } = useNotify(); const stateStore = useStateStore(); -const workersOptions = ref([]); let filterParams = ref({}); const denyFormRef = ref(null); const denyRequestId = ref(null); @@ -91,6 +90,7 @@ const columns = computed(() => [ name: 'item', align: 'left', component: 'input', + visible: false, }, { label: t('item.buyRequest.achieved'), @@ -98,6 +98,7 @@ const columns = computed(() => [ name: 'achieved', align: 'left', component: 'input', + visible: false, }, { label: t('item.buyRequest.concept'), @@ -106,6 +107,7 @@ const columns = computed(() => [ align: 'left', sortable: true, component: 'input', + visible: false, }, { label: t('item.buyRequest.state'), @@ -114,10 +116,17 @@ const columns = computed(() => [ align: 'left', }, { + align: 'right', label: '', - name: 'action', - align: 'left', - columnFilter: null, + name: 'tableActions', + actions: [ + { + title: t('Client ticket list'), + icon: 'thumb_down', + action: onDenyAccept, + isPrimary: true, + }, + ], }, ]); From fe78de0c4761772593a3e714c69894f36651e696 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 5 Aug 2024 11:49:06 +0200 Subject: [PATCH 014/150] refs #7283 fixedPrices --- src/pages/Item/ItemFixedPrice.vue | 325 +++++++----------------------- 1 file changed, 70 insertions(+), 255 deletions(-) diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 2ecd1f21b..41f2bac2f 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -1,7 +1,7 @@ <script setup> import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue'; import { useI18n } from 'vue-i18n'; - +import { toDate } from 'src/filters'; import FetchData from 'components/FetchData.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; import VnInput from 'src/components/common/VnInput.vue'; @@ -10,7 +10,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue'; import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue'; import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue'; import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue'; - +import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { dashIfEmpty } from 'src/filters'; import { useVnConfirm } from 'composables/useVnConfirm'; @@ -27,7 +27,7 @@ const { t } = useI18n(); const { openConfirmationModal } = useVnConfirm(); const state = useState(); const { notify } = useNotify(); - +const tableRef = ref(); const editTableCellDialogRef = ref(null); const user = state.getUser(); const fixedPrices = ref([]); @@ -111,86 +111,81 @@ const defaultColumnAttrs = { const columns = computed(() => [ { label: t('item.fixedPrice.itemId'), - name: 'itemId', - field: 'itemFk', - ...defaultColumnAttrs, - columnFilter: { - ...defaultColumnFilter, + name: 'itemFk', + align: 'left', + isId: true, + chip: { + condition: () => true, }, }, { label: t('globals.description'), - field: 'name', name: 'description', - ...defaultColumnAttrs, - columnFilter: { - ...defaultColumnFilter, - }, + align: 'left', + create: true, }, { label: t('item.fixedPrice.groupingPrice'), - field: 'rate2', - name: 'groupingPrice', - ...defaultColumnAttrs, - columnFilter: { - ...defaultColumnFilter, - }, - format: (val) => toCurrency(val), + name: 'rate2', + align: 'left', + create: true, }, { label: t('item.fixedPrice.packingPrice'), - field: 'rate3', - name: 'packingPrice', - ...defaultColumnAttrs, - columnFilter: { - ...defaultColumnFilter, - }, - format: (val) => dashIfEmpty(val), + name: 'rate3', + align: 'left', + create: true, }, { label: t('item.fixedPrice.minPrice'), - field: 'minPrice', name: 'minPrice', - ...defaultColumnAttrs, - columnFilter: { - ...defaultColumnFilter, - }, + align: 'left', + create: true, }, { label: t('item.fixedPrice.started'), - field: 'started', name: 'started', - ...defaultColumnAttrs, - columnFilter: null, + align: 'left', + create: true, + format: (row) => toDate(row.started), }, { label: t('item.fixedPrice.ended'), - field: 'ended', name: 'ended', - ...defaultColumnAttrs, - columnFilter: null, + align: 'left', + create: true, + format: (row) => toDate(row.ended), }, - { label: t('item.fixedPrice.warehouse'), - field: 'warehouseFk', - name: 'warehouse', - ...defaultColumnAttrs, + name: 'warehouseFk', columnFilter: { - component: VnSelect, - type: 'select', - filterValue: null, - event: getColumnInputEvents, + component: 'select', attrs: { - options: warehousesOptions.value, - 'option-value': 'id', - 'option-label': 'name', - dense: true, + url: 'Warehouses', + optionValue: 'id', + optionLabel: 'name', }, }, + component: 'select', + attrs: { + url: 'Warehouses', + fields: ['id', 'name'], + }, + disable: false, + }, + { + align: 'right', + label: '', + name: 'tableActions', + actions: [ + { + title: t('Delete'), + icon: 'delete', + }, + ], }, - { name: 'deleteAction', align: 'center' }, ]); const editTableFieldsOptions = [ @@ -364,212 +359,32 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> - <FetchData - url="Warehouses" - :filter="{ order: ['name'] }" + <VnTable + ref="tableRef" + data-key="ItemFixedPrices" + url="FixedPrices/filter" + :create="{ + urlCreate: 'PriceFixed', + title: 'Create Item', + onDataSaved: () => tableRef.redirect(), + formInitialData: {}, + }" + order="id ASC" + :columns="columns" auto-load - @on-fetch="(data) => onWarehousesFetched(data)" - /> - <RightMenu> - <template #right-panel> - <ItemFixedPriceFilter - data-key="ItemFixedPrices" - :warehouses-options="warehousesOptions" - /> + :is-editable="false" + :use-model="true" + > + <template #column-description="{ row }"> + <div class="row column full-width justify-between items-start"> + {{ row?.description }} + <div v-if="row?.description" class="subName"> + {{ row?.subName.toUpperCase() }} + </div> + </div> + <FetchedTags :item="row" :max-length="6" /> </template> - </RightMenu> - <QPage class="column items-center q-pa-md"> - <QTable - :rows="fixedPrices" - :columns="columns" - row-key="id" - selection="multiple" - v-model:selected="rowsSelected" - :pagination="{ rowsPerPage: 0 }" - class="full-width q-mt-md" - :no-data-label="t('globals.noResults')" - > - <template #top-row="{ cols }"> - <QTr> - <QTd /> - <QTd - v-for="(col, index) in cols" - :key="index" - style="max-width: 100px" - > - <component - :is="col.columnFilter.component" - v-if="col.columnFilter" - v-model="col.columnFilter.filterValue" - v-bind="col.columnFilter.attrs" - v-on="col.columnFilter.event(col)" - dense - /> - </QTd> - </QTr> - </template> - - <template #body-cell-itemId="props"> - <QTd> - <VnSelect - url="Items/withName" - hide-selected - option-label="id" - option-value="id" - v-model="props.row.itemFk" - v-on="getRowUpdateInputEvents(props, true, 'select')" - > - <template #option="scope"> - <QItem v-bind="scope.itemProps"> - <QItemSection> - <QItemLabel> #{{ scope.opt?.id }} </QItemLabel> - <QItemLabel caption>{{ scope.opt?.name }}</QItemLabel> - </QItemSection> - </QItem> - </template> - </VnSelect> - </QTd> - </template> - <template #body-cell-description="{ row }"> - <QTd class="col"> - <span class="link"> - {{ row.name }} - </span> - <ItemDescriptorProxy :id="row.itemFk" /> - <FetchedTags :item="row" :max-length="6" /> - </QTd> - </template> - <template #body-cell-groupingPrice="props"> - <QTd class="col"> - <VnInput - v-model.number="props.row.rate2" - v-on="getRowUpdateInputEvents(props)" - > - <template #append>€</template> - </VnInput> - </QTd> - </template> - <template #body-cell-packingPrice="props"> - <QTd class="col"> - <VnInput - v-model.number="props.row.rate3" - v-on="getRowUpdateInputEvents(props)" - > - <template #append>€</template> - </VnInput> - </QTd> - </template> - <template #body-cell-minPrice="props"> - <QTd class="col"> - <div class="row"> - <QCheckbox - class="col" - :model-value="props.row.hasMinPrice" - @update:model-value="updateMinPrice($event, props)" - :false-value="0" - :true-value="1" - :toggle-indeterminate="false" - /> - <VnInput - class="col" - :disable="!props.row.hasMinPrice" - v-model.number="props.row.minPrice" - v-on="getRowUpdateInputEvents(props)" - type="number" - /> - </div> - </QTd> - </template> - <template #body-cell-started="props"> - <QTd class="col" style="min-width: 160px"> - <VnInputDate - v-model="props.row.started" - v-on="getRowUpdateInputEvents(props, false, 'date')" - v-bind=" - isBigger(props.row.started) - ? { 'bg-color': 'warning', 'is-outlined': true } - : {} - " - /> - </QTd> - </template> - <template #body-cell-ended="props"> - <QTd class="col" style="min-width: 150px"> - <VnInputDate - v-model="props.row.ended" - v-on="getRowUpdateInputEvents(props, false, 'date')" - v-bind=" - isLower(props.row.ended) - ? { 'bg-color': 'warning', 'is-outlined': true } - : {} - " - /> - </QTd> - </template> - <template #body-cell-warehouse="props"> - <QTd class="col"> - <VnSelect - :options="warehousesOptions" - hide-selected - option-label="name" - option-value="id" - v-model="props.row.warehouseFk" - v-on="getRowUpdateInputEvents(props, false, 'select')" - /> - </QTd> - </template> - <template #body-cell-deleteAction="{ row, rowIndex }"> - <QTd class="col"> - <QIcon - name="delete" - size="sm" - class="cursor-pointer fill-icon-on-hover" - color="primary" - @click.stop=" - openConfirmationModal( - t('This row will be removed'), - t('Do you want to clone this item?'), - () => removePrice(row.id, rowIndex) - ) - " - > - <QTooltip class="text-no-wrap"> - {{ t('Delete') }} - </QTooltip> - </QIcon> - </QTd> - </template> - <template #bottom-row> - <QTd align="center"> - <QIcon - @click.stop="addRow()" - class="fill-icon-on-hover" - color="primary" - name="add_circle" - size="sm" - > - <QTooltip> - {{ t('Add fixed price') }} - </QTooltip> - </QIcon> - </QTd> - </template> - </QTable> - <QPageSticky v-if="rowsSelected.length" :offset="[20, 20]"> - <QBtn @click="openEditTableCellDialog()" color="primary" fab icon="edit" /> - <QTooltip> - {{ t('Edit fixed price(s)') }} - </QTooltip> - </QPageSticky> - <QDialog ref="editTableCellDialogRef"> - <EditTableCellValueForm - edit-url="FixedPrices/editFixedPrice" - :rows="rowsSelected" - :fields-options="editTableFieldsOptions" - @on-data-saved="onEditCellDataSaved()" - /> - </QDialog> - </QPage> + </VnTable> </template> <i18n> From 6ca8ede946356c0d3b04cf57671e583f349f7f2b Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 7 Aug 2024 09:53:42 +0200 Subject: [PATCH 015/150] refs #7283 item request --- src/pages/Item/ItemRequest.vue | 28 ++++++++-------------------- src/pages/Item/ItemTypeList.vue | 3 +++ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 0389ba864..8791a575f 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -33,8 +33,7 @@ watch( const columns = computed(() => [ { label: t('item.buyRequest.ticketId'), - name: 'id', - field: 'id', + name: 'ticketFk', align: 'left', isId: true, chip: { @@ -44,7 +43,6 @@ const columns = computed(() => [ }, { label: t('item.buyRequest.shipped'), - field: 'shipped', name: 'shipped', align: 'left', component: 'date', @@ -61,57 +59,46 @@ const columns = computed(() => [ }, { label: t('item.buyRequest.requester'), - field: 'requesterName', - name: 'requesterFk', + name: 'requesterName', align: 'left', }, { label: t('item.buyRequest.requested'), - field: 'quantity', - name: 'requested', + name: 'saleQuantity', align: 'left', }, { label: t('item.buyRequest.price'), - field: 'price', name: 'price', align: 'left', format: (row) => toCurrency(row.price), }, { label: t('item.buyRequest.attender'), - field: 'attender', - name: 'attender', + name: 'attenderName', align: 'left', }, { label: t('item.buyRequest.item'), - field: 'item', name: 'item', align: 'left', component: 'input', - visible: false, }, { label: t('item.buyRequest.achieved'), - field: 'achieved', name: 'achieved', align: 'left', component: 'input', - visible: false, }, { label: t('item.buyRequest.concept'), - field: 'concept', name: 'concept', align: 'left', sortable: true, component: 'input', - visible: false, }, { label: t('item.buyRequest.state'), - field: 'state', name: 'state', align: 'left', }, @@ -123,7 +110,7 @@ const columns = computed(() => [ { title: t('Client ticket list'), icon: 'thumb_down', - action: onDenyAccept, + action: showDenyRequestForm, isPrimary: true, }, ], @@ -348,14 +335,15 @@ onBeforeMount(() => { <VnTable ref="tableRef" data-key="itemRequest" - url="ticketRequests" + url="ticketRequests/filter" order="id DESC" :columns="columns" + :is-editable="false" auto-load > <template #column-attender="{ row }"> <span class="link" @click.stop> - {{ row.attenderFk }} + {{ row }} <WorkerDescriptorProxy :id="row.attenderFk" /> </span> </template> diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index c02f9bb43..5ebbec62b 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -84,6 +84,7 @@ const columns = computed(() => [ fields: ['id', 'firstName'], }, cardVisible: true, + visible: false, }, { align: 'left', @@ -96,6 +97,7 @@ const columns = computed(() => [ fields: ['id', 'name'], }, cardVisible: true, + visible: false, }, { align: 'left', @@ -108,6 +110,7 @@ const columns = computed(() => [ fields: ['id', 'name'], }, cardVisible: true, + visible: false, }, ]); </script> From 589ee629cfbfc3c2b79c32ecef9ff47f30d33c00 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 13 Aug 2024 12:45:30 +0200 Subject: [PATCH 016/150] refs #7283 fix conflicts --- src/pages/Item/ItemFixedPrice.vue | 255 +++++++++--------------------- src/pages/Item/ItemRequest.vue | 15 +- 2 files changed, 89 insertions(+), 181 deletions(-) diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 41f2bac2f..56ad7a17d 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -2,25 +2,15 @@ import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import { toDate } from 'src/filters'; -import FetchData from 'components/FetchData.vue'; import FetchedTags from 'components/ui/FetchedTags.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 EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue'; -import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue'; -import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; -import { dashIfEmpty } from 'src/filters'; import { useVnConfirm } from 'composables/useVnConfirm'; import { useState } from 'src/composables/useState'; -import { toCurrency } from 'filters/index'; import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; import { useArrayData } from 'composables/useArrayData'; -import { isLower, isBigger } from 'src/filters/date.js'; -import RightMenu from 'src/components/common/RightMenu.vue'; const stateStore = useStateStore(); const { t } = useI18n(); @@ -75,39 +65,6 @@ watch( (data) => onFixedPricesFetched(data) ); -const applyColumnFilter = async (col) => { - try { - const paramKey = col.columnFilter?.filterParamKey || col.field; - params[paramKey] = col.columnFilter.filterValue; - await arrayData.addFilter({ params }); - } catch (err) { - console.error('Error applying column filter', err); - } -}; - -const getColumnInputEvents = (col) => { - return col.columnFilter.type === 'select' - ? { 'update:modelValue': () => applyColumnFilter(col) } - : { - 'keyup.enter': () => applyColumnFilter(col), - }; -}; - -const defaultColumnFilter = { - component: VnInput, - type: 'text', - filterValue: null, - event: getColumnInputEvents, - attrs: { - dense: true, - }, -}; - -const defaultColumnAttrs = { - align: 'left', - sortable: true, -}; - const columns = computed(() => [ { label: t('item.fixedPrice.itemId'), @@ -127,12 +84,14 @@ const columns = computed(() => [ { label: t('item.fixedPrice.groupingPrice'), name: 'rate2', + component: 'input', align: 'left', create: true, }, { label: t('item.fixedPrice.packingPrice'), name: 'rate3', + component: 'input', align: 'left', create: true, }, @@ -140,6 +99,7 @@ const columns = computed(() => [ { label: t('item.fixedPrice.minPrice'), name: 'minPrice', + component: 'input', align: 'left', create: true, }, @@ -188,70 +148,14 @@ const columns = computed(() => [ }, ]); -const editTableFieldsOptions = [ - { - field: 'rate2', - label: t('item.fixedPrice.groupingPrice'), - component: 'input', - attrs: { - type: 'number', - }, - }, - { - field: 'rate3', - label: t('item.fixedPrice.packingPrice'), - component: 'input', - attrs: { - type: 'number', - }, - }, - { - field: 'minPrice', - label: t('item.fixedPrice.minPrice'), - component: 'input', - attrs: { - type: 'number', - }, - }, - { - field: 'hasMinPrice', - label: t('item.fixedPrice.hasMinPrice'), - component: 'checkbox', - attrs: { - 'false-value': 0, - 'true-value': 1, - }, - }, - { - field: 'started', - label: t('item.fixedPrice.started'), - component: 'date', - }, - { - field: 'ended', - label: t('item.fixedPrice.ended'), - component: 'date', - }, - { - field: 'warehouseFk', - label: t('item.fixedPrice.warehouse'), - component: 'select', - attrs: { - options: [], - 'option-label': 'name', - 'option-value': 'id', - }, - }, -]; - -const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => { - return inputType === 'text' - ? { - 'keyup.enter': () => upsertPrice(props, resetMinPrice), - blur: () => upsertPrice(props, resetMinPrice), - } - : { 'update:modelValue': () => upsertPrice(props, resetMinPrice) }; -}; +// const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => { +// return inputType === 'text' +// ? { +// 'keyup.enter': () => upsertPrice(props, resetMinPrice), +// blur: () => upsertPrice(props, resetMinPrice), +// } +// : { 'update:modelValue': () => upsertPrice(props, resetMinPrice) }; +// }; const validations = (row, rowIndex, col) => { const isNew = !row.id; @@ -281,81 +185,73 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => { } }; -const addRow = () => { - if (!fixedPrices.value || fixedPrices.value.length === 0) { - fixedPrices.value = []; +// const addRow = () => { +// if (!fixedPrices.value || fixedPrices.value.length === 0) { +// fixedPrices.value = []; - const today = Date.vnNew(); - const millisecsInDay = 86400000; - const daysInWeek = 7; - const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay); +// const today = Date.vnNew(); +// const millisecsInDay = 86400000; +// const daysInWeek = 7; +// const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay); - const newPrice = { - started: today, - ended: nextWeek, - hasMinPrice: 0, - }; +// const newPrice = { +// started: today, +// ended: nextWeek, +// hasMinPrice: 0, +// }; - fixedPricesOriginalData.value.push({ ...newPrice }); - fixedPrices.value.push({ ...newPrice }); - return; - } +// fixedPricesOriginalData.value.push({ ...newPrice }); +// fixedPrices.value.push({ ...newPrice }); +// return; +// } - const lastItemCopy = JSON.parse( - JSON.stringify(fixedPrices.value[fixedPrices.value.length - 1]) - ); - delete lastItemCopy.id; - fixedPricesOriginalData.value.push(lastItemCopy); - fixedPrices.value.push(lastItemCopy); -}; +// const lastItemCopy = JSON.parse( +// JSON.stringify(fixedPrices.value[fixedPrices.value.length - 1]) +// ); +// delete lastItemCopy.id; +// fixedPricesOriginalData.value.push(lastItemCopy); +// fixedPrices.value.push(lastItemCopy); +// }; -const openEditTableCellDialog = () => { - editTableCellDialogRef.value.show(); -}; +// const openEditTableCellDialog = () => { +// editTableCellDialogRef.value.show(); +// }; -const onEditCellDataSaved = async () => { - rowsSelected.value = []; - await fetchFixedPrices(); -}; +// const onEditCellDataSaved = async () => { +// rowsSelected.value = []; +// await fetchFixedPrices(); +// }; -const onWarehousesFetched = (data) => { - warehousesOptions.value = data; - // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'. - const warehouseField = editTableFieldsOptions.find( - (field) => field.field === 'warehouseFk' - ); - warehouseField.attrs.options = data; -}; +// const onWarehousesFetched = (data) => { +// warehousesOptions.value = data; +// // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'. +// const warehouseField = editTableFieldsOptions.find( +// (field) => field.field === 'warehouseFk' +// ); +// warehouseField.attrs.options = data; +// }; -const removePrice = async (id, rowIndex) => { - try { - await axios.delete(`FixedPrices/${id}`); - fixedPrices.value.splice(rowIndex, 1); - fixedPricesOriginalData.value.splice(rowIndex, 1); - notify(t('globals.dataSaved'), 'positive'); - } catch (err) { - console.error('Error removing price', err); - } -}; +// const removePrice = async (id, rowIndex) => { +// try { +// await axios.delete(`FixedPrices/${id}`); +// fixedPrices.value.splice(rowIndex, 1); +// fixedPricesOriginalData.value.splice(rowIndex, 1); +// notify(t('globals.dataSaved'), 'positive'); +// } catch (err) { +// console.error('Error removing price', err); +// } +// }; -const updateMinPrice = async (value, props) => { - // El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice - // Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado - props.row.hasMinPrice = value; - await upsertPrice({ - row: props.row, - col: { field: 'hasMinPrice' }, - rowIndex: props.rowIndex, - }); -}; - -onMounted(async () => { - stateStore.rightDrawer = true; - params.warehouseFk = user.value.warehouseFk; - await fetchFixedPrices(); -}); - -onUnmounted(() => (stateStore.rightDrawer = false)); +// const updateMinPrice = async (value, props) => { +// // El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice +// // Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado +// props.row.hasMinPrice = value; +// await upsertPrice({ +// row: props.row, +// col: { field: 'hasMinPrice' }, +// rowIndex: props.rowIndex, +// }); +// }; </script> <template> @@ -372,13 +268,13 @@ onUnmounted(() => (stateStore.rightDrawer = false)); order="id ASC" :columns="columns" auto-load - :is-editable="false" + :is-editable="true" :use-model="true" > <template #column-description="{ row }"> <div class="row column full-width justify-between items-start"> - {{ row?.description }} - <div v-if="row?.description" class="subName"> + {{ row?.name }} + <div v-if="row?.subName" class="subName"> {{ row?.subName.toUpperCase() }} </div> </div> @@ -386,7 +282,12 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </template> </VnTable> </template> - +<style lang="scss" scoped> +.subName { + text-transform: uppercase; + color: var(--vn-label-color); +} +</style> <i18n> es: Add fixed price: Añadir precio fijado diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 8791a575f..f67f7eda2 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -100,6 +100,7 @@ const columns = computed(() => [ { label: t('item.buyRequest.state'), name: 'state', + action: (row) => getState(row.isOk), align: 'left', }, { @@ -146,7 +147,6 @@ const confirmRequest = async (request) => { `TicketRequests/${request.id}/confirm`, params ); - request.itemDescription = data.concept; request.isOk = true; notify(t('globals.dataSaved'), 'positive'); @@ -338,15 +338,22 @@ onBeforeMount(() => { url="ticketRequests/filter" order="id DESC" :columns="columns" - :is-editable="false" + :is-editable="true" auto-load > - <template #column-attender="{ row }"> + <template #column-attenderName="{ row }"> <span class="link" @click.stop> - {{ row }} + {{ row.attenderName }} <WorkerDescriptorProxy :id="row.attenderFk" /> </span> </template> + + <template #column-requesterName="{ row }"> + <span class="link" @click.stop> + {{ row.requesterName }} + <WorkerDescriptorProxy :id="row.requesterFk" /> + </span> + </template> </VnTable> </template> From 1df089cef78e6728c764d7d72cc015b573f44f66 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 14 Aug 2024 15:50:13 +0200 Subject: [PATCH 017/150] refs #7283 fix itemFixedPrice --- src/pages/Item/ItemFixedPrice.vue | 65 ++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 56ad7a17d..30eb8544e 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -1,9 +1,10 @@ <script setup> -import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue'; +import { ref, reactive, computed, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import { toDate } from 'src/filters'; +import { useQuasar } from 'quasar'; import FetchedTags from 'components/ui/FetchedTags.vue'; -import VnInput from 'src/components/common/VnInput.vue'; +import { useRoute } from 'vue-router'; import VnTable from 'components/VnTable/VnTable.vue'; import { useStateStore } from 'stores/useStateStore'; import { useVnConfirm } from 'composables/useVnConfirm'; @@ -11,20 +12,30 @@ import { useState } from 'src/composables/useState'; import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; import { useArrayData } from 'composables/useArrayData'; +import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; +import VnConfirm from 'components/ui/VnConfirm.vue'; -const stateStore = useStateStore(); +const route = useRoute(); const { t } = useI18n(); const { openConfirmationModal } = useVnConfirm(); const state = useState(); const { notify } = useNotify(); const tableRef = ref(); -const editTableCellDialogRef = ref(null); +const quasar = useQuasar(); const user = state.getUser(); const fixedPrices = ref([]); const fixedPricesOriginalData = ref([]); const warehousesOptions = ref([]); const rowsSelected = ref([]); +// function localUserData() { +// state.setUser(user.value); +// } + +// const userLocal = localUserData(); +// console.log('localUserData', userLocal); + +console.log('user', user.value.warehouseFk); const exprBuilder = (param, value) => { switch (param) { case 'name': @@ -80,6 +91,10 @@ const columns = computed(() => [ name: 'description', align: 'left', create: true, + columnCreate: { + component: 'select', + url: 'Items', + }, }, { label: t('item.fixedPrice.groupingPrice'), @@ -109,6 +124,9 @@ const columns = computed(() => [ align: 'left', create: true, format: (row) => toDate(row.started), + columnCreate: { + component: 'date', + }, }, { label: t('item.fixedPrice.ended'), @@ -116,6 +134,9 @@ const columns = computed(() => [ align: 'left', create: true, format: (row) => toDate(row.ended), + columnCreate: { + component: 'date', + }, }, { label: t('item.fixedPrice.warehouse'), @@ -148,6 +169,27 @@ const columns = computed(() => [ }, ]); +function confirmRemove(row) { + quasar.dialog({ + component: VnConfirm, + componentProps: { + title: t('confirmDeletion'), + message: t('confirmDeletionMessage'), + promise: () => remove(row), + }, + }); +} + +async function remove(item) { + await axios.post('FixedPrices/filter', { + rows: [item.id], + }); + quasar.notify({ + message: t('globals.dataDeleted'), + type: 'positive', + }); + tableRef.value.reload(); +} // const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => { // return inputType === 'text' // ? { @@ -213,15 +255,6 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => { // fixedPrices.value.push(lastItemCopy); // }; -// const openEditTableCellDialog = () => { -// editTableCellDialogRef.value.show(); -// }; - -// const onEditCellDataSaved = async () => { -// rowsSelected.value = []; -// await fetchFixedPrices(); -// }; - // const onWarehousesFetched = (data) => { // warehousesOptions.value = data; // // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'. @@ -259,6 +292,7 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => { ref="tableRef" data-key="ItemFixedPrices" url="FixedPrices/filter" + :filter="{ where: { warehouseFk: user.warehouseFk } }" :create="{ urlCreate: 'PriceFixed', title: 'Create Item', @@ -273,7 +307,10 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => { > <template #column-description="{ row }"> <div class="row column full-width justify-between items-start"> - {{ row?.name }} + <span class="link" @click.stop> + {{ row?.name }} + <ItemDescriptorProxy class="link" :id="row.itemFk" /> + </span> <div v-if="row?.subName" class="subName"> {{ row?.subName.toUpperCase() }} </div> From 12a5d6cec035b72deaa3c9f316d0f329d5411081 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 19 Aug 2024 17:05:00 +0200 Subject: [PATCH 018/150] fix: refs #7283 css --- src/components/VnTable/VnTable.vue | 7 ++++--- src/pages/Item/ItemRequest.vue | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 6c77d44df..69c4739bf 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -486,11 +486,12 @@ defineExpose({ :icon="btn.icon" class="q-px-sm" flat - :class=" + :class="[ btn.isPrimary ? 'text-primary-light' - : 'color-vn-text ' - " + : 'color-vn-text ', + btn.class, + ]" :style="`visibility: ${ (btn.show && btn.show(row)) ?? true ? 'visible' diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index f67f7eda2..65c0aaae1 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -111,6 +111,7 @@ const columns = computed(() => [ { title: t('Client ticket list'), icon: 'thumb_down', + class: 'fill-icon', action: showDenyRequestForm, isPrimary: true, }, From bae0b4de351f43a65de5b6ef05fbdaa9cd525d7c Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 22 Aug 2024 10:38:15 +0200 Subject: [PATCH 019/150] fix: itemBotanical --- src/pages/Item/Card/ItemBotanical.vue | 35 +++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue index 416c7f78b..88d251c47 100644 --- a/src/pages/Item/Card/ItemBotanical.vue +++ b/src/pages/Item/Card/ItemBotanical.vue @@ -13,7 +13,6 @@ import CreateSpecieForm from './CreateSpecieForm.vue'; const route = useRoute(); const { t } = useI18n(); -const itemBotanicalsRef = ref(null); const itemGenusOptions = ref([]); const itemSpeciesOptions = ref([]); const itemBotanicals = ref([]); @@ -31,22 +30,19 @@ const onSpecieCreated = (response, formData) => { const entityId = computed(() => { return route.params.id; }); -onMounted(async () => { - itemBotanicalsForm.itemFk = entityId.value; - itemBotanicals.value = await itemBotanicalsRef.value.fetch(); - if (itemBotanicals.value.length > 0) - Object.assign(itemBotanicalsForm, itemBotanicals.value[0]); -}); +// onMounted(async () => { +// itemBotanicalsForm.itemFk = entityId.value; +// // itemBotanicals.value = await itemBotanicalsRef.value.fetch(); +// if (itemBotanicals.value.length > 0) +// Object.assign(itemBotanicalsForm, itemBotanicals.value[0]); +// }); +async function handleItemBotanical(data) { + itemBotanicalsForm = data; + // if (data.length > 0) Object.assign(itemBotanicalsForm, itemBotanicals.value[0]); +} </script> <template> - <FetchData - ref="itemBotanicalsRef" - url="ItemBotanicals" - :filter="{ - where: { itemFk: entityId }, - }" - @on-fetch="(data) => (itemBotanicals = data)" - /> + <!-- <FetchData ref="itemBotanicalsRef" @on-fetch="(data) => (itemBotanicals = data)" /> --> <FetchData url="Genera" :filter="{ fields: ['id', 'name'], order: 'name ASC' }" @@ -60,11 +56,14 @@ onMounted(async () => { auto-load /> <FormModel + url="ItemBotanicals" url-update="ItemBotanicals" - model="entry" + model="item" auto-load - :form-initial-data="itemBotanicalsForm" - :clear-store-on-unmount="false" + :filter="{ + where: { itemFk: entityId }, + }" + @on-fetch="handleItemBotanical" > <template #form="{ data }"> <VnRow> From 2ef56f9f9730c0cbc8c287531ad12363a9f4c447 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 27 Aug 2024 08:00:07 +0200 Subject: [PATCH 020/150] refs #7283 fixedPrice --- src/pages/Item/ItemFixedPrice.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 30eb8544e..05a8545c8 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -292,6 +292,7 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => { ref="tableRef" data-key="ItemFixedPrices" url="FixedPrices/filter" + save-url="FixedPrices/crud" :filter="{ where: { warehouseFk: user.warehouseFk } }" :create="{ urlCreate: 'PriceFixed', From 6de59c64194c4aec32294575a531dda73d5d8b0d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 27 Aug 2024 14:39:21 +0200 Subject: [PATCH 021/150] refs #7283 itemRequestFixed --- src/pages/Item/ItemFixedPrice.vue | 1 + src/pages/Item/ItemList.vue | 2 ++ src/pages/Item/ItemRequest.vue | 31 +++++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 05a8545c8..7ba4984e7 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -164,6 +164,7 @@ const columns = computed(() => [ { title: t('Delete'), icon: 'delete', + isPrimary: true, }, ], }, diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index a3b70372f..2f3d20390 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -226,11 +226,13 @@ const columns = computed(() => [ title: t('Clone item'), icon: 'vn:clone', action: cloneItem, + isPrimary: true, }, { title: t('view Summary'), icon: 'preview', action: (row) => viewSummary(row.id, ItemSummary), + isPrimary: true, }, ], }, diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 65c0aaae1..0d641f8e2 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -9,7 +9,8 @@ import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; import { toDate } from 'src/filters'; import VnTable from 'components/VnTable/VnTable.vue'; - +import VnInput from 'src/components/common/VnInput.vue'; +import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; const { t } = useI18n(); const { notify } = useNotify(); const stateStore = useStateStore(); @@ -64,7 +65,7 @@ const columns = computed(() => [ }, { label: t('item.buyRequest.requested'), - name: 'saleQuantity', + name: 'quantity', align: 'left', }, { @@ -100,7 +101,7 @@ const columns = computed(() => [ { label: t('item.buyRequest.state'), name: 'state', - action: (row) => getState(row.isOk), + format: (row) => getState(row.isOk), align: 'left', }, { @@ -333,11 +334,12 @@ onBeforeMount(() => { /> </QDialog> </QPage> --> + <VnSubToolbar /> <VnTable ref="tableRef" data-key="itemRequest" url="ticketRequests/filter" - order="id DESC" + order="shippedDate ASC, isOk ASC" :columns="columns" :is-editable="true" auto-load @@ -355,6 +357,27 @@ onBeforeMount(() => { <WorkerDescriptorProxy :id="row.requesterFk" /> </span> </template> + + <template #column-item="{ row }"> + <span @click.stop disabled="row.isOk != null"> + <VnInput type="number" v-model="row.item" fill-input /> + </span> + </template> + <template #column-achieved="{ row }"> + <span @click.stop disabled="!request.itemFk || request.isOk != null"> + <VnInput + type="number" + v-model="row.achieved" + fill-input + @keyup.enter="changeQuantity(row)" + /> + </span> + </template> + <template #column-concept="{ row }"> + <span @click.stop disabled="row.isOk != null"> + {{ row.itemDescription }} + </span> + </template> </VnTable> </template> From cd1b5f56f711ad8a0b0c3141c085f03897f36f8c Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 28 Aug 2024 11:35:57 +0200 Subject: [PATCH 022/150] refs #7283 column-action --- src/pages/Item/ItemRequest.vue | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 0d641f8e2..41248afbb 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -110,7 +110,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Client ticket list'), + title: t('Deny Request'), icon: 'thumb_down', class: 'fill-icon', action: showDenyRequestForm, @@ -378,6 +378,32 @@ onBeforeMount(() => { {{ row.itemDescription }} </span> </template> + <template #column-action="{ row, rowIndex }"> + <QTd> + <QIcon + v-if="row.response?.length" + name="insert_drive_file" + color="primary" + size="sm" + > + <QTooltip> + {{ row.response }} + </QTooltip> + </QIcon> + <QIcon + v-if="row.isOk == null" + name="thumb_down" + color="primary" + size="sm" + class="fill-icon" + @click="showDenyRequestForm(row.id, rowIndex)" + > + <QTooltip> + {{ t('Discard') }} + </QTooltip> + </QIcon> + </QTd> + </template> </VnTable> </template> From e4aec1773b74c002913bf60f807c07ca39a873f5 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 29 Aug 2024 12:38:57 +0200 Subject: [PATCH 023/150] refs #7283 itemRequest fix deny --- src/pages/Item/ItemRequest.vue | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 41248afbb..0e59a16c4 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -7,6 +7,7 @@ import { useArrayData } from 'composables/useArrayData'; import { toCurrency } from 'filters/index'; import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; +import ItemRequestDenyForm from './ItemRequestDenyForm.vue'; import { toDate } from 'src/filters'; import VnTable from 'components/VnTable/VnTable.vue'; import VnInput from 'src/components/common/VnInput.vue'; @@ -26,6 +27,10 @@ const arrayData = useArrayData('ItemRequests', { }); const store = arrayData.store; +const userParams = { + state: 'pending', +}; + watch( () => store.data, (value) => (itemRequestsOptions.value = value) @@ -174,7 +179,9 @@ const onDenyAccept = (_, responseData) => { itemRequestsOptions.value[denyRequestIndex.value].isOk = responseData.isOk; itemRequestsOptions.value[denyRequestIndex.value].attenderFk = responseData.attenderFk; + console.log('itemRequestsOptions: ', itemRequestsOptions.value); itemRequestsOptions.value[denyRequestIndex.value].response = responseData.response; + console.log('itemRequestsOptions.value', itemRequestsOptions.value); denyRequestId.value = null; denyRequestIndex.value = null; }; @@ -341,8 +348,10 @@ onBeforeMount(() => { url="ticketRequests/filter" order="shippedDate ASC, isOk ASC" :columns="columns" + :user-params="userParams" :is-editable="true" auto-load + :disable-option="{ card: true }" > <template #column-attenderName="{ row }"> <span class="link" @click.stop> @@ -378,7 +387,7 @@ onBeforeMount(() => { {{ row.itemDescription }} </span> </template> - <template #column-action="{ row, rowIndex }"> + <template #column-tableActions="{ row, rowIndex }"> <QTd> <QIcon v-if="row.response?.length" @@ -405,6 +414,9 @@ onBeforeMount(() => { </QTd> </template> </VnTable> + <QDialog ref="denyFormRef" transition-show="scale" transition-hide="scale"> + <ItemRequestDenyForm :request-id="denyRequestId" @on-data-saved="onDenyAccept" /> + </QDialog> </template> <i18n> From 388036a2eb589b6475132e630ee555147938b4a6 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 2 Sep 2024 09:19:49 +0200 Subject: [PATCH 024/150] refs #7283 itemRequest fix --- src/pages/Item/ItemRequest.vue | 146 ++------------------------------- 1 file changed, 7 insertions(+), 139 deletions(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 0e59a16c4..10ed237b4 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -208,140 +208,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" - :label="t('globals.search')" - :info="t('You can search by Id or alias')" - :redirect="false" - /> - <RightMenu> - <template #right-panel> - <ItemRequestFilter data-key="ItemRequests" /> - </template> - </RightMenu> - <QPage class="column items-center q-pa-md"> - <QTable - :rows="itemRequestsOptions" - :columns="columns" - row-key="id" - :pagination="{ rowsPerPage: 0 }" - class="full-width q-mt-md" - :no-data-label="t('globals.noResults')" - > - <template #body-cell-id="{ row }"> - <QTd> - <QBtn flat color="primary"> {{ row.ticketFk }}</QBtn> - <TicketDescriptorProxy :id="row.ticketFk" /> - </QTd> - </template> - <template #body-cell-shipped="{ row }"> - <QTd> - <QBadge - v-if="getDateQBadgeColor(row.shipped)" - :color="getDateQBadgeColor(row.shipped)" - text-color="black" - class="q-ma-none" - dense - style="font-size: 14px" - > - {{ toDateFormat(row.shipped) }} - </QBadge> - <span v-else>{{ toDateFormat(row.shipped) }}</span> - </QTd> - </template> - <template #body-cell-requester="{ row }"> - <QTd> - <QBtn flat dense color="primary"> {{ row.requesterName }}</QBtn> - <WorkerDescriptorProxy :id="row.requesterFk" /> - </QTd> - </template> - <template #body-cell-attender="{ row }"> - <QTd> - <VnSelect - v-model="row.attenderFk" - :options="workersOptions" - hide-selected - option-label="firstName" - option-value="id" - dense - /> - </QTd> - </template> - <template #body-cell-item="{ row }"> - <QTd> - <VnInput - v-model.number="row.itemFk" - type="number" - :disable="row.isOk != null" - dense - /> - </QTd> - </template> - <template #body-cell-achieved="{ row }"> - <QTd> - <VnInput - v-model.number="row.saleQuantity" - @blur="changeQuantity(row)" - type="number" - :disable="!row.itemFk || row.isOk != null" - dense - /> - </QTd> - </template> - <template #body-cell-concept="{ row }"> - <QTd> - <QBtn flat dense color="primary"> {{ row.itemDescription }}</QBtn> - <ItemDescriptorProxy :id="row.itemFk" /> - </QTd> - </template> - <template #body-cell-state="{ row }"> - <QTd> - <span>{{ getState(row.isOk) }}</span> - </QTd> - </template> - <template #body-cell-action="{ row, rowIndex }"> - <QTd> - <QIcon - v-if="row.response?.length" - name="insert_drive_file" - color="primary" - size="sm" - > - <QTooltip> - {{ row.response }} - </QTooltip> - </QIcon> - <QIcon - v-if="row.isOk == null" - name="thumb_down" - color="primary" - size="sm" - class="fill-icon" - @click="showDenyRequestForm(row.id, rowIndex)" - > - <QTooltip> - {{ t('Discard') }} - </QTooltip> - </QIcon> - </QTd> - </template> - </QTable> - <QDialog ref="denyFormRef" transition-show="scale" transition-hide="scale"> - <ItemRequestDenyForm - :request-id="denyRequestId" - @on-data-saved="onDenyAccept" - /> - </QDialog> - </QPage> --> - <VnSubToolbar /> <VnTable ref="tableRef" data-key="itemRequest" @@ -368,17 +234,19 @@ onBeforeMount(() => { </template> <template #column-item="{ row }"> - <span @click.stop disabled="row.isOk != null"> - <VnInput type="number" v-model="row.item" fill-input /> + <span> + <VnInput v-model.number="row.itemFk" dense /> </span> </template> <template #column-achieved="{ row }"> - <span @click.stop disabled="!request.itemFk || request.isOk != null"> + <span> <VnInput type="number" - v-model="row.achieved" - fill-input + v-model.number="row.saleQuantity" + :disable="!row.itemFk || row.isOk != null" + @blur="changeQuantity(row)" @keyup.enter="changeQuantity(row)" + dense /> </span> </template> From 3bf3e8eeaa4c93dd2c8a737d5a95fb0528abbe50 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 2 Sep 2024 11:08:50 +0200 Subject: [PATCH 025/150] refs #7283 itemRequest fix deny --- src/pages/Item/ItemRequest.vue | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 10ed237b4..8273c5c91 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -112,16 +112,7 @@ const columns = computed(() => [ { align: 'right', label: '', - name: 'tableActions', - actions: [ - { - title: t('Deny Request'), - icon: 'thumb_down', - class: 'fill-icon', - action: showDenyRequestForm, - isPrimary: true, - }, - ], + name: 'denyOptions', }, ]); @@ -171,7 +162,9 @@ const getState = (isOk) => { const showDenyRequestForm = (requestId, rowIndex) => { denyRequestId.value = requestId; + console.log('denyRequestId.value: ', denyRequestId.value); denyRequestIndex.value = rowIndex; + console.log('denyRequestIndex.value: ', denyRequestIndex.value); denyFormRef.value.show(); }; @@ -255,8 +248,8 @@ onBeforeMount(() => { {{ row.itemDescription }} </span> </template> - <template #column-tableActions="{ row, rowIndex }"> - <QTd> + <template #column-denyOptions="{ row, rowIndex }"> + <QTd class="sticky no-padding"> <QIcon v-if="row.response?.length" name="insert_drive_file" From 488dc74c1f07c9314dc14618765c7250191ae72d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 2 Sep 2024 12:14:23 +0200 Subject: [PATCH 026/150] refs #7283 itemRequest fix reload --- src/pages/Item/ItemRequest.vue | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 8273c5c91..4062f8538 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -1,5 +1,5 @@ <script setup> -import { ref, computed, onMounted, onBeforeMount, watch } from 'vue'; +import { ref, computed, onMounted, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import { useStateStore } from 'stores/useStateStore'; @@ -11,7 +11,6 @@ import ItemRequestDenyForm from './ItemRequestDenyForm.vue'; import { toDate } from 'src/filters'; import VnTable from 'components/VnTable/VnTable.vue'; import VnInput from 'src/components/common/VnInput.vue'; -import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; const { t } = useI18n(); const { notify } = useNotify(); const stateStore = useStateStore(); @@ -31,6 +30,7 @@ const userParams = { state: 'pending', }; +const tableRef = ref(); watch( () => store.data, (value) => (itemRequestsOptions.value = value) @@ -162,9 +162,7 @@ const getState = (isOk) => { const showDenyRequestForm = (requestId, rowIndex) => { denyRequestId.value = requestId; - console.log('denyRequestId.value: ', denyRequestId.value); denyRequestIndex.value = rowIndex; - console.log('denyRequestIndex.value: ', denyRequestIndex.value); denyFormRef.value.show(); }; @@ -172,32 +170,16 @@ const onDenyAccept = (_, responseData) => { itemRequestsOptions.value[denyRequestIndex.value].isOk = responseData.isOk; itemRequestsOptions.value[denyRequestIndex.value].attenderFk = responseData.attenderFk; - console.log('itemRequestsOptions: ', itemRequestsOptions.value); itemRequestsOptions.value[denyRequestIndex.value].response = responseData.response; - console.log('itemRequestsOptions.value', itemRequestsOptions.value); denyRequestId.value = null; denyRequestIndex.value = null; + tableRef.value.reload(); }; onMounted(async () => { await arrayData.fetch({ append: false }); stateStore.rightDrawer = true; }); - -onBeforeMount(() => { - const today = Date.vnNew(); - today.setHours(0, 0, 0, 0); - - const nextWeek = Date.vnNew(); - nextWeek.setHours(23, 59, 59, 59); - nextWeek.setDate(nextWeek.getDate() + 7); - - filterParams.value = { - from: today, - to: nextWeek, - state: 'pending', - }; -}); </script> <template> From 04c8481b51e70f3062d7d0a997bdab2fece7c98a Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 12 Sep 2024 07:43:12 +0200 Subject: [PATCH 027/150] refs #7283 fix itemFixed --- src/pages/Item/ItemFixedPrice.vue | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 29b886e25..d91b5189e 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -1,6 +1,5 @@ <script setup> import { onMounted, ref, reactive, onUnmounted, nextTick, computed } from 'vue'; -import { ref, reactive, computed, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; @@ -27,15 +26,12 @@ import { QCheckbox } from 'quasar'; const quasar = useQuasar(); const stateStore = useStateStore(); -const route = useRoute(); const { t } = useI18n(); const { openConfirmationModal } = useVnConfirm(); const state = useState(); const { notify } = useNotify(); const tableRef = ref(); const editTableCellDialogRef = ref(null); -const tableRef = ref(); -const quasar = useQuasar(); const user = state.getUser(); const fixedPrices = ref([]); const warehousesOptions = ref([]); @@ -129,6 +125,7 @@ const columns = computed(() => [ }, { label: t('item.fixedPrice.started'), + field: 'started', name: 'started', format: ({ started }) => toDate(started), cardVisible: true, @@ -144,6 +141,7 @@ const columns = computed(() => [ }, { label: t('item.fixedPrice.ended'), + field: 'ended', name: 'ended', ...defaultColumnAttrs, cardVisible: true, @@ -157,6 +155,7 @@ const columns = computed(() => [ columnClass: 'expand', format: (row) => toDate(row.ended), }, + { label: t('item.fixedPrice.warehouse'), field: 'warehouseFk', @@ -223,18 +222,17 @@ const editTableFieldsOptions = [ label: t('item.fixedPrice.ended'), component: 'date', }, -]; - -function confirmRemove(row) { - quasar.dialog({ - component: VnConfirm, - componentProps: { - title: t('confirmDeletion'), - message: t('confirmDeletionMessage'), - promise: () => remove(row), + { + field: 'warehouseFk', + label: t('item.fixedPrice.warehouse'), + component: 'select', + attrs: { + options: [], + 'option-label': 'name', + 'option-value': 'id', }, - }), -}; + }, +]; const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => { return inputType === 'text' ? { From 039a8d1d02e1ca30a2dd6701a60b7211b3de01cd Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 12 Sep 2024 09:32:41 +0200 Subject: [PATCH 028/150] refs #7283 fix items --- src/i18n/locale/en.yml | 2 ++ src/i18n/locale/es.yml | 2 ++ src/pages/Item/Card/ItemCard.vue | 2 +- src/pages/Item/ItemList.vue | 49 ++++++++++++-------------------- src/pages/Item/ItemRequest.vue | 2 +- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 6546b78c4..37d1e3726 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -1022,6 +1022,8 @@ travel: travelFileDescription: 'Travel id { travelId }' file: File item: + searchbar: + label: Search item descriptor: item: Item buyer: Buyer diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 79e66b5f7..0c0447b78 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -1007,6 +1007,8 @@ travel: travelFileDescription: 'Id envío { travelId }' file: Fichero item: + searchbar: + label: Buscar artículo descriptor: item: Artículo buyer: Comprador diff --git a/src/pages/Item/Card/ItemCard.vue b/src/pages/Item/Card/ItemCard.vue index 1162327c1..4cf1372cc 100644 --- a/src/pages/Item/Card/ItemCard.vue +++ b/src/pages/Item/Card/ItemCard.vue @@ -12,7 +12,7 @@ import ItemListFilter from '../ItemListFilter.vue'; search-data-key="ItemList" :searchbar-props="{ url: 'Items/filter', - label: 'searchbar.labelr', + label: 'item.searchbar.label', info: 'searchbar.info', }" /> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 2f3d20390..c31302bba 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -44,25 +44,24 @@ const itemFilter = { ], }; const columns = computed(() => [ - // { - // label: '', - // name: 'image', - // align: 'left', - // columnField: { - // component: VnImg, - // attrs: (id) => { - // return { - // id, - // width: '50px', - // }; - // }, - // }, - // columnFilter: false, - // }, + { + label: '', + name: 'image', + align: 'left', + columnField: { + component: VnImg, + attrs: (id) => { + return { + id, + width: '50px', + }; + }, + }, + columnFilter: false, + }, { label: t('item.list.id'), name: 'id', - field: 'id', align: 'left', isId: true, chip: { @@ -71,38 +70,35 @@ const columns = computed(() => [ }, { label: t('item.list.grouping'), - field: 'grouping', name: 'grouping', align: 'left', }, { label: t('item.list.packing'), - field: 'packing', name: 'packing', align: 'left', }, { label: t('globals.description'), - field: 'name', name: 'description', align: 'left', create: true, + columnFilter: { + name: 'description', + }, }, { label: t('item.list.stems'), - field: 'stems', name: 'stems', align: 'left', }, { label: t('item.list.size'), - field: 'size', name: 'size', align: 'left', }, { label: t('item.list.typeName'), - field: 'typeName', name: 'typeName', align: 'left', component: 'select', @@ -117,7 +113,6 @@ const columns = computed(() => [ }, { label: t('item.list.category'), - field: 'category', name: 'category', align: 'left', component: 'select', @@ -131,7 +126,6 @@ const columns = computed(() => [ }, { label: t('item.list.intrastat'), - field: 'intrastat', name: 'intrastat', align: 'left', component: 'select', @@ -146,7 +140,6 @@ const columns = computed(() => [ }, { label: t('item.list.origin'), - field: 'origin', name: 'origin', align: 'left', component: 'select', @@ -161,13 +154,11 @@ const columns = computed(() => [ }, { label: t('item.list.userName'), - field: 'userName', name: 'userName', align: 'left', }, { label: t('item.list.weightByPiece'), - field: 'weightByPiece', name: 'weightByPiece', align: 'left', component: 'input', @@ -177,7 +168,6 @@ const columns = computed(() => [ }, { label: t('item.list.stemMultiplier'), - field: 'stemMultiplier', name: 'stemMultiplier', align: 'left', component: 'input', @@ -187,14 +177,12 @@ const columns = computed(() => [ }, { label: t('item.list.isActive'), - field: 'isActive', name: 'isActive', align: 'left', component: 'checkbox', }, { label: t('item.list.producer'), - field: 'producer', name: 'producer', align: 'left', component: 'select', @@ -208,7 +196,6 @@ const columns = computed(() => [ }, { label: t('item.list.landed'), - field: 'landed', name: 'landed', align: 'left', component: 'date', diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index be0a9c2e8..4bf45fe4c 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -188,7 +188,7 @@ onMounted(async () => { ref="tableRef" data-key="itemRequest" url="ticketRequests/filter" - order="shippedDate ASC, isOk ASC" + order="shipped ASC, isOk ASC" :columns="columns" :user-params="userParams" :is-editable="true" From 68a2ac385f6638d0b23168f085984b2d8b40d313 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 12 Sep 2024 11:36:02 +0200 Subject: [PATCH 029/150] refs #7283 fix items images --- src/pages/Item/ItemList.vue | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index c31302bba..ca00a6502 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -50,9 +50,9 @@ const columns = computed(() => [ align: 'left', columnField: { component: VnImg, - attrs: (id) => { + attrs: ({ row }) => { return { - id, + id: row.id, width: '50px', }; }, @@ -102,9 +102,11 @@ const columns = computed(() => [ name: 'typeName', align: 'left', component: 'select', - attrs: { - url: 'ItemTypes', - fields: ['id', 'name'], + columnFilter: { + attrs: { + url: 'ItemTypes', + fields: ['name'], + }, }, columnField: { component: null, From 050d8ae208329f51e38f3e3acb79fe4c8e8a991a Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 12 Sep 2024 11:43:37 +0200 Subject: [PATCH 030/150] refs #7283 fix items error get images --- src/pages/Item/ItemList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index ca00a6502..bed30725b 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -52,7 +52,7 @@ const columns = computed(() => [ component: VnImg, attrs: ({ row }) => { return { - id: row.id, + id: row?.id, width: '50px', }; }, From 1916c6e4bf0b3b161b9c1ccaf6d554d5e5c2c83b Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 16 Sep 2024 14:29:39 +0200 Subject: [PATCH 031/150] refs #7283 itemFilters --- src/pages/Item/ItemList.vue | 50 ++++++++++++++++++++++++---------- src/pages/Item/ItemRequest.vue | 11 ++++++-- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index bed30725b..b6668edea 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -84,7 +84,7 @@ const columns = computed(() => [ align: 'left', create: true, columnFilter: { - name: 'description', + name: 'search', }, }, { @@ -103,9 +103,10 @@ const columns = computed(() => [ align: 'left', component: 'select', columnFilter: { + name: 'typeFk', attrs: { url: 'ItemTypes', - fields: ['name'], + fields: ['id', 'name'], }, }, columnField: { @@ -118,9 +119,12 @@ const columns = computed(() => [ name: 'category', align: 'left', component: 'select', - attrs: { - url: 'ItemCategories', - fields: ['id', 'name'], + columnFilter: { + name: 'categoryFk', + attrs: { + url: 'ItemCategories', + fields: ['id', 'name'], + }, }, columnField: { component: null, @@ -131,9 +135,15 @@ const columns = computed(() => [ name: 'intrastat', align: 'left', component: 'select', - attrs: { - url: 'Intrastats', - fields: ['id', 'description'], + columnFilter: { + name: 'description', + attrs: { + url: 'Intrastats', + optionValue: 'description', + optionLabel: 'description', + }, + inWhere: true, + alias: 'intr', }, columnField: { component: null, @@ -145,9 +155,15 @@ const columns = computed(() => [ name: 'origin', align: 'left', component: 'select', - attrs: { - url: 'Origins', - fields: ['id', 'name'], + columnFilter: { + name: 'id', + attrs: { + url: 'Origins', + optionValue: 'id', + optionLabel: 'code', + }, + inWhere: true, + alias: 'ori', }, columnField: { component: null, @@ -158,6 +174,14 @@ const columns = computed(() => [ label: t('item.list.userName'), name: 'userName', align: 'left', + columnFilter: { + name: 'workerFk', + attrs: { + url: 'Users', + optionValue: 'id', + optionLabel: 'userName', + }, + }, }, { label: t('item.list.weightByPiece'), @@ -249,8 +273,6 @@ const cloneItem = async (itemFk) => { data-key="ItemList" url="Items/filter" url-create="Items" - save-url="Items/crud" - :filter="itemFilter" :create="{ urlCreate: 'Items', title: 'Create Item', @@ -264,7 +286,7 @@ const cloneItem = async (itemFk) => { auto-load redirect="Item" :is-editable="false" - :use-model="true" + :filer="itemFilter" > <template #column-userName="{ row }"> <span class="link" @click.stop> diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 4bf45fe4c..1a1d98211 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -57,45 +57,51 @@ const columns = computed(() => [ component: null, }, format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.shipped)), + columnClass: 'shrink', }, { label: t('globals.description'), field: 'description', name: 'description', align: 'left', + columnClass: 'expand', }, { label: t('item.buyRequest.requester'), name: 'requesterName', - align: 'left', + columnClass: 'shrink', }, { label: t('item.buyRequest.requested'), name: 'quantity', - align: 'left', + columnClass: 'shrink', }, { label: t('item.buyRequest.price'), name: 'price', align: 'left', format: (row) => toCurrency(row.price), + columnClass: 'shrink', }, { label: t('item.buyRequest.attender'), name: 'attenderName', align: 'left', + columnClass: 'shrink', }, { label: t('item.buyRequest.item'), name: 'item', align: 'left', component: 'input', + columnClass: 'expand', }, { label: t('item.buyRequest.achieved'), name: 'achieved', align: 'left', component: 'input', + columnClass: 'shrink', }, { label: t('item.buyRequest.concept'), @@ -103,6 +109,7 @@ const columns = computed(() => [ align: 'left', sortable: true, component: 'input', + columnClass: 'expand', }, { label: t('item.buyRequest.state'), From db783e15384163fdb7221afae2fc4e509e0c4c52 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 17 Sep 2024 15:03:30 +0200 Subject: [PATCH 032/150] refs #7283 item Descriptor --- src/composables/useArrayData.js | 1 + src/pages/Item/Card/ItemDescriptor.vue | 70 ++++++------- src/pages/Item/Card/ItemDescriptorImage.vue | 8 +- src/pages/Item/Card/ItemDiary.vue | 110 +++++++++++--------- 4 files changed, 98 insertions(+), 91 deletions(-) diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 651bcefb0..6671632b3 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -246,6 +246,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { } function updateStateParams() { + if (!route) return; const newUrl = { path: route.path, query: { ...(route.query ?? {}) } }; newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter); diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue index baac0c608..a14ee1446 100644 --- a/src/pages/Item/Card/ItemDescriptor.vue +++ b/src/pages/Item/Card/ItemDescriptor.vue @@ -14,6 +14,7 @@ import useCardDescription from 'src/composables/useCardDescription'; import { getUrl } from 'src/composables/getUrl'; import axios from 'axios'; import { dashIfEmpty } from 'src/filters'; +import { useArrayData } from 'src/composables/useArrayData'; const $props = defineProps({ id: { @@ -49,58 +50,49 @@ const entityId = computed(() => { }); const regularizeStockFormDialog = ref(null); -const available = ref(null); -const visible = ref(null); const salixUrl = ref(); +const mounted = ref(); + +const arrayDataStock = useArrayData('descriptorStock', { + url: `Items/${entityId.value}/getVisibleAvailable`, +}); onMounted(async () => { - salixUrl.value = await getUrl(''); + salixUrl.value = await getUrl('getVisibleAvailable'); await getItemConfigs(); - await updateStock(); + mounted.value = true; }); const data = ref(useCardDescription()); const setData = async (entity) => { - try { - if (!entity) return; - data.value = useCardDescription(entity.name, entity.id); - await updateStock(); - } catch (err) { - console.error('Error item'); - } + if (!entity) return; + data.value = useCardDescription(entity.name, entity.id); + await updateStock(); }; const getItemConfigs = async () => { - try { - const { data } = await axios.get('ItemConfigs/findOne'); - if (!data) return; - return (warehouseConfig.value = data.warehouseFk); - } catch (err) { - console.error('Error item'); - } + const { data } = await axios.get('ItemConfigs/findOne'); + if (!data) return; + return (warehouseConfig.value = data.warehouseFk); }; const updateStock = async () => { - try { - available.value = null; - visible.value = null; + if (!mounted.value) return; + await getItemConfigs(); - const params = { - warehouseFk: $props.warehouseFk, - dated: $props.dated, - }; + const params = { + warehouseFk: $props.warehouseFk ?? warehouseConfig.value, + dated: $props.dated, + }; - await getItemConfigs(); - if (!params.warehouseFk) { - params.warehouseFk = warehouseConfig.value; - } - const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, { - params, - }); - available.value = data.available; - visible.value = data.visible; - } catch (err) { - console.error('Error updating stock'); - } + if (!params.warehouseFk) return; + + const stock = useArrayData('descriptorStock', { + url: `Items/${entityId.value}/getVisibleAvailable`, + userParams: params, + }); + const storeData = stock.store.data; + if (storeData?.itemFk == entityId.value) return; + await stock.fetch({}); }; const openRegularizeStockForm = () => { @@ -163,8 +155,8 @@ const openCloneDialog = async () => { <template #before> <ItemDescriptorImage :entity-id="entityId" - :visible="visible" - :available="available" + :visible="arrayDataStock.store.data?.visible" + :available="arrayDataStock.store.data?.available" /> </template> <template #body="{ entity }"> diff --git a/src/pages/Item/Card/ItemDescriptorImage.vue b/src/pages/Item/Card/ItemDescriptorImage.vue index a4ef22ce3..b035a630a 100644 --- a/src/pages/Item/Card/ItemDescriptorImage.vue +++ b/src/pages/Item/Card/ItemDescriptorImage.vue @@ -32,6 +32,10 @@ const editPhotoFormDialog = ref(null); const showEditPhotoForm = ref(false); const warehouseName = ref(null); +onMounted(async () => { + getItemConfigs(); +}); + const toggleEditPictureForm = () => { showEditPhotoForm.value = !showEditPhotoForm.value; }; @@ -56,10 +60,6 @@ const getWarehouseName = async (warehouseFk) => { warehouseName.value = data.name; }; -onMounted(async () => { - getItemConfigs(); -}); - const handlePhotoUpdated = (evt = false) => { image.value.reload(evt); }; diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue index 68633caa2..a36b6a246 100644 --- a/src/pages/Item/Card/ItemDiary.vue +++ b/src/pages/Item/Card/ItemDiary.vue @@ -17,6 +17,7 @@ import { toDateFormat } from 'src/filters/date.js'; import { dashIfEmpty } from 'src/filters'; import { date } from 'quasar'; import { useState } from 'src/composables/useState'; +import { useArrayData } from 'src/composables/useArrayData'; import axios from 'axios'; const { t } = useI18n(); @@ -37,6 +38,33 @@ const warehouseFk = ref(null); const _showWhatsBeforeInventory = ref(false); const inventoriedDate = ref(null); +const originTypeMap = { + entry: { + descriptor: EntryDescriptorProxy, + icon: 'vn:entry', + color: 'green', + }, + ticket: { + descriptor: TicketDescriptorProxy, + icon: 'vn:ticket', + color: 'red', + }, + order: { + descriptor: OrderDescriptorProxy, + icon: 'vn:basket', + color: 'yellow', + }, +}; + +const entityTypeMap = { + client: { + descriptor: CustomerDescriptorProxy, + }, + supplier: { + descriptor: SupplierDescriptorProxy, + }, +}; + const columns = computed(() => [ { name: 'claim', @@ -105,6 +133,28 @@ const showWhatsBeforeInventory = computed({ }, }); +onMounted(async () => { + today.value.setHours(0, 0, 0, 0); + if (route.query.warehouseFk) warehouseFk.value = route.query.warehouseFk; + else if (user.value) warehouseFk.value = user.value.warehouseFk; + itemsBalanceFilter.where.warehouseFk = warehouseFk.value; + const { data } = await axios.get('Configs/findOne'); + inventoriedDate.value = data.inventoried; + await fetchItemBalances(); + await scrollToToday(); + await updateWarehouse(warehouseFk.value); +}); + +onUnmounted(() => (stateStore.rightDrawer = false)); + +watch( + () => router.currentRoute.value.params.id, + (newId) => { + itemsBalanceFilter.where.itemFk = newId; + itemBalancesRef.value.fetch(); + } +); + const fetchItemBalances = async () => await itemBalancesRef.value.fetch(); const getBadgeAttrs = (_date) => { @@ -131,53 +181,15 @@ const formatDateForAttribute = (dateValue) => { return dateValue; }; -const originTypeMap = { - entry: { - descriptor: EntryDescriptorProxy, - icon: 'vn:entry', - color: 'green', - }, - ticket: { - descriptor: TicketDescriptorProxy, - icon: 'vn:ticket', - color: 'red', - }, - order: { - descriptor: OrderDescriptorProxy, - icon: 'vn:basket', - color: 'yellow', - }, -}; - -const entityTypeMap = { - client: { - descriptor: CustomerDescriptorProxy, - }, - supplier: { - descriptor: SupplierDescriptorProxy, - }, -}; - -onMounted(async () => { - today.value.setHours(0, 0, 0, 0); - if (route.query.warehouseFk) warehouseFk.value = route.query.warehouseFk; - else if (user.value) warehouseFk.value = user.value.warehouseFk; - itemsBalanceFilter.where.warehouseFk = warehouseFk.value; - const { data } = await axios.get('Configs/findOne'); - inventoriedDate.value = data.inventoried; - await fetchItemBalances(); - await scrollToToday(); -}); - -onUnmounted(() => (stateStore.rightDrawer = false)); - -watch( - () => router.currentRoute.value.params.id, - (newId) => { - itemsBalanceFilter.where.itemFk = newId; - itemBalancesRef.value.fetch(); - } -); +async function updateWarehouse(warehouseFk) { + const stock = useArrayData('descriptorStock', { + userParams: { + warehouseFk, + }, + }); + await stock.fetch({}); + stock.store.data.itemFk = route.params.id +} </script> <template> @@ -203,7 +215,9 @@ watch( option-value="id" dense v-model="itemsBalanceFilter.where.warehouseFk" - @update:model-value="fetchItemBalances" + @update:model-value=" + (value) => fetchItemBalances() && updateWarehouse(value) + " class="q-mr-lg" /> <QCheckbox From 716d018121026767ecd2e9b5b1f47dffa349a028 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Fri, 20 Sep 2024 14:20:26 +0200 Subject: [PATCH 033/150] refs #72983 fix filters --- src/pages/Item/ItemList.vue | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index b6668edea..48fe3280e 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -54,6 +54,7 @@ const columns = computed(() => [ return { id: row?.id, width: '50px', + zoomResolution: '1600x900', }; }, }, @@ -72,11 +73,19 @@ const columns = computed(() => [ label: t('item.list.grouping'), name: 'grouping', align: 'left', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { label: t('item.list.packing'), name: 'packing', align: 'left', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { label: t('globals.description'), @@ -91,11 +100,19 @@ const columns = computed(() => [ label: t('item.list.stems'), name: 'stems', align: 'left', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { label: t('item.list.size'), name: 'size', align: 'left', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { label: t('item.list.typeName'), From 54015fb6bfbf975a7fed3150cf82de904df56d63 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 1 Oct 2024 08:44:08 +0200 Subject: [PATCH 034/150] refs #7283 fix itemMigration --- src/pages/Item/ItemRequest.vue | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 1a1d98211..7fbeccbf9 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -117,6 +117,15 @@ const columns = computed(() => [ format: (row) => getState(row.isOk), align: 'left', }, + { + align: 'left', + name: 'daysOnward', + label: t('item.buyRequest.daysOnward'), + visible: false, + columnFilter: { + inWhere: false, + }, + }, { align: 'right', label: '', @@ -238,6 +247,17 @@ onMounted(async () => { {{ row.itemDescription }} </span> </template> + <template #moreFilterPanel="{ params }"> + <VnInputNumber + :label="t('params.scopeDays')" + v-model.number="params.scopeDays" + @keyup.enter="(evt) => handleScopeDays(evt.target.value)" + @remove="handleScopeDays()" + class="q-px-xs q-pr-lg" + filled + dense + /> + </template> <template #column-denyOptions="{ row, rowIndex }"> <QTd class="sticky no-padding"> <QIcon From 55ddc8644f0450091f49ce96d4ef1ad2d3988a68 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 2 Oct 2024 12:51:33 +0200 Subject: [PATCH 035/150] refs #7283 fix itemMigration list filters --- src/pages/Item/ItemList.vue | 8 +++++++- src/pages/Item/ItemRequest.vue | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 48fe3280e..ad2c2e238 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -208,6 +208,9 @@ const columns = computed(() => [ columnField: { component: null, }, + columnFilter: { + inWhere: true, + }, }, { label: t('item.list.stemMultiplier'), @@ -217,11 +220,14 @@ const columns = computed(() => [ columnField: { component: null, }, + columnFilter: { + inWhere: true, + }, }, { label: t('item.list.isActive'), name: 'isActive', - align: 'left', + align: 'center', component: 'checkbox', }, { diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 7fbeccbf9..0eba6f9a4 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -29,6 +29,7 @@ const store = arrayData.store; const userParams = { state: 'pending', + daysOnward: 7, }; const tableRef = ref(); @@ -120,7 +121,7 @@ const columns = computed(() => [ { align: 'left', name: 'daysOnward', - label: t('item.buyRequest.daysOnward'), + label: t('travel.travelList.tableVisibleColumns.daysOnward'), visible: false, columnFilter: { inWhere: false, From 419d3d2d45b16d268da452d06fbc1aadfeb03707 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Thu, 10 Oct 2024 07:20:33 +0200 Subject: [PATCH 036/150] fix: refs #6896 fixed module problems --- src/components/ui/VnFilterPanel.vue | 12 ++- src/components/ui/VnSearchbar.vue | 2 + src/i18n/locale/en.yml | 2 + src/i18n/locale/es.yml | 4 +- src/pages/Order/Card/OrderCatalog.vue | 6 +- src/pages/Order/Card/OrderCatalogFilter.vue | 88 +++++++------------- src/pages/Order/Card/OrderDescriptor.vue | 10 +-- src/pages/Order/Card/OrderDescriptorMenu.vue | 7 +- src/pages/Order/Card/OrderLines.vue | 3 +- src/pages/Order/Card/OrderSummary.vue | 27 ++++++ 10 files changed, 87 insertions(+), 74 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 43d634ad9..242c4220d 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -185,6 +185,9 @@ async function remove(key) { } function formatValue(value) { + if (typeof value === 'object') { + return value; + } if (typeof value === 'boolean') return value ? t('Yes') : t('No'); if (isNaN(value) && !isNaN(Date.parse(value))) return toDate(value); @@ -193,6 +196,13 @@ function formatValue(value) { function sanitizer(params) { for (const [key, value] of Object.entries(params)) { + if (key == 'and') { + value.forEach((andValue) => { + params = { ...params, ...andValue }; + }); + delete params[key]; + } + if (value && typeof value === 'object') { const param = Object.values(value)[0]; if (typeof param == 'string') params[key] = param.replaceAll('%', ''); @@ -211,7 +221,7 @@ function sanitizer(params) { icon="search" @click="search()" ></QBtn> - <QForm @submit="search" id="filterPanelForm" @keyup.enter="search()"> + <QForm @submit="search(true)" id="filterPanelForm" @keyup.enter="search(true)"> <QList dense> <QItem class="q-mt-xs"> <QItemSection top> diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index dc6d4751c..fc292e980 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -9,6 +9,7 @@ import { useStateStore } from 'src/stores/useStateStore'; const quasar = useQuasar(); const { t } = useI18n(); const state = useStateStore(); +const emit = defineEmits(['on-search']); const props = defineProps({ dataKey: { @@ -118,6 +119,7 @@ async function search() { delete filter.params.search; } await arrayData.applyFilter(filter); + emit('on-search', store.data); } </script> <template> diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 6235447fe..e24926092 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -705,6 +705,8 @@ order: quantity: Quantity price: Price amount: Amount + confirm: Confirm + confirmLines: Confirm lines department: pageTitles: basicData: Basic data diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 6076af5d9..648868fcb 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -679,13 +679,15 @@ order: vat: IVA state: Estado alias: Alias - items: Items + items: Artículos orderTicketList: Tickets del pedido details: Detalles item: Item quantity: Cantidad price: Precio amount: Monto + confirm: Confirmar + confirmLines: Confirmar lineas shelving: list: parking: Parking diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index b13e8661d..110ea48af 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -86,11 +86,8 @@ function extractValueTags(items) { <div class="full-width"> <VnPaginate data-key="OrderCatalogList" - url="Orders/CatalogFilter" - :limit="50" - :user-params="catalogParams" - @on-fetch="extractTags" :update-router="false" + @on-change="extractTags" > <template #body="{ rows }"> <div class="catalog-list"> @@ -102,6 +99,7 @@ function extractValueTags(items) { :key="row.id" :item="row" is-catalog + class="fill-icon" /> </div> </template> diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 6de43e86a..9c713e77e 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -1,12 +1,11 @@ <script setup> -import { computed, ref } from 'vue'; +import { computed, ref, onMounted } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import axios from 'axios'; 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 VnInput from 'src/components/common/VnInput.vue'; import getParamWhere from 'src/filters/getParamWhere'; @@ -48,6 +47,15 @@ const orderWayList = ref([ const orderBySelected = ref('relevancy DESC, name'); const orderWaySelected = ref('ASC'); +const routeQuery = JSON.parse(route?.query.params ?? '{}'); +const params = ref({}); + +onMounted(() => { + const filter = routeQuery.filter; + params.value = JSON.parse(filter ?? '{}')?.where ?? {}; + if (Object.keys(params.value).length > 0) vnFilterPanelRef.value.search(); +}); + const createValue = (val, done) => { if (val.length > 2) { if (!tagOptions.value.includes(val)) { @@ -61,19 +69,15 @@ const resetCategory = () => { typeList.value = null; }; -const clearFilter = (key) => { - if (key === 'categoryFk') { +const selectCategory = (category, search) => { + if (!params.value?.filter) params.value.filter = { where: {} }; + const where = params.value.filter.where; + if (where.categoryFk === category?.id) { resetCategory(); - } -}; - -const selectCategory = (params, category, search) => { - if (params.categoryFk === category?.id) { - resetCategory(); - params.categoryFk = null; + where.categoryFk = null; } else { selectedCategoryFk.value = category?.id; - params.categoryFk = category?.id; + where.categoryFk = category?.id; loadTypes(category?.id); } search(); @@ -103,17 +107,6 @@ const selectedType = computed(() => { return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value); }); -function exprBuilder(param, value) { - switch (param) { - case 'categoryFk': - case 'typeFk': - return { [param]: value }; - case 'search': - if (/^\d+$/.test(value)) return { 'i.id': value }; - else return { 'i.name': { like: `%${value}%` } }; - } -} - const applyTagFilter = (params, search) => { if (!tagValues.value?.length) { params.tagGroups = null; @@ -138,15 +131,6 @@ const applyTagFilter = (params, search) => { tagValues.value = [{}]; }; -const removeTagChip = (selection, params, search) => { - if (params.tagGroups) { - params.tagGroups = (params.tagGroups || []).filter( - (value) => value !== selection - ); - } - search(); -}; - const setCategoryList = (data) => { categoryList.value = (data || []) .filter((category) => category.display) @@ -178,12 +162,11 @@ function addOrder(value, field, params) { <VnFilterPanel ref="vnFilterPanelRef" :data-key="props.dataKey" - :hidden-tags="['orderFk', 'orderBy']" - :un-removable-params="['orderFk', 'orderBy']" - :expr-builder="exprBuilder" - :custom-tags="['tagGroups']" - @remove="clearFilter" + v-model="params" :redirect="false" + :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']" + :un-removable-params="['orderFk', 'orderBy']" + :disable-submit-event="true" > <template #tags="{ tag, formatFn }"> <strong v-if="tag.label === 'categoryFk'"> @@ -192,30 +175,17 @@ function addOrder(value, field, params) { <strong v-else-if="tag.label === 'typeFk'"> {{ t(selectedType?.name || '') }} </strong> + <div v-else-if="tag.label === 'tagGroups'" class="q-gutter-x-xs"> + <strong v-if="JSON.parse(tag.value).tagSelection.name" + >{{ JSON.parse(tag.value).tagSelection?.name }}: + </strong> + <span>{{ JSON.parse(tag.value).values[0].value }}</span> + </div> <div v-else class="q-gutter-x-xs"> <strong>{{ t(`params.${tag.label}`) }}: </strong> <span>{{ formatFn(tag.value) }}</span> </div> </template> - <template #customTags="{ tags: customTags, params, searchFn }"> - <template v-for="tag in customTags" :key="tag.label"> - <template v-if="tag.label === 'tagGroups'"> - <VnFilterPanelChip - v-for="chip in tag.value" - :key="chip" - removable - @remove="removeTagChip(chip, params, searchFn)" - > - <strong> {{ JSON.parse(chip).tagSelection?.name }}: </strong> - <span>{{ - (JSON.parse(chip).values || []) - .map((item) => item.value) - .join(' | ') - }}</span> - </VnFilterPanelChip> - </template> - </template> - </template> <template #body="{ params, searchFn }"> <QItem class="category-filter q-mt-md"> <div @@ -226,7 +196,7 @@ function addOrder(value, field, params) { <QIcon :name="category.icon" class="category-icon" - @click="selectCategory(params, category, searchFn)" + @click="selectCategory(category, searchFn)" > <QTooltip> {{ t(category.name) }} @@ -320,7 +290,7 @@ function addOrder(value, field, params) { > <FetchData v-if="selectedTag" - :url="`Tags/${selectedTag}/filterValue`" + :url="`Tags/${selectedTag.id}/filterValue`" limit="30" auto-load @on-fetch="(data) => (tagOptions = data)" @@ -343,7 +313,7 @@ function addOrder(value, field, params) { @update:model-value="applyTagFilter(params, searchFn)" /> <VnSelect - v-else-if="selectedTag === 1" + v-else-if="selectedTag.id === 1" :label="t('params.value')" v-model="value.value" :options="tagOptions || []" diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue index a035971b0..74c8d8bed 100644 --- a/src/pages/Order/Card/OrderDescriptor.vue +++ b/src/pages/Order/Card/OrderDescriptor.vue @@ -71,10 +71,6 @@ const getConfirmationValue = (isConfirmed) => { }; const total = ref(null); - -function ticketFilter(order) { - return JSON.stringify({ id: order.id }); -} </script> <template> @@ -126,7 +122,11 @@ function ticketFilter(order) { color="primary" :to="{ name: 'TicketList', - query: { table: ticketFilter(entity) }, + query: { + table: JSON.stringify({ + orderFk: entity.id, + }), + }, }" > <QTooltip>{{ t('order.summary.orderTicketList') }}</QTooltip> diff --git a/src/pages/Order/Card/OrderDescriptorMenu.vue b/src/pages/Order/Card/OrderDescriptorMenu.vue index 2695da4e5..461efa22f 100644 --- a/src/pages/Order/Card/OrderDescriptorMenu.vue +++ b/src/pages/Order/Card/OrderDescriptorMenu.vue @@ -23,8 +23,8 @@ function confirmRemove() { .dialog({ component: VnConfirm, componentProps: { - title: t('globals.confirmDeletion'), - message: t('confirmDeletionMessage'), + title: t('You are going to delete this order'), + message: t('Continue anyway?'), promise: remove, }, }) @@ -57,5 +57,6 @@ en: es: deleteOrder: Eliminar pedido confirmDeletionMessage: Seguro que quieres eliminar este pedido? - + You are going to delete this order: El pedido se eliminará + Continue anyway?: ¿Continuar de todos modos? </i18n> diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index 175c956e1..0d00e04ed 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -168,7 +168,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Delete'), + title: t('Remove item'), icon: 'delete', show: (row) => !row.order.isConfirmed, action: (row) => confirmRemove(row), @@ -397,4 +397,5 @@ es: confirmDeletion: Confirmar eliminación, confirmDeletionMessage: Seguro que quieres eliminar este artículo? confirm: Confirmar + Remove item: Eliminar artículo </i18n> diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue index 60358f744..9bc89ad28 100644 --- a/src/pages/Order/Card/OrderSummary.vue +++ b/src/pages/Order/Card/OrderSummary.vue @@ -2,7 +2,10 @@ import { computed, ref } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; +import { useQuasar } from 'quasar'; +import axios from 'axios'; import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters'; +import { useArrayData } from 'composables/useArrayData'; import VnLv from 'components/ui/VnLv.vue'; import CardSummary from 'components/ui/CardSummary.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; @@ -21,6 +24,9 @@ const $props = defineProps({ }); const entityId = computed(() => $props.id || route.params.id); +const summary = ref(); +const quasar = useQuasar(); +const descriptorData = useArrayData('orderData'); const detailsColumns = ref([ { name: 'item', @@ -49,6 +55,16 @@ const detailsColumns = ref([ field: (row) => toCurrency(row?.quantity * row?.price), }, ]); + +async function confirmOrder() { + await axios.post(`Orders/${route.params.id}/confirm`); + quasar.notify({ + message: t('globals.confirm'), + type: 'positive', + }); + summary.value.fetch({}); + descriptorData.fetch({}); +} </script> <template> @@ -62,6 +78,17 @@ const detailsColumns = ref([ {{ t('order.summary.basket') }} #{{ entity?.id }} - {{ entity?.client?.name }} ({{ entity?.clientFk }}) </template> + <template #header-right> + <QBtn + flat + text-color="white" + :disabled="isConfirmed" + :label="t('order.summary.confirm')" + @click="confirmOrder()" + > + <QTooltip>{{ t('order.summary.confirmLines') }}</QTooltip> + </QBtn> + </template> <template #body="{ entity }"> <QCard class="vn-one"> <VnTitle From d7210837959ca94189a78071a8145d5509af419d Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Thu, 10 Oct 2024 08:43:33 +0200 Subject: [PATCH 037/150] fix: filter panel --- src/components/ui/VnFilterPanel.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 242c4220d..f042c6481 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -221,7 +221,7 @@ function sanitizer(params) { icon="search" @click="search()" ></QBtn> - <QForm @submit="search(true)" id="filterPanelForm" @keyup.enter="search(true)"> + <QForm @submit="search()" id="filterPanelForm" @keyup.enter="search(true)"> <QList dense> <QItem class="q-mt-xs"> <QItemSection top> From f16707756e99cd64e798e6190bf03845b82520a5 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Wed, 9 Oct 2024 00:40:21 +0200 Subject: [PATCH 038/150] fix: change type vnput --- src/pages/Ticket/Card/TicketCreateRequest.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Ticket/Card/TicketCreateRequest.vue b/src/pages/Ticket/Card/TicketCreateRequest.vue index ffe009516..d6e757afe 100644 --- a/src/pages/Ticket/Card/TicketCreateRequest.vue +++ b/src/pages/Ticket/Card/TicketCreateRequest.vue @@ -56,6 +56,7 @@ const attendersOptions = ref([]); v-model="data.price" :label="t('purchaseRequest.price')" type="number" + step="0.01" min="0" /> </VnRow> From 3d5d2673452f4aa1e584fa333f12bcfc83e1f52f Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 10 Oct 2024 09:29:12 +0200 Subject: [PATCH 039/150] revert: commit --- src/components/ui/VnFilterPanel.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index f042c6481..66e9df5f5 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -221,7 +221,7 @@ function sanitizer(params) { icon="search" @click="search()" ></QBtn> - <QForm @submit="search()" id="filterPanelForm" @keyup.enter="search(true)"> + <QForm @submit="search" id="filterPanelForm" @keyup.enter.prevent="search(true)"> <QList dense> <QItem class="q-mt-xs"> <QItemSection top> From 787ce2fc48d28728fcc27aa1df73705d02508188 Mon Sep 17 00:00:00 2001 From: guillermo <guillermo@verdnatura.es> Date: Mon, 14 Oct 2024 09:33:00 +0200 Subject: [PATCH 040/150] feat: refs #7006 itemTypeLog added --- src/pages/ItemType/Card/ItemTypeBasicData.vue | 1 + src/pages/ItemType/Card/ItemTypeLog.vue | 7 +++++++ src/pages/ItemType/locale/en.yml | 1 + src/pages/ItemType/locale/es.yml | 1 + src/router/modules/itemType.js | 11 ++++++++++- 5 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/pages/ItemType/Card/ItemTypeLog.vue diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue index 51e24272d..deaf4a5fe 100644 --- a/src/pages/ItemType/Card/ItemTypeBasicData.vue +++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue @@ -73,6 +73,7 @@ const temperaturesOptions = ref([]); option-label="name" hide-selected /> + <VnInput v-model="data.life" :label="t('shared.life')" /> </VnRow> </template> </FormModel> diff --git a/src/pages/ItemType/Card/ItemTypeLog.vue b/src/pages/ItemType/Card/ItemTypeLog.vue new file mode 100644 index 000000000..b3f7a6597 --- /dev/null +++ b/src/pages/ItemType/Card/ItemTypeLog.vue @@ -0,0 +1,7 @@ +<script setup> +import VnLog from 'src/components/common/VnLog.vue'; +</script> + +<template> + <VnLog model="ItemType" url="/ItemTypeLogs"></VnLog> +</template> diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/ItemType/locale/en.yml index 7889418ea..4b203bd68 100644 --- a/src/pages/ItemType/locale/en.yml +++ b/src/pages/ItemType/locale/en.yml @@ -4,6 +4,7 @@ shared: worker: Worker category: Category temperature: Temperature + life: Life summary: id: id life: Life diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/ItemType/locale/es.yml index 9a94dceb6..43699c332 100644 --- a/src/pages/ItemType/locale/es.yml +++ b/src/pages/ItemType/locale/es.yml @@ -4,6 +4,7 @@ shared: worker: Trabajador category: Reino temperature: Temperatura + life: Vida summary: id: id code: Código diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js index 8064c41ff..0fd3797e6 100644 --- a/src/router/modules/itemType.js +++ b/src/router/modules/itemType.js @@ -12,7 +12,7 @@ export default { redirect: { name: 'ItemTypeList' }, menus: { main: [], - card: ['ItemTypeBasicData'], + card: ['ItemTypeBasicData', 'ItemTypeLog'], }, children: [ { @@ -40,6 +40,15 @@ export default { component: () => import('src/pages/ItemType/Card/ItemTypeBasicData.vue'), }, + { + path: 'log', + name: 'ItemTypeLog', + meta: { + title: 'log', + icon: 'vn:History', + }, + component: () => import('src/pages/ItemType/Card/ItemTypeLog.vue'), + }, ], }, ], From f2cb0111eb6a5ceb1232da91f5190b5610c2e81d Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 16 Oct 2024 14:45:33 +0200 Subject: [PATCH 041/150] fix: refs #7283 fix image --- src/pages/Item/ItemList.vue | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index ad2c2e238..6c7b7eaed 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -1,10 +1,9 @@ <script setup> -import { ref, computed, onUnmounted } from 'vue'; +import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRouter, useRoute } from 'vue-router'; import VnImg from 'src/components/ui/VnImg.vue'; import VnTable from 'components/VnTable/VnTable.vue'; -import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'src/filters'; import axios from 'axios'; import FetchedTags from 'src/components/ui/FetchedTags.vue'; @@ -16,7 +15,6 @@ const entityId = computed(() => route.params.id); const { viewSummary } = useSummaryDialog(); const router = useRouter(); -const stateStore = useStateStore(); const { t } = useI18n(); const tableRef = ref(); const route = useRoute(); @@ -53,8 +51,8 @@ const columns = computed(() => [ attrs: ({ row }) => { return { id: row?.id, - width: '50px', zoomResolution: '1600x900', + zoom: true, }; }, }, @@ -116,9 +114,13 @@ const columns = computed(() => [ }, { label: t('item.list.typeName'), - name: 'typeName', + name: 'typeFk', align: 'left', component: 'select', + attrs: { + url: 'ItemTypes', + fields: ['id', 'name'], + }, columnFilter: { name: 'typeFk', attrs: { @@ -152,6 +154,11 @@ const columns = computed(() => [ name: 'intrastat', align: 'left', component: 'select', + attrs: { + url: 'Intrastats', + optionValue: 'description', + optionLabel: 'description', + }, columnFilter: { name: 'description', attrs: { @@ -159,7 +166,6 @@ const columns = computed(() => [ optionValue: 'description', optionLabel: 'description', }, - inWhere: true, alias: 'intr', }, columnField: { @@ -172,6 +178,11 @@ const columns = computed(() => [ name: 'origin', align: 'left', component: 'select', + attrs: { + url: 'Origins', + optionValue: 'id', + optionLabel: 'code', + }, columnFilter: { name: 'id', attrs: { From 11bf70d76ce3aa42d7c5ae2d94e4ebeb0a281082 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 18 Oct 2024 14:12:23 +0200 Subject: [PATCH 042/150] fix: category and tags filters --- src/components/ui/VnSearchbar.vue | 4 +- src/pages/Order/Card/OrderCatalogFilter.vue | 111 +++++++++++++++----- 2 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index fc292e980..569e0ed18 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -9,7 +9,7 @@ import { useStateStore } from 'src/stores/useStateStore'; const quasar = useQuasar(); const { t } = useI18n(); const state = useStateStore(); -const emit = defineEmits(['on-search']); +const emit = defineEmits(['onSearch']); const props = defineProps({ dataKey: { @@ -119,7 +119,7 @@ async function search() { delete filter.params.search; } await arrayData.applyFilter(filter); - emit('on-search', store.data); + emit('onSearch', store.data); } </script> <template> diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 9c713e77e..850212a62 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -1,12 +1,12 @@ <script setup> -import { computed, ref, onMounted } from 'vue'; +import { computed, ref, onMounted, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import axios from 'axios'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue'; -import VnInput from 'src/components/common/VnInput.vue'; +// import VnInput from 'src/components/common/VnInput.vue'; import getParamWhere from 'src/filters/getParamWhere'; const { t } = useI18n(); @@ -48,12 +48,15 @@ const orderBySelected = ref('relevancy DESC, name'); const orderWaySelected = ref('ASC'); const routeQuery = JSON.parse(route?.query.params ?? '{}'); -const params = ref({}); +const paramsSearch = ref({}); onMounted(() => { - const filter = routeQuery.filter; - params.value = JSON.parse(filter ?? '{}')?.where ?? {}; - if (Object.keys(params.value).length > 0) vnFilterPanelRef.value.search(); + paramsSearch.value = JSON.parse(routeQuery.filter ?? '{}')?.where ?? {}; + if (Object.keys(paramsSearch.value).length > 0) vnFilterPanelRef.value.search(); + if (routeQuery.categoryFk && routeQuery.typeFk) { + selectedCategoryFk.value = routeQuery.categoryFk; + selectedTypeFk.value = routeQuery.typeFk; + } }); const createValue = (val, done) => { @@ -70,12 +73,18 @@ const resetCategory = () => { }; const selectCategory = (category, search) => { - if (!params.value?.filter) params.value.filter = { where: {} }; - const where = params.value.filter.where; + if (!paramsSearch.value?.filter) paramsSearch.value.filter = { where: {} }; + const where = paramsSearch.value.filter.where; if (where.categoryFk === category?.id) { resetCategory(); where.categoryFk = null; } else { + if (where.categoryFk && where.categoryFk !== category?.id) { + paramsSearch.value.typeFk = null; + selectedTypeFk.value = null; + typeList.value = []; + } + selectedCategoryFk.value = category?.id; where.categoryFk = category?.id; loadTypes(category?.id); @@ -90,6 +99,10 @@ const loadTypes = async (categoryFk = selectedCategoryFk.value) => { typeList.value = data; }; +watch(selectedTypeFk, (newValue) => { + paramsSearch.value.typeFk = newValue; +}); + const selectedCategory = computed(() => (categoryList.value || []).find( (category) => category?.id === selectedCategoryFk.value @@ -107,25 +120,55 @@ const selectedType = computed(() => { return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value); }); +// const applyTagFilter = (params, search) => { + +// if (params.tagGroups) params.tagGroups = null; +// if (!tagValues.value?.length) { +// params.tagGroups = null; +// search(); +// return; +// } +// if (!params.tagGroups) { +// params.tagGroups = []; +// } +// params.tagGroups.push( +// JSON.stringify({ +// values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), +// tagSelection: { +// ...selectedTag.value, +// orgShowField: selectedTag?.value?.name, +// }, +// tagFk: selectedTag?.value?.tagFk, +// }) +// ); +// search(); +// selectedTag.value = null; +// tagValues.value = [{}]; +// }; + const applyTagFilter = (params, search) => { if (!tagValues.value?.length) { params.tagGroups = null; search(); return; } + if (!params.tagGroups) { params.tagGroups = []; + } else if (typeof params.tagGroups === 'string') { + params.tagGroups = [params.tagGroups]; } - params.tagGroups.push( - JSON.stringify({ - values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), - tagSelection: { - ...selectedTag.value, - orgShowField: selectedTag?.value?.name, - }, - tagFk: selectedTag?.value?.tagFk, - }) - ); + + const newTagGroup = JSON.stringify({ + values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), + tagSelection: { + ...selectedTag.value, + orgShowField: selectedTag?.value?.name, + }, + tagFk: selectedTag?.value?.tagFk, + }); + params.tagGroups.push(newTagGroup); + search(); selectedTag.value = null; tagValues.value = [{}]; @@ -155,6 +198,22 @@ function addOrder(value, field, params) { params.orderBy = JSON.stringify(orderBy); vnFilterPanelRef.value.search(); } + +const data = ref([]); + +function getTagData(value) { + if (Array.isArray(value) && value.length >= 1) { + const parsedTags = value.map((obj) => JSON.parse(obj)); + + if (parsedTags.length === 1) { + data.value = parsedTags[0]; + } else { + data.value = parsedTags; + } + } else { + data.value = JSON.parse(value); + } +} </script> <template> @@ -162,13 +221,14 @@ function addOrder(value, field, params) { <VnFilterPanel ref="vnFilterPanelRef" :data-key="props.dataKey" - v-model="params" + v-model="paramsSearch" :redirect="false" :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']" - :un-removable-params="['orderFk', 'orderBy']" + :unremovable-params="['orderFk', 'orderBy']" :disable-submit-event="true" > <template #tags="{ tag, formatFn }"> + {{ console.log('tag.label: ', tag.label) }} <strong v-if="tag.label === 'categoryFk'"> {{ t(selectedCategory?.name || '') }} </strong> @@ -176,10 +236,11 @@ function addOrder(value, field, params) { {{ t(selectedType?.name || '') }} </strong> <div v-else-if="tag.label === 'tagGroups'" class="q-gutter-x-xs"> - <strong v-if="JSON.parse(tag.value).tagSelection.name" - >{{ JSON.parse(tag.value).tagSelection?.name }}: - </strong> - <span>{{ JSON.parse(tag.value).values[0].value }}</span> + {{ getTagData(tag.value) }} + <strong> {{ data.tagSelection?.name }}: </strong> + <span> + {{ console.log('data', data.values) }} + </span> </div> <div v-else class="q-gutter-x-xs"> <strong>{{ t(`params.${tag.label}`) }}: </strong> @@ -328,7 +389,7 @@ function addOrder(value, field, params) { @new-value="createValue" @update:model-value="applyTagFilter(params, searchFn)" /> - <VnInput + <QInput v-else :label="t('params.value')" v-model="value.value" From 52981953f7e9efbc1a8875c321376e02a50c3b33 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 21 Oct 2024 13:23:45 +0200 Subject: [PATCH 043/150] feat(): refs #8039 canceledError not notify --- src/boot/axios.js | 5 +++-- src/boot/quasar.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/boot/axios.js b/src/boot/axios.js index 3bd80f487..d3e981adb 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -3,6 +3,7 @@ import { useSession } from 'src/composables/useSession'; import { Router } from 'src/router'; import useNotify from 'src/composables/useNotify.js'; import { useStateQueryStore } from 'src/stores/useStateQueryStore'; +import { CanceledError } from 'axios'; const session = useSession(); const { notify } = useNotify(); @@ -42,7 +43,7 @@ const onResponseError = (error) => { let message = ''; const response = error.response; - const responseData = response && response.data; + const responseData = response?.data; const responseError = responseData && response.data.error; if (responseError) { message = responseError.message; @@ -78,7 +79,7 @@ const onResponseError = (error) => { return Promise.reject(error); } - notify(message, 'negative'); + if (!(error instanceof CanceledError)) notify(message, 'negative'); return Promise.reject(error); }; diff --git a/src/boot/quasar.js b/src/boot/quasar.js index 5db6edd24..41a7990c7 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -3,6 +3,7 @@ import qFormMixin from './qformMixin'; import mainShortcutMixin from './mainShortcutMixin'; import keyShortcut from './keyShortcut'; import useNotify from 'src/composables/useNotify.js'; +import { CanceledError } from 'axios'; const { notify } = useNotify(); export default boot(({ app }) => { @@ -11,6 +12,6 @@ export default boot(({ app }) => { app.directive('shortcut', keyShortcut); app.config.errorHandler = function (err) { console.error(err); - notify('globals.error', 'negative', 'error'); + if (!(err instanceof CanceledError)) notify('globals.error', 'negative', 'error'); }; }); From 9780fe596fa0af4212f308a3150d1066a3275517 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 21 Oct 2024 13:33:12 +0200 Subject: [PATCH 044/150] feat: refs #8039 notify error unify --- src/boot/axios.js | 34 +--------------------------------- src/boot/quasar.js | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/boot/axios.js b/src/boot/axios.js index d3e981adb..b084b835d 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -40,37 +40,7 @@ const onResponse = (response) => { const onResponseError = (error) => { stateQuery.remove(error.config); - let message = ''; - - const response = error.response; - const responseData = response?.data; - const responseError = responseData && response.data.error; - if (responseError) { - message = responseError.message; - } - - switch (response?.status) { - case 422: - if (error.name == 'ValidationError') - message += - ' "' + - responseError.details.context + - '.' + - Object.keys(responseError.details.codes).join(',') + - '"'; - break; - case 500: - message = 'errors.statusInternalServerError'; - break; - case 502: - message = 'errors.statusBadGateway'; - break; - case 504: - message = 'errors.statusGatewayTimeout'; - break; - } - - if (session.isLoggedIn() && response?.status === 401) { + if (session.isLoggedIn() && error.response?.status === 401) { session.destroy(false); const hash = window.location.hash; const url = hash.slice(1); @@ -79,8 +49,6 @@ const onResponseError = (error) => { return Promise.reject(error); } - if (!(error instanceof CanceledError)) notify(message, 'negative'); - return Promise.reject(error); }; diff --git a/src/boot/quasar.js b/src/boot/quasar.js index 41a7990c7..bf5175ee6 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -10,8 +10,39 @@ export default boot(({ app }) => { app.mixin(qFormMixin); app.mixin(mainShortcutMixin); app.directive('shortcut', keyShortcut); - app.config.errorHandler = function (err) { - console.error(err); - if (!(err instanceof CanceledError)) notify('globals.error', 'negative', 'error'); + app.config.errorHandler = (error) => { + let message; + const response = error.response; + const responseData = response?.data; + const responseError = responseData && response.data.error; + if (responseError) { + message = responseError.message; + } + + switch (response?.status) { + case 422: + if (error.name == 'ValidationError') + message += + ' "' + + responseError.details.context + + '.' + + Object.keys(responseError.details.codes).join(',') + + '"'; + break; + case 500: + message = 'errors.statusInternalServerError'; + break; + case 502: + message = 'errors.statusBadGateway'; + break; + case 504: + message = 'errors.statusGatewayTimeout'; + break; + } + + console.error(error); + if (error instanceof CanceledError) return; + + notify(message ?? 'globals.error', 'negative', 'error'); }; }); From c69f7af3906e5d88f321350813ec42cc692bd6f4 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 21 Oct 2024 13:38:35 +0200 Subject: [PATCH 045/150] test: refs #8039 axios not notify --- test/vitest/__tests__/boot/axios.spec.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/test/vitest/__tests__/boot/axios.spec.js b/test/vitest/__tests__/boot/axios.spec.js index 7a802b4d2..19d396ec5 100644 --- a/test/vitest/__tests__/boot/axios.spec.js +++ b/test/vitest/__tests__/boot/axios.spec.js @@ -36,8 +36,6 @@ describe('Axios boot', () => { describe('onResponseError()', async () => { it('should call to the Notify plugin with a message error for an status code "500"', async () => { - Notify.create = vi.fn(); - const error = { response: { status: 500, @@ -45,19 +43,10 @@ describe('Axios boot', () => { }; const result = onResponseError(error); - expect(result).rejects.toEqual(expect.objectContaining(error)); - expect(Notify.create).toHaveBeenCalledWith( - expect.objectContaining({ - message: 'An internal server error has ocurred', - type: 'negative', - }) - ); }); it('should call to the Notify plugin with a message from the response property', async () => { - Notify.create = vi.fn(); - const error = { response: { status: 401, @@ -70,14 +59,7 @@ describe('Axios boot', () => { }; const result = onResponseError(error); - expect(result).rejects.toEqual(expect.objectContaining(error)); - expect(Notify.create).toHaveBeenCalledWith( - expect.objectContaining({ - message: 'Invalid user or password', - type: 'negative', - }) - ); }); }); }); From 29a7f3b2fed499111e620941fd17baa55611a388 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Mon, 21 Oct 2024 13:48:43 +0200 Subject: [PATCH 046/150] fix: refs #7283 #7283 ItemDiary subToolbar --- src/pages/Item/Card/ItemDiary.vue | 67 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue index a36b6a246..23dc6214e 100644 --- a/src/pages/Item/Card/ItemDiary.vue +++ b/src/pages/Item/Card/ItemDiary.vue @@ -188,7 +188,7 @@ async function updateWarehouse(warehouseFk) { }, }); await stock.fetch({}); - stock.store.data.itemFk = route.params.id + stock.store.data.itemFk = route.params.id; } </script> @@ -205,38 +205,39 @@ async function updateWarehouse(warehouseFk) { auto-load @on-fetch="(data) => (warehousesOptions = data)" /> - <QToolbar class="justify-end"> - <div id="st-data" class="row"> - <VnSelect - :label="t('itemDiary.warehouse')" - :options="warehousesOptions" - hide-selected - option-label="name" - option-value="id" - dense - v-model="itemsBalanceFilter.where.warehouseFk" - @update:model-value=" - (value) => fetchItemBalances() && updateWarehouse(value) - " - class="q-mr-lg" - /> - <QCheckbox - :label="t('itemDiary.showBefore')" - v-model="showWhatsBeforeInventory" - @update:model-value="fetchItemBalances" - class="q-mr-lg" - /> - <VnInputDate - v-if="showWhatsBeforeInventory" - :label="t('itemDiary.since')" - dense - v-model="itemsBalanceFilter.where.date" - @update:model-value="fetchItemBalances" - /> - </div> - <QSpace /> - <div id="st-actions"></div> - </QToolbar> + <template v-if="stateStore.isHeaderMounted()"> + <Teleport to="#st-data"> + <div class="row"> + <VnSelect + :label="t('itemDiary.warehouse')" + :options="warehousesOptions" + hide-selected + option-label="name" + option-value="id" + dense + v-model="itemsBalanceFilter.where.warehouseFk" + @update:model-value=" + (value) => fetchItemBalances() && updateWarehouse(value) + " + class="q-mr-lg" + /> + <QCheckbox + :label="t('itemDiary.showBefore')" + v-model="showWhatsBeforeInventory" + @update:model-value="fetchItemBalances" + class="q-mr-lg" + /> + <VnInputDate + v-if="showWhatsBeforeInventory" + :label="t('itemDiary.since')" + dense + v-model="itemsBalanceFilter.where.date" + @update:model-value="fetchItemBalances" + /> + </div> + </Teleport> + <Teleport to="#st-actions"> </Teleport> + </template> <QPage class="column items-center q-pa-md"> <QTable :rows="itemBalances" From f4a7e20c574ab8603d6f4b2dabd222556a9a31ea Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Mon, 21 Oct 2024 14:31:31 +0200 Subject: [PATCH 047/150] test: refs #6943 #6943 add comands to solve tests --- .../integration/client/clientBalance.spec.js | 2 +- .../integration/client/clientCredits.spec.js | 2 +- .../integration/client/clientList.spec.js | 6 +-- .../client/clientRecoveries.spec.js | 2 +- .../clientCreditOpinion.spec.js | 2 +- .../client/others/clientConsumption.spec.js | 2 +- .../client/others/clientMandates.spec.js | 2 +- .../client/others/clientSamples.spec.js | 2 +- .../client/others/clientWebPayments.spec.js | 4 +- test/cypress/support/commands.js | 42 +++++++++++++++++++ 10 files changed, 54 insertions(+), 12 deletions(-) diff --git a/test/cypress/integration/client/clientBalance.spec.js b/test/cypress/integration/client/clientBalance.spec.js index 4a666bdb1..dfba56b16 100644 --- a/test/cypress/integration/client/clientBalance.spec.js +++ b/test/cypress/integration/client/clientBalance.spec.js @@ -8,6 +8,6 @@ describe('Client balance', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/clientCredits.spec.js b/test/cypress/integration/client/clientCredits.spec.js index f81bf987d..5f303b40d 100644 --- a/test/cypress/integration/client/clientCredits.spec.js +++ b/test/cypress/integration/client/clientCredits.spec.js @@ -8,6 +8,6 @@ describe('Client credits', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index 93e53b9f6..909bd3933 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -44,7 +44,7 @@ describe('Client list', () => { }); }); - it('Client founded create ticket', () => { + it.skip('Client founded create ticket', () => { const search = 'Jessica Jones'; cy.searchByLabel('Name', search); cy.clickButtonsDescriptor(2); @@ -52,10 +52,10 @@ describe('Client list', () => { cy.waitForElement('.q-form'); cy.checkValueForm(1, search); }); - it('Client founded create order', () => { + it.skip('Client founded create order', () => { const search = 'Jessica Jones'; cy.searchByLabel('Name', search); - cy.clickButtonsDescriptor(4); + cy.clickButtonsDescriptor(3); cy.waitForElement('#formModel'); cy.waitForElement('.q-form'); cy.checkValueForm(2, search); diff --git a/test/cypress/integration/client/clientRecoveries.spec.js b/test/cypress/integration/client/clientRecoveries.spec.js index a4e220008..ea6f14407 100644 --- a/test/cypress/integration/client/clientRecoveries.spec.js +++ b/test/cypress/integration/client/clientRecoveries.spec.js @@ -8,6 +8,6 @@ describe('Client recoveries', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js b/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js index 7d9c0fa77..c32215f01 100644 --- a/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js +++ b/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js @@ -8,6 +8,6 @@ describe('Client credit opinion', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/others/clientConsumption.spec.js b/test/cypress/integration/client/others/clientConsumption.spec.js index 179a37707..bbc11998e 100644 --- a/test/cypress/integration/client/others/clientConsumption.spec.js +++ b/test/cypress/integration/client/others/clientConsumption.spec.js @@ -8,6 +8,6 @@ describe('Client consumption', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/others/clientMandates.spec.js b/test/cypress/integration/client/others/clientMandates.spec.js index aaeb7f930..055eda2d0 100644 --- a/test/cypress/integration/client/others/clientMandates.spec.js +++ b/test/cypress/integration/client/others/clientMandates.spec.js @@ -8,6 +8,6 @@ describe('Client mandates', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/others/clientSamples.spec.js b/test/cypress/integration/client/others/clientSamples.spec.js index 03b7238f4..a50120402 100644 --- a/test/cypress/integration/client/others/clientSamples.spec.js +++ b/test/cypress/integration/client/others/clientSamples.spec.js @@ -8,6 +8,6 @@ describe('Client samples', () => { }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/integration/client/others/clientWebPayments.spec.js b/test/cypress/integration/client/others/clientWebPayments.spec.js index 5f7087d21..f35b12612 100644 --- a/test/cypress/integration/client/others/clientWebPayments.spec.js +++ b/test/cypress/integration/client/others/clientWebPayments.spec.js @@ -3,11 +3,11 @@ describe('Client web payments', () => { beforeEach(() => { cy.viewport(1280, 720); cy.login('developer'); - cy.visit('#/customer/1101/others/web-payments', { + cy.visit('#/customer/1101/others/web-payment', { timeout: 5000, }); }); it('Should load layout', () => { - cy.get('.q-card').should('be.visible'); + cy.get('.q-page').should('be.visible'); }); }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 83f45b721..9106a64cd 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -245,6 +245,13 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => { cy.get(selector).should('have.text', expectedValue); }); +Cypress.Commands.add('openActionDescriptor', (opt) => { + cy.openActionsDescriptor(); + const listItem = '[role="menu"] .q-list .q-item'; + cy.contains(listItem, opt).click(); + 1; +}); + Cypress.Commands.add('openActionsDescriptor', () => { cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click(); }); @@ -254,3 +261,38 @@ Cypress.Commands.add('openUserPanel', () => { '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image' ).click(); }); + +Cypress.Commands.add('clickButtonsDescriptor', (id) => { + cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`) + .invoke('removeAttr', 'target') + .click(); +}); + +Cypress.Commands.add('openActions', (row) => { + cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); +}); + +Cypress.Commands.add('checkNotification', (type) => { + const values = { + created: 'Data created', + updated: 'Data saved', + deleted: 'Data deleted', + }; + cy.get('.q-notification__message').should('have.text', values[type]); +}); + +Cypress.Commands.add('checkValueForm', (id, search) => { + cy.get( + `.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input` + ).should('have.value', search); +}); + +Cypress.Commands.add('checkValueSelectForm', (id, search) => { + cy.get( + `.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input` + ).should('have.value', search); +}); + +Cypress.Commands.add('searchByLabel', (label, value) => { + cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`); +}); From f3a62091893009b75b4ef3a701f7db1b43f151e1 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 22 Oct 2024 12:00:11 +0200 Subject: [PATCH 048/150] fix: refs #7283 #7283 bugs --- src/pages/Item/Card/ItemDescriptorImage.vue | 4 +- src/pages/Item/ItemList.vue | 55 ++++++++++++++++----- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/pages/Item/Card/ItemDescriptorImage.vue b/src/pages/Item/Card/ItemDescriptorImage.vue index b035a630a..3cd51758b 100644 --- a/src/pages/Item/Card/ItemDescriptorImage.vue +++ b/src/pages/Item/Card/ItemDescriptorImage.vue @@ -134,10 +134,10 @@ es: Regularize stock: Regularizar stock All it's properties will be copied: Todas sus propiedades serán copiadas Do you want to clone this item?: ¿Desea clonar este artículo? - warehouseText: Calculated on the warehouse of { warehouseName } + warehouseText: Calculado sobre el almacén de { warehouseName } en: - warehouseText: Calculado sobre el almacén de { warehouseName } + warehouseText: Calculated on the warehouse of { warehouseName } </i18n> <style lang="scss" scoped> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 6c7b7eaed..92c9d188b 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -11,6 +11,9 @@ import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import ItemSummary from '../Item/Card/ItemSummary.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue'; +import VnConfirm from 'src/components/ui/VnConfirm.vue'; +import { useQuasar } from 'quasar'; const entityId = computed(() => route.params.id); const { viewSummary } = useSummaryDialog(); @@ -18,6 +21,7 @@ const router = useRouter(); const { t } = useI18n(); const tableRef = ref(); const route = useRoute(); +const quasar = useQuasar(); const itemFilter = { include: [ @@ -53,10 +57,12 @@ const columns = computed(() => [ id: row?.id, zoomResolution: '1600x900', zoom: true, + class: 'rounded', }; }, }, columnFilter: false, + cardVisible: true, }, { label: t('item.list.id'), @@ -66,6 +72,7 @@ const columns = computed(() => [ chip: { condition: () => true, }, + cardVisible: true, }, { label: t('item.list.grouping'), @@ -93,6 +100,7 @@ const columns = computed(() => [ columnFilter: { name: 'search', }, + cardVisible: true, }, { label: t('item.list.stems'), @@ -102,6 +110,7 @@ const columns = computed(() => [ component: 'number', inWhere: true, }, + cardVisible: true, }, { label: t('item.list.size'), @@ -111,6 +120,7 @@ const columns = computed(() => [ component: 'number', inWhere: true, }, + cardVisible: true, }, { label: t('item.list.typeName'), @@ -172,6 +182,7 @@ const columns = computed(() => [ component: null, }, create: true, + cardVisible: true, }, { label: t('item.list.origin'), @@ -197,6 +208,7 @@ const columns = computed(() => [ component: null, }, create: true, + cardVisible: true, }, { label: t('item.list.userName'), @@ -270,13 +282,14 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Clone item'), + title: t('globals.clone'), + icon: 'vn:clone', - action: cloneItem, + action: openCloneDialog, isPrimary: true, }, { - title: t('view Summary'), + title: t('components.smartCard.viewSummary'), icon: 'preview', action: (row) => viewSummary(row.id, ItemSummary), isPrimary: true, @@ -285,21 +298,34 @@ const columns = computed(() => [ }, ]); -const cloneItem = async (itemFk) => { +const cloneItem = async () => { try { - const { data } = await axios.post(`Items/${itemFk.id}/clone`); - if (!data) return; + const { data } = await axios.post(`Items/${entityId.value}/clone`); router.push({ name: 'ItemTags', params: { id: data.id } }); } catch (err) { - console.error('Error cloning item', err); + console.error('Error cloning item'); } }; + +const openCloneDialog = async () => { + quasar + .dialog({ + component: VnConfirm, + componentProps: { + title: t('All its properties will be copied'), + message: t('Do you want to clone this item?'), + }, + }) + .onOk(async () => { + await cloneItem(); + }); +}; </script> <template> <VnSearchbar data-key="ItemList" - :label="t('Search Item')" + :label="t('item.searchbar.label')" :info="t('You can search by id')" /> <VnTable @@ -309,19 +335,25 @@ const cloneItem = async (itemFk) => { url-create="Items" :create="{ urlCreate: 'Items', - title: 'Create Item', + title: t('Create Item'), onDataSaved: () => tableRef.redirect(), formInitialData: { editorFk: entityId, }, }" - order="id ASC" + :order="['isActive DESC', 'name', 'id']" :columns="columns" auto-load redirect="Item" :is-editable="false" :filer="itemFilter" > + <template #column-id="{ row }"> + <span class="link" @click.stop> + {{ row.id }} + <ItemDescriptorProxy :id="row.id" /> + </span> + </template> <template #column-userName="{ row }"> <span class="link" @click.stop> {{ row.userName }} @@ -349,7 +381,8 @@ const cloneItem = async (itemFk) => { <i18n> es: New item: Nuevo artículo - All it's properties will be copied: Todas sus propiedades serán copiadas + All its properties will be copied: Todas sus propiedades serán copiadas Do you want to clone this item?: ¿Desea clonar este artículo? Preview: Vista previa + Regularize stock: Regularizar stock </i18n> From 1346a2e7fd52f8811ac0b2efbd0c03676becca1f Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Tue, 22 Oct 2024 12:05:44 +0200 Subject: [PATCH 049/150] fix: tag chips --- src/pages/Order/Card/OrderCatalogFilter.vue | 113 +++++++++----------- 1 file changed, 50 insertions(+), 63 deletions(-) diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 850212a62..0fe8c4d64 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -6,8 +6,8 @@ import axios from 'axios'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue'; -// import VnInput from 'src/components/common/VnInput.vue'; import getParamWhere from 'src/filters/getParamWhere'; +import VnFilterPanelChip from 'src/components/ui/VnFilterPanelChip.vue'; const { t } = useI18n(); @@ -49,6 +49,7 @@ const orderWaySelected = ref('ASC'); const routeQuery = JSON.parse(route?.query.params ?? '{}'); const paramsSearch = ref({}); +const tagData = ref([]); onMounted(() => { paramsSearch.value = JSON.parse(routeQuery.filter ?? '{}')?.where ?? {}; @@ -120,32 +121,6 @@ const selectedType = computed(() => { return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value); }); -// const applyTagFilter = (params, search) => { - -// if (params.tagGroups) params.tagGroups = null; -// if (!tagValues.value?.length) { -// params.tagGroups = null; -// search(); -// return; -// } -// if (!params.tagGroups) { -// params.tagGroups = []; -// } -// params.tagGroups.push( -// JSON.stringify({ -// values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), -// tagSelection: { -// ...selectedTag.value, -// orgShowField: selectedTag?.value?.name, -// }, -// tagFk: selectedTag?.value?.tagFk, -// }) -// ); -// search(); -// selectedTag.value = null; -// tagValues.value = [{}]; -// }; - const applyTagFilter = (params, search) => { if (!tagValues.value?.length) { params.tagGroups = null; @@ -153,27 +128,46 @@ const applyTagFilter = (params, search) => { return; } - if (!params.tagGroups) { + if (!params.tagGroups || typeof params.tagGroups === 'string') { params.tagGroups = []; - } else if (typeof params.tagGroups === 'string') { - params.tagGroups = [params.tagGroups]; } - const newTagGroup = JSON.stringify({ - values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), - tagSelection: { - ...selectedTag.value, - orgShowField: selectedTag?.value?.name, - }, - tagFk: selectedTag?.value?.tagFk, - }); - params.tagGroups.push(newTagGroup); + params.tagGroups.push( + JSON.stringify({ + values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), + tagSelection: { + ...selectedTag.value, + orgShowField: selectedTag?.value?.name, + }, + tagFk: selectedTag?.value?.tagFk, + }) + ); + tagData.value.push(JSON.parse(params.tagGroups[params.tagGroups.length - 1])); search(); selectedTag.value = null; tagValues.value = [{}]; }; +const removeTagChip = (selection, params, search) => { + if (typeof params.tagGroups === 'string') { + try { + params.tagGroups = JSON.parse(params.tagGroups); + } catch (error) { + console.error('Error parsing tagGroups:', error); + params.tagGroups = []; + } + } + console.log('params.tagGroups: ', params.tagGroups); + console.log('selection: ', selection); + + if (Array.isArray(params.tagGroups)) { + params.tagGroups = params.tagGroups.filter((value) => value !== selection); + } + + search(); +}; + const setCategoryList = (data) => { categoryList.value = (data || []) .filter((category) => category.display) @@ -198,22 +192,6 @@ function addOrder(value, field, params) { params.orderBy = JSON.stringify(orderBy); vnFilterPanelRef.value.search(); } - -const data = ref([]); - -function getTagData(value) { - if (Array.isArray(value) && value.length >= 1) { - const parsedTags = value.map((obj) => JSON.parse(obj)); - - if (parsedTags.length === 1) { - data.value = parsedTags[0]; - } else { - data.value = parsedTags; - } - } else { - data.value = JSON.parse(value); - } -} </script> <template> @@ -224,29 +202,37 @@ function getTagData(value) { v-model="paramsSearch" :redirect="false" :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']" + :custom-tags="['tagGroups']" :unremovable-params="['orderFk', 'orderBy']" :disable-submit-event="true" > <template #tags="{ tag, formatFn }"> - {{ console.log('tag.label: ', tag.label) }} <strong v-if="tag.label === 'categoryFk'"> {{ t(selectedCategory?.name || '') }} </strong> <strong v-else-if="tag.label === 'typeFk'"> {{ t(selectedType?.name || '') }} </strong> - <div v-else-if="tag.label === 'tagGroups'" class="q-gutter-x-xs"> - {{ getTagData(tag.value) }} - <strong> {{ data.tagSelection?.name }}: </strong> - <span> - {{ console.log('data', data.values) }} - </span> - </div> <div v-else class="q-gutter-x-xs"> <strong>{{ t(`params.${tag.label}`) }}: </strong> <span>{{ formatFn(tag.value) }}</span> </div> </template> + <template #customTags="{ tags: customTags, params, searchFn }"> + <template v-for="tag in customTags" :key="tag.label"> + <template v-if="tag.label === 'tagGroups'"> + <VnFilterPanelChip + v-for="chip in tagData" + :key="chip" + removable + @remove="removeTagChip(chip, params, searchFn)" + > + <strong>{{ chip.tagSelection?.name }}: </strong> + <span>{{ chip.values[0].value }}</span> + </VnFilterPanelChip> + </template> + </template> + </template> <template #body="{ params, searchFn }"> <QItem class="category-filter q-mt-md"> <div @@ -410,6 +396,7 @@ function getTagData(value) { icon="add_circle" shortcut="+" flat + size="md" class="filter-icon" @click="tagValues.push({})" /> From d6b8d41b6c4cf5144018754d8bc7cdee1e73f5ba Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 22 Oct 2024 12:09:19 +0200 Subject: [PATCH 050/150] fix: refs #7283 #7283 ItemSummary bugs --- src/pages/Item/Card/ItemSummary.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/Item/Card/ItemSummary.vue b/src/pages/Item/Card/ItemSummary.vue index 7b6015c30..071203038 100644 --- a/src/pages/Item/Card/ItemSummary.vue +++ b/src/pages/Item/Card/ItemSummary.vue @@ -135,7 +135,7 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`; <VnLv v-for="(tax, index) in item.taxes" :key="index" - :label="tax.country.country" + :label="tax.country.name" :value="tax.taxClass.description" /> </QCard> @@ -155,7 +155,8 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`; :url="getUrl(entityId, 'barcode')" :text="t('item.summary.barcode')" /> - <p + <div + class="text-bold" v-for="(barcode, index) in item.itemBarcode" :key="index" v-text="barcode.code" From fff3310658db3eb3a80d8a259e94ac57a02a3d48 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 22 Oct 2024 12:09:32 +0200 Subject: [PATCH 051/150] fix: refs #7283 tooltips !Item --- src/pages/Account/AccountList.vue | 2 +- src/pages/Account/Role/AccountRoles.vue | 2 +- src/pages/Route/RouteAutonomous.vue | 2 +- src/pages/Route/RouteExtendedList.vue | 2 +- src/pages/Ticket/TicketList.vue | 2 +- src/pages/Zone/ZoneList.vue | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue index 72c445fa9..9e7f1b10a 100644 --- a/src/pages/Account/AccountList.vue +++ b/src/pages/Account/AccountList.vue @@ -74,7 +74,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('View Summary'), + title: t('components.smartCard.viewSummary'), icon: 'preview', action: (row) => viewSummary(row.id, AccountSummary), isPrimary: true, diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue index ea175d913..5398485e3 100644 --- a/src/pages/Account/Role/AccountRoles.vue +++ b/src/pages/Account/Role/AccountRoles.vue @@ -54,7 +54,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('View Summary'), + title: t('components.smartCard.viewSummary'), icon: 'preview', action: (row) => viewSummary(row.id, RoleSummary), isPrimary: true, diff --git a/src/pages/Route/RouteAutonomous.vue b/src/pages/Route/RouteAutonomous.vue index 5ad349942..4a691dbef 100644 --- a/src/pages/Route/RouteAutonomous.vue +++ b/src/pages/Route/RouteAutonomous.vue @@ -126,7 +126,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Preview'), + title: t('components.smartCard.viewSummary'), icon: 'preview', isPrimary: true, action: (row) => viewSummary(row?.routeFk, RouteSummary), diff --git a/src/pages/Route/RouteExtendedList.vue b/src/pages/Route/RouteExtendedList.vue index 51da4ec12..dbf646935 100644 --- a/src/pages/Route/RouteExtendedList.vue +++ b/src/pages/Route/RouteExtendedList.vue @@ -204,7 +204,7 @@ const columns = computed(() => [ isPrimary: true, }, { - title: t('route.components.smartCard.viewSummary'), + title: t('components.smartCard.viewSummary'), icon: 'preview', action: (row) => viewSummary(row?.id, RouteSummary), isPrimary: true, diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index ad97e75c1..272d3a666 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -202,7 +202,7 @@ const columns = computed(() => [ action: (row) => redirectToLines(row.id), }, { - title: t('ticketList.summary'), + title: t('components.smartCard.viewSummary'), icon: 'preview', isPrimary: true, action: (row) => viewSummary(row.id, TicketSummary), diff --git a/src/pages/Zone/ZoneList.vue b/src/pages/Zone/ZoneList.vue index d160ea6b5..89f2dd42c 100644 --- a/src/pages/Zone/ZoneList.vue +++ b/src/pages/Zone/ZoneList.vue @@ -103,7 +103,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('list.zoneSummary'), + title: t('components.smartCard.viewSummary'), icon: 'preview', action: (row) => viewSummary(row.id, ZoneSummary), isPrimary: true, From 49c0d64c07e27e2c469499d85dacba8a611184e3 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 22 Oct 2024 12:13:43 +0200 Subject: [PATCH 052/150] fix: refs #7283 #7283 ItemSummary bugs --- src/pages/Item/Card/ItemSummary.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Item/Card/ItemSummary.vue b/src/pages/Item/Card/ItemSummary.vue index 071203038..6ad09ac64 100644 --- a/src/pages/Item/Card/ItemSummary.vue +++ b/src/pages/Item/Card/ItemSummary.vue @@ -119,7 +119,7 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`; <VnLv v-for="(tag, index) in tags" :key="index" - :label="`${tag.priority} ${tag.tag.name}`" + :label="`${tag.priority} ${tag.tag.name}:`" :value="tag.value" /> </QCard> From 9db1c4f721a3a0d5f3f3a103060a65b78bca4fa4 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 12:29:33 +0200 Subject: [PATCH 053/150] fix: refs #8039 bad tests --- src/components/FormModel.vue | 3 --- test/cypress/integration/outLogin/logout.spec.js | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue index 05f947cf3..9ac2d38a5 100644 --- a/src/components/FormModel.vue +++ b/src/components/FormModel.vue @@ -217,9 +217,6 @@ async function save() { updateAndEmit('onDataSaved', formData.value, response?.data); if ($props.reload) await arrayData.fetch({}); hasChanges.value = false; - } catch (err) { - console.error(err); - notify('errors.writeRequest', 'negative'); } finally { isLoading.value = false; } diff --git a/test/cypress/integration/outLogin/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js index 423189908..8d4e90aac 100644 --- a/test/cypress/integration/outLogin/logout.spec.js +++ b/test/cypress/integration/outLogin/logout.spec.js @@ -13,7 +13,7 @@ describe('Logout', () => { }); describe('not user', () => { beforeEach(() => { - cy.intercept('GET', '**/VnUsers/acl', { + cy.intercept('GET', '**DefaultViewConfigs**', { statusCode: 401, body: { error: { @@ -24,10 +24,11 @@ describe('Logout', () => { }, }, statusMessage: 'AUTHORIZATION_REQUIRED', - }).as('someRoute'); + }); }); + it('when token not exists', () => { - cy.reload(); + cy.get('.q-list > [href="#/item"]').click(); cy.get('.q-notification__message').should( 'have.text', 'Authorization Required' From d25cdbf34e808838eec6cddbc0f3347af84c60f0 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 22 Oct 2024 12:43:58 +0200 Subject: [PATCH 054/150] test: refs #6943 #6943 use command correctly --- test/cypress/integration/client/clientList.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index 909bd3933..1b41e55cd 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -50,7 +50,7 @@ describe('Client list', () => { cy.clickButtonsDescriptor(2); cy.waitForElement('#formModel'); cy.waitForElement('.q-form'); - cy.checkValueForm(1, search); + cy.checkValueSelectForm(1, search); }); it.skip('Client founded create order', () => { const search = 'Jessica Jones'; From a732ec05fbe6ba834d46c8eaf3d348c9dbcc0b01 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 13:21:16 +0200 Subject: [PATCH 055/150] test: refs #8039 add hasNotify and, refactor: agencyWorkCenter test --- src/components/FormModelPopup.vue | 2 ++ .../route/agency/agencyWorkCenter.spec.js | 33 +++++++------------ test/cypress/support/commands.js | 11 +++++++ 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/components/FormModelPopup.vue b/src/components/FormModelPopup.vue index 118c8f5f4..d91f07535 100644 --- a/src/components/FormModelPopup.vue +++ b/src/components/FormModelPopup.vue @@ -61,6 +61,7 @@ defineExpose({ :loading="isLoading" @click="emit('onDataCanceled')" v-close-popup + data-cy="FormModelPopup_cancel" /> <QBtn :label="t('globals.save')" @@ -70,6 +71,7 @@ defineExpose({ class="q-ml-sm" :disabled="isLoading" :loading="isLoading" + data-cy="FormModelPopup_save" /> </div> </template> diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js index 353c5805b..6a3cab664 100644 --- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js +++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js @@ -7,31 +7,20 @@ describe('AgencyWorkCenter', () => { const createButton = '.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon'; const workCenterCombobox = 'input[role="combobox"]'; - it('assign workCenter', () => { + it('check workCenter crud', () => { + // create cy.get(createButton).click(); cy.get(workCenterCombobox).type('workCenterOne{enter}'); - cy.get('.q-notification__message').should('have.text', 'Data created'); - }); + cy.hasNotify('Data created'); - it('delete workCenter', () => { + // expect error when duplicate + cy.get(createButton).click(); + cy.get('[data-cy="FormModelPopup_save"]').click(); + cy.hasNotify('This workCenter is already assigned to this agency'); + cy.get('[data-cy="FormModelPopup_cancel"]').click(); + + // delete cy.get('.q-item__section--side > .q-btn > .q-btn__content > .q-icon').click(); - cy.get('.q-notification__message').should( - 'have.text', - 'WorkCenter removed successfully' - ); - }); - - it('error on duplicate workCenter', () => { - cy.get(createButton).click(); - cy.get(workCenterCombobox).type('workCenterOne{enter}'); - cy.get('.q-notification__message').should('have.text', 'Data created'); - cy.get(createButton).click(); - cy.get( - '.vn-row > .q-field > .q-field__inner > .q-field__control > .q-field__control-container' - ).type('workCenterOne{enter}'); - - cy.get( - ':nth-child(2) > .q-notification__wrapper > .q-notification__content > .q-notification__message' - ).should('have.text', 'This workCenter is already assigned to this agency'); + cy.hasNotify('WorkCenter removed successfully'); }); }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 83f45b721..c7b36cd3a 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -254,3 +254,14 @@ Cypress.Commands.add('openUserPanel', () => { '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image' ).click(); }); + +Cypress.Commands.add('hasNotify', (text) => { + //last + cy.get('.q-notification') + .should('be.visible') + .last() + .then(($lastNotification) => { + if (!Cypress.$($lastNotification).text().includes(text)) + throw new Error(`Notification not found: "${text}"`); + }); +}); From 2d81cffb3329bb874f32e41b3fe38c2eef6eb55e Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 13:34:20 +0200 Subject: [PATCH 056/150] feat: refs #8039 show duplicate request in local --- src/boot/quasar.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/boot/quasar.js b/src/boot/quasar.js index bf5175ee6..7845719fe 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -4,6 +4,7 @@ import mainShortcutMixin from './mainShortcutMixin'; import keyShortcut from './keyShortcut'; import useNotify from 'src/composables/useNotify.js'; import { CanceledError } from 'axios'; + const { notify } = useNotify(); export default boot(({ app }) => { @@ -41,7 +42,11 @@ export default boot(({ app }) => { } console.error(error); - if (error instanceof CanceledError) return; + if (error instanceof CanceledError) { + const env = process.env.NODE_ENV; + if (env && env !== 'development') return; + message = 'Duplicate request'; + } notify(message ?? 'globals.error', 'negative', 'error'); }; From 0c9c01b6e9f2ea6fa3674301039eed2f6501e65d Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 13:53:01 +0200 Subject: [PATCH 057/150] test: refs #8039 fix ZoneWarehouse e2e --- src/pages/Zone/Card/ZoneWarehouses.vue | 16 ++++------------ .../worker/workerNotificationsManager.spec.js | 5 +---- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/pages/Zone/Card/ZoneWarehouses.vue b/src/pages/Zone/Card/ZoneWarehouses.vue index 6b2933224..98e446797 100644 --- a/src/pages/Zone/Card/ZoneWarehouses.vue +++ b/src/pages/Zone/Card/ZoneWarehouses.vue @@ -34,21 +34,13 @@ const columns = computed(() => [ ]); const deleteWarehouse = async (row) => { - try { - await axios.delete(`${urlPath.value}/${row.id}`); - fetchWarehouses(); - } catch (error) { - console.error(error); - } + await axios.delete(`${urlPath.value}/${row.id}`); + fetchWarehouses(); }; const createZoneWarehouse = async (ZoneWarehouseFormData) => { - try { - await axios.post(urlPath.value, ZoneWarehouseFormData); - fetchWarehouses(); - } catch (error) { - console.error(error); - } + await axios.post(urlPath.value, ZoneWarehouseFormData); + fetchWarehouses(); }; watch( diff --git a/test/cypress/integration/worker/workerNotificationsManager.spec.js b/test/cypress/integration/worker/workerNotificationsManager.spec.js index ac452c4ff..367287a5a 100644 --- a/test/cypress/integration/worker/workerNotificationsManager.spec.js +++ b/test/cypress/integration/worker/workerNotificationsManager.spec.js @@ -17,10 +17,7 @@ describe('WorkerNotificationsManager', () => { cy.login('developer'); cy.visit(`/#/worker/${salesPersonId}/notifications`); cy.get(firstAvailableNotification).click(); - cy.notificationHas( - '.q-notification__message', - 'The notification subscription of this worker cant be modified' - ); + cy.hasNotify('The notification subscription of this worker cant be modified'); }); it('should active a notification that is yours', () => { From cd00a3c67f0fe72a4437f3a2eae3e7eae5266ebf Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 13:55:35 +0200 Subject: [PATCH 058/150] test: refs #8039 fix WorkerNotification e2e --- src/pages/Worker/Card/WorkerNotificationsManager.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/Worker/Card/WorkerNotificationsManager.vue b/src/pages/Worker/Card/WorkerNotificationsManager.vue index 731e073cd..53571fb93 100644 --- a/src/pages/Worker/Card/WorkerNotificationsManager.vue +++ b/src/pages/Worker/Card/WorkerNotificationsManager.vue @@ -44,8 +44,9 @@ async function toggleNotification(notification) { `worker.notificationsManager.${notification.active ? '' : 'un'}subscribed` ), }); - } catch { + } catch (e) { notification.active = !notification.active; + throw e; } } From dd2dc86eea3d035aa6ee6845a1dd0047fb653db0 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 14:05:15 +0200 Subject: [PATCH 059/150] fix: refs #8039 o not handle unnecessary errors --- src/pages/Account/Alias/Card/AliasUsers.vue | 10 +- src/pages/Account/Card/AccountMailAlias.vue | 20 +-- src/pages/Account/Role/Card/SubRoles.vue | 26 +-- .../Card/BasicData/TicketBasicDataForm.vue | 28 +-- .../Card/BasicData/TicketBasicDataView.vue | 85 ++++----- src/pages/Ticket/Card/ExpeditionNewTicket.vue | 32 ++-- src/pages/Ticket/Card/TicketComponents.vue | 26 +-- src/pages/Ticket/Card/TicketExpedition.vue | 58 +++---- .../Ticket/Card/TicketSaleMoreActions.vue | 12 +- src/pages/Ticket/Card/TicketSaleTracking.vue | 164 +++++++----------- src/pages/Ticket/Card/TicketService.vue | 52 +++--- src/pages/Ticket/Card/TicketVolume.vue | 22 +-- src/pages/Ticket/TicketAdvanceFilter.vue | 24 ++- src/pages/Ticket/TicketFutureFilter.vue | 40 ++--- 14 files changed, 228 insertions(+), 371 deletions(-) diff --git a/src/pages/Account/Alias/Card/AliasUsers.vue b/src/pages/Account/Alias/Card/AliasUsers.vue index 4a9c449e4..4aad68f1a 100644 --- a/src/pages/Account/Alias/Card/AliasUsers.vue +++ b/src/pages/Account/Alias/Card/AliasUsers.vue @@ -46,13 +46,9 @@ const columns = computed(() => [ ]); const deleteAlias = async (row) => { - try { - await axios.delete(`${urlPath.value}/${row.id}`); - notify(t('User removed'), 'positive'); - fetchAliases(); - } catch (error) { - console.error(error); - } + await axios.delete(`${urlPath.value}/${row.id}`); + notify(t('User removed'), 'positive'); + fetchAliases(); }; watch( diff --git a/src/pages/Account/Card/AccountMailAlias.vue b/src/pages/Account/Card/AccountMailAlias.vue index 15d03c665..8d3bd3b67 100644 --- a/src/pages/Account/Card/AccountMailAlias.vue +++ b/src/pages/Account/Card/AccountMailAlias.vue @@ -61,23 +61,15 @@ const fetchAccountExistence = async () => { }; const deleteMailAlias = async (row) => { - try { - await axios.delete(`${urlPath}/${row.id}`); - fetchMailAliases(); - notify(t('Unsubscribed from alias!'), 'positive'); - } catch (error) { - console.error(error); - } + await axios.delete(`${urlPath}/${row.id}`); + fetchMailAliases(); + notify(t('Unsubscribed from alias!'), 'positive'); }; const createMailAlias = async (mailAliasFormData) => { - try { - await axios.post(urlPath, mailAliasFormData); - notify(t('Subscribed to alias!'), 'positive'); - fetchMailAliases(); - } catch (error) { - console.error(error); - } + await axios.post(urlPath, mailAliasFormData); + notify(t('Subscribed to alias!'), 'positive'); + fetchMailAliases(); }; const fetchMailAliases = async () => { diff --git a/src/pages/Account/Role/Card/SubRoles.vue b/src/pages/Account/Role/Card/SubRoles.vue index d17f96dd8..6cac94667 100644 --- a/src/pages/Account/Role/Card/SubRoles.vue +++ b/src/pages/Account/Role/Card/SubRoles.vue @@ -46,29 +46,15 @@ const columns = computed(() => [ ]); const deleteSubRole = async (row) => { - try { - await axios.delete(`${urlPath.value}/${row.id}`); - fetchSubRoles(); - notify( - t('Role removed. Changes will take a while to fully propagate.'), - 'positive' - ); - } catch (error) { - console.error(error); - } + await axios.delete(`${urlPath.value}/${row.id}`); + fetchSubRoles(); + notify(t('Role removed. Changes will take a while to fully propagate.'), 'positive'); }; const createSubRole = async (subRoleFormData) => { - try { - await axios.post(urlPath.value, subRoleFormData); - notify( - t('Role added! Changes will take a while to fully propagate.'), - 'positive' - ); - fetchSubRoles(); - } catch (error) { - console.error(error); - } + await axios.post(urlPath.value, subRoleFormData); + notify(t('Role added! Changes will take a while to fully propagate.'), 'positive'); + fetchSubRoles(); }; watch( diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue index f5ce8a0f3..f6c20c514 100644 --- a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue @@ -112,32 +112,20 @@ const getShipped = async (params) => { }; const onChangeZone = async (zoneId) => { - try { - formData.value.agencyModeFk = null; - const { data } = await axios.get(`Zones/${zoneId}`); - formData.value.agencyModeFk = data.agencyModeFk; - } catch (error) { - console.error(error); - } + formData.value.agencyModeFk = null; + const { data } = await axios.get(`Zones/${zoneId}`); + formData.value.agencyModeFk = data.agencyModeFk; }; const onChangeAddress = async (addressId) => { - try { - formData.value.nickname = null; - const { data } = await axios.get(`Addresses/${addressId}`); - formData.value.nickname = data.nickname; - } catch (error) { - console.error(error); - } + formData.value.nickname = null; + const { data } = await axios.get(`Addresses/${addressId}`); + formData.value.nickname = data.nickname; }; const getClientDefaultAddress = async (clientId) => { - try { - const { data } = await axios.get(`Clients/${clientId}`); - if (data) addressId.value = data.defaultAddressFk; - } catch (error) { - console.error(error); - } + const { data } = await axios.get(`Clients/${clientId}`); + if (data) addressId.value = data.defaultAddressFk; }; const clientAddressesList = async (value) => { diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue index 92640f898..fb7881403 100644 --- a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue @@ -70,60 +70,51 @@ const isFormInvalid = () => { }; const getPriceDifference = async () => { - try { - const params = { - landed: formData.value.landed, - addressId: formData.value.addressFk, - agencyModeId: formData.value.agencyModeFk, - zoneId: formData.value.zoneFk, - warehouseId: formData.value.warehouseFk, - shipped: formData.value.shipped, - }; - const { data } = await axios.post( - `tickets/${formData.value.id}/priceDifference`, - params - ); - formData.value.sale = data; - } catch (error) { - console.error(error); - } + const params = { + landed: formData.value.landed, + addressId: formData.value.addressFk, + agencyModeId: formData.value.agencyModeFk, + zoneId: formData.value.zoneFk, + warehouseId: formData.value.warehouseFk, + shipped: formData.value.shipped, + }; + const { data } = await axios.post( + `tickets/${formData.value.id}/priceDifference`, + params + ); + formData.value.sale = data; }; const submit = async () => { - try { - if (!formData.value.option) - return notify(t('basicData.chooseAnOption'), 'negative'); + if (!formData.value.option) return notify(t('basicData.chooseAnOption'), 'negative'); - const params = { - clientFk: formData.value.clientFk, - nickname: formData.value.nickname, - agencyModeFk: formData.value.agencyModeFk, - addressFk: formData.value.addressFk, - zoneFk: formData.value.zoneFk, - warehouseFk: formData.value.warehouseFk, - companyFk: formData.value.companyFk, - shipped: formData.value.shipped, - landed: formData.value.landed, - isDeleted: formData.value.isDeleted, - option: formData.value.option, - isWithoutNegatives: formData.value.withoutNegatives, - withWarningAccept: formData.value.withWarningAccept, - keepPrice: false, - }; + const params = { + clientFk: formData.value.clientFk, + nickname: formData.value.nickname, + agencyModeFk: formData.value.agencyModeFk, + addressFk: formData.value.addressFk, + zoneFk: formData.value.zoneFk, + warehouseFk: formData.value.warehouseFk, + companyFk: formData.value.companyFk, + shipped: formData.value.shipped, + landed: formData.value.landed, + isDeleted: formData.value.isDeleted, + option: formData.value.option, + isWithoutNegatives: formData.value.withoutNegatives, + withWarningAccept: formData.value.withWarningAccept, + keepPrice: false, + }; - const { data } = await axios.post( - `tickets/${formData.value.id}/componentUpdate`, - params - ); + const { data } = await axios.post( + `tickets/${formData.value.id}/componentUpdate`, + params + ); - if (!data) return; + if (!data) return; - const ticketToMove = data.id; - notify(t('basicData.unroutedTicket'), 'positive'); - router.push({ name: 'TicketSummary', params: { id: ticketToMove } }); - } catch (error) { - console.error(error); - } + const ticketToMove = data.id; + notify(t('basicData.unroutedTicket'), 'positive'); + router.push({ name: 'TicketSummary', params: { id: ticketToMove } }); }; const submitWithNegatives = async () => { diff --git a/src/pages/Ticket/Card/ExpeditionNewTicket.vue b/src/pages/Ticket/Card/ExpeditionNewTicket.vue index 9183ae405..c288f6cc2 100644 --- a/src/pages/Ticket/Card/ExpeditionNewTicket.vue +++ b/src/pages/Ticket/Card/ExpeditionNewTicket.vue @@ -34,26 +34,20 @@ const newTicketFormData = reactive({}); const date = new Date(); const createTicket = async () => { - try { - const expeditionIds = $props.selectedExpeditions.map( - (expedition) => expedition.id - ); - const params = { - clientId: $props.ticket.clientFk, - landed: newTicketFormData.landed, - warehouseId: $props.ticket.warehouseFk, - addressId: $props.ticket.addressFk, - agencyModeId: $props.ticket.agencyModeFk, - routeId: newTicketFormData.routeFk, - expeditionIds: expeditionIds, - }; + const expeditionIds = $props.selectedExpeditions.map((expedition) => expedition.id); + const params = { + clientId: $props.ticket.clientFk, + landed: newTicketFormData.landed, + warehouseId: $props.ticket.warehouseFk, + addressId: $props.ticket.addressFk, + agencyModeId: $props.ticket.agencyModeFk, + routeId: newTicketFormData.routeFk, + expeditionIds: expeditionIds, + }; - const { data } = await axios.post('Expeditions/moveExpeditions', params); - notify(t('globals.dataSaved'), 'positive'); - router.push({ name: 'TicketSummary', params: { id: data.id } }); - } catch (error) { - console.error(error); - } + const { data } = await axios.post('Expeditions/moveExpeditions', params); + notify(t('globals.dataSaved'), 'positive'); + router.push({ name: 'TicketSummary', params: { id: data.id } }); }; </script> diff --git a/src/pages/Ticket/Card/TicketComponents.vue b/src/pages/Ticket/Card/TicketComponents.vue index 0bccdaacd..b5b3c430c 100644 --- a/src/pages/Ticket/Card/TicketComponents.vue +++ b/src/pages/Ticket/Card/TicketComponents.vue @@ -150,31 +150,19 @@ const getTotal = computed(() => { }); const getComponentsSum = async () => { - try { - const { data } = await axios.get(`Tickets/${route.params.id}/getComponentsSum`); - componentsList.value = data; - } catch (error) { - console.error(error); - } + const { data } = await axios.get(`Tickets/${route.params.id}/getComponentsSum`); + componentsList.value = data; }; const getTheoricalCost = async () => { - try { - const { data } = await axios.get(`Tickets/${route.params.id}/freightCost`); - theoricalCost.value = data; - } catch (error) { - console.error(error); - } + const { data } = await axios.get(`Tickets/${route.params.id}/freightCost`); + theoricalCost.value = data; }; const getTicketVolume = async () => { - try { - if (!ticketData.value) return; - const { data } = await axios.get(`Tickets/${ticketData.value.id}/getVolume`); - ticketVolume.value = data[0].volume; - } catch (error) { - console.error(error); - } + if (!ticketData.value) return; + const { data } = await axios.get(`Tickets/${ticketData.value.id}/getVolume`); + ticketVolume.value = data[0].volume; }; onMounted(() => { diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue index 4becb3db3..b4a2ca732 100644 --- a/src/pages/Ticket/Card/TicketExpedition.vue +++ b/src/pages/Ticket/Card/TicketExpedition.vue @@ -187,18 +187,12 @@ const showNewTicketDialog = (withRoute = false) => { }; const deleteExpedition = async () => { - try { - const expeditionIds = selectedExpeditions.value.map( - (expedition) => expedition.id - ); - const params = { expeditionIds }; - await axios.post('Expeditions/deleteExpeditions', params); - await refetchExpeditions(); - selectedExpeditions.value = []; - notify(t('expedition.expeditionRemoved'), 'positive'); - } catch (error) { - console.error(error); - } + const expeditionIds = selectedExpeditions.value.map((expedition) => expedition.id); + const params = { expeditionIds }; + await axios.post('Expeditions/deleteExpeditions', params); + await refetchExpeditions(); + selectedExpeditions.value = []; + notify(t('expedition.expeditionRemoved'), 'positive'); }; const showLog = async (expedition) => { @@ -207,29 +201,25 @@ const showLog = async (expedition) => { }; const getExpeditionState = async (expedition) => { - try { - const filter = { - where: { expeditionFk: expedition.id }, - order: ['created DESC'], + const filter = { + where: { expeditionFk: expedition.id }, + order: ['created DESC'], + }; + + const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, { + params: { filter: JSON.stringify(filter) }, + }); + const { data: scannedStates } = await axios.get(`ExpeditionStates`, { + params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] }, + }); + + expeditionsLogsData.value = expeditionStates.map((state) => { + const scannedState = scannedStates.find((s) => s.id === state.id); + return { + ...state, + isScanned: scannedState ? scannedState.isScanned : false, }; - - const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, { - params: { filter: JSON.stringify(filter) }, - }); - const { data: scannedStates } = await axios.get(`ExpeditionStates`, { - params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] }, - }); - - expeditionsLogsData.value = expeditionStates.map((state) => { - const scannedState = scannedStates.find((s) => s.id === state.id); - return { - ...state, - isScanned: scannedState ? scannedState.isScanned : false, - }; - }); - } catch (error) { - console.error(error); - } + }); }; onMounted(async () => { diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 2ec519d2d..588f78a7b 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -165,14 +165,10 @@ const createRefund = async (withWarehouse) => { negative: true, }; - try { - const { data } = await axios.post('Tickets/cloneAll', params); - const [refundTicket] = data; - notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive'); - push({ name: 'TicketSale', params: { id: refundTicket.id } }); - } catch (error) { - console.error(error); - } + const { data } = await axios.post('Tickets/cloneAll', params); + const [refundTicket] = data; + notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive'); + push({ name: 'TicketSale', params: { id: refundTicket.id } }); }; </script> diff --git a/src/pages/Ticket/Card/TicketSaleTracking.vue b/src/pages/Ticket/Card/TicketSaleTracking.vue index e7830bf37..1083393c4 100644 --- a/src/pages/Ticket/Card/TicketSaleTracking.vue +++ b/src/pages/Ticket/Card/TicketSaleTracking.vue @@ -150,18 +150,14 @@ const shelvingsTableColumns = computed(() => [ ]); const getSaleTrackings = async (sale) => { - try { - const filter = { - where: { saleFk: sale.saleFk }, - order: ['itemFk DESC'], - }; - const { data } = await axios.get(`SaleTrackings/listSaleTracking`, { - params: { filter: JSON.stringify(filter) }, - }); - saleTrackings.value = data; - } catch (error) { - console.error(error); - } + const filter = { + where: { saleFk: sale.saleFk }, + order: ['itemFk DESC'], + }; + const { data } = await axios.get(`SaleTrackings/listSaleTracking`, { + params: { filter: JSON.stringify(filter) }, + }); + saleTrackings.value = data; }; const showLog = async (sale) => { @@ -170,17 +166,13 @@ const showLog = async (sale) => { }; const getItemShelvingSales = async (sale) => { - try { - const filter = { - where: { saleFk: sale.saleFk }, - }; - const { data } = await axios.get(`ItemShelvingSales/filter`, { - params: { filter: JSON.stringify(filter) }, - }); - itemShelvingsSales.value = data; - } catch (error) { - console.error(error); - } + const filter = { + where: { saleFk: sale.saleFk }, + }; + const { data } = await axios.get(`ItemShelvingSales/filter`, { + params: { filter: JSON.stringify(filter) }, + }); + itemShelvingsSales.value = data; }; const showShelving = async (sale) => { @@ -189,36 +181,28 @@ const showShelving = async (sale) => { }; const updateQuantity = async (sale) => { - try { - if (oldQuantity.value === sale.quantity) return; - const params = { - quantity: sale.quantity, - }; - await axios.patch(`ItemShelvingSales/${sale.id}`, params); - oldQuantity.value = null; - } catch (error) { - console.error(error); - } + if (oldQuantity.value === sale.quantity) return; + const params = { + quantity: sale.quantity, + }; + await axios.patch(`ItemShelvingSales/${sale.id}`, params); + oldQuantity.value = null; }; const updateParking = async (sale) => { - try { - const filter = { - fields: ['id'], - where: { - code: sale.shelvingFk, - }, - }; - const { data } = await axios.get(`Shelvings/findOne`, { - params: { filter: JSON.stringify(filter) }, - }); - const params = { - parkingFk: sale.parkingFk, - }; - await axios.patch(`Shelvings/${data.id}`, params); - } catch (error) { - console.error(error); - } + const filter = { + fields: ['id'], + where: { + code: sale.shelvingFk, + }, + }; + const { data } = await axios.get(`Shelvings/findOne`, { + params: { filter: JSON.stringify(filter) }, + }); + const params = { + parkingFk: sale.parkingFk, + }; + await axios.patch(`Shelvings/${data.id}`, params); }; const updateShelving = async (sale) => { @@ -241,61 +225,41 @@ const updateShelving = async (sale) => { }; const saleTrackingNew = async (sale, stateCode, isChecked) => { - try { - const params = { - saleFk: sale.saleFk, - isChecked, - quantity: sale.quantity, - stateCode, - }; - await axios.post(`SaleTrackings/new`, params); - notify(t('globals.dataSaved'), 'positive'); - } catch (error) { - console.error(error); - } + const params = { + saleFk: sale.saleFk, + isChecked, + quantity: sale.quantity, + stateCode, + }; + await axios.post(`SaleTrackings/new`, params); + notify(t('globals.dataSaved'), 'positive'); }; const saleTrackingDel = async ({ saleFk }, stateCode) => { - try { - const params = { - saleFk, - stateCodes: [stateCode], - }; - await axios.post(`SaleTrackings/delete`, params); - notify(t('globals.dataSaved'), 'positive'); - } catch (error) { - console.error(error); - } + const params = { + saleFk, + stateCodes: [stateCode], + }; + await axios.post(`SaleTrackings/delete`, params); + notify(t('globals.dataSaved'), 'positive'); }; const clickSaleGroupDetail = async (sale) => { - try { - if (!sale.saleGroupDetailFk) return; + if (!sale.saleGroupDetailFk) return; - await axios.delete(`SaleGroupDetails/${sale.saleGroupDetailFk}`); - sale.hasSaleGroupDetail = false; - notify(t('globals.dataSaved'), 'positive'); - } catch (error) { - console.error(error); - } + await axios.delete(`SaleGroupDetails/${sale.saleGroupDetailFk}`); + sale.hasSaleGroupDetail = false; + notify(t('globals.dataSaved'), 'positive'); }; const clickPreviousSelected = (sale) => { - try { - qCheckBoxController(sale, 'isPreviousSelected'); - if (!sale.isPreviousSelected) sale.isPrevious = false; - } catch (error) { - console.error(error); - } + qCheckBoxController(sale, 'isPreviousSelected'); + if (!sale.isPreviousSelected) sale.isPrevious = false; }; const clickPrevious = (sale) => { - try { - qCheckBoxController(sale, 'isPrevious'); - if (sale.isPrevious) sale.isPreviousSelected = true; - } catch (error) { - console.error(error); - } + qCheckBoxController(sale, 'isPrevious'); + if (sale.isPrevious) sale.isPreviousSelected = true; }; const qCheckBoxController = (sale, action) => { @@ -306,16 +270,12 @@ const qCheckBoxController = (sale, action) => { isPreviousSelected: 'PREVIOUS_PREPARATION', }; const stateCode = STATE_CODES[action]; - try { - if (!sale[action]) { - saleTrackingNew(sale, stateCode, true); - sale[action] = true; - } else { - saleTrackingDel(sale, stateCode); - sale[action] = false; - } - } catch (error) { - console.error(error); + if (!sale[action]) { + saleTrackingNew(sale, stateCode, true); + sale[action] = true; + } else { + saleTrackingDel(sale, stateCode); + sale[action] = false; } }; </script> diff --git a/src/pages/Ticket/Card/TicketService.vue b/src/pages/Ticket/Card/TicketService.vue index 45a870f7f..47c28a422 100644 --- a/src/pages/Ticket/Card/TicketService.vue +++ b/src/pages/Ticket/Card/TicketService.vue @@ -46,40 +46,32 @@ watch( onMounted(async () => await getDefaultTaxClass()); const createRefund = async () => { - try { - if (!selected.value.length) return; + if (!selected.value.length) return; - const params = { - servicesIds: selected.value.map((s) => +s.id), - withWarehouse: false, - negative: true, - }; - const { data } = await axios.post('Sales/clone', params); - const [refundTicket] = data; - notify( - t('service.createRefundSuccess', { - ticketId: refundTicket.id, - }), - 'positive' - ); - router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); - } catch (error) { - console.error(error); - } + const params = { + servicesIds: selected.value.map((s) => +s.id), + withWarehouse: false, + negative: true, + }; + const { data } = await axios.post('Sales/clone', params); + const [refundTicket] = data; + notify( + t('service.createRefundSuccess', { + ticketId: refundTicket.id, + }), + 'positive' + ); + router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); }; const getDefaultTaxClass = async () => { - try { - let filter = { - where: { code: 'G' }, - }; - const { data } = await axios.get('TaxClasses/findOne', { - params: { filter: JSON.stringify(filter) }, - }); - defaultTaxClass.value = data; - } catch (error) { - console.error(error); - } + let filter = { + where: { code: 'G' }, + }; + const { data } = await axios.get('TaxClasses/findOne', { + params: { filter: JSON.stringify(filter) }, + }); + defaultTaxClass.value = data; }; const columns = computed(() => [ diff --git a/src/pages/Ticket/Card/TicketVolume.vue b/src/pages/Ticket/Card/TicketVolume.vue index 2cf7ffc42..edfe489d9 100644 --- a/src/pages/Ticket/Card/TicketVolume.vue +++ b/src/pages/Ticket/Card/TicketVolume.vue @@ -75,22 +75,18 @@ const columns = computed(() => [ ]); const applyVolumes = async (salesData) => { - try { - if (!salesData.length) return; + if (!salesData.length) return; - sales.value = salesData; - const ticket = sales.value[0].ticketFk; - const { data } = await axios.get(`Tickets/${ticket}/getVolume`); - const volumes = new Map(data.saleVolume.map((volume) => [volume.saleFk, volume])); + sales.value = salesData; + const ticket = sales.value[0].ticketFk; + const { data } = await axios.get(`Tickets/${ticket}/getVolume`); + const volumes = new Map(data.saleVolume.map((volume) => [volume.saleFk, volume])); - sales.value.forEach((sale) => { - sale.saleVolume = volumes.get(sale.id); - }); + sales.value.forEach((sale) => { + sale.saleVolume = volumes.get(sale.id); + }); - packingTypeVolume.value = data.packingTypeVolume; - } catch (error) { - console.error(error); - } + packingTypeVolume.value = data.packingTypeVolume; }; onMounted(() => (stateStore.rightDrawer = true)); diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue index b25ebdea6..27cacd80a 100644 --- a/src/pages/Ticket/TicketAdvanceFilter.vue +++ b/src/pages/Ticket/TicketAdvanceFilter.vue @@ -27,20 +27,16 @@ const warehousesOptions = ref([]); const itemPackingTypes = 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 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, + })); }; onMounted(async () => await getItemPackingTypes()); diff --git a/src/pages/Ticket/TicketFutureFilter.vue b/src/pages/Ticket/TicketFutureFilter.vue index 6345f62b3..ffe967272 100644 --- a/src/pages/Ticket/TicketFutureFilter.vue +++ b/src/pages/Ticket/TicketFutureFilter.vue @@ -24,33 +24,25 @@ 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 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, + })); }; 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); - } + const { data } = await axios.get('AlertLevels'); + stateOptions.value = data.map((state) => ({ + id: state.id, + name: t(`futureTickets.${state.code}`), + code: state.code, + })); }; onMounted(async () => { From ee31bc8262d511bc659316d7046b5f9d0228034d Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 22 Oct 2024 14:07:27 +0200 Subject: [PATCH 060/150] chore: refs #8039 not required --- src/boot/axios.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/boot/axios.js b/src/boot/axios.js index b084b835d..aee38e887 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -3,7 +3,6 @@ import { useSession } from 'src/composables/useSession'; import { Router } from 'src/router'; import useNotify from 'src/composables/useNotify.js'; import { useStateQueryStore } from 'src/stores/useStateQueryStore'; -import { CanceledError } from 'axios'; const session = useSession(); const { notify } = useNotify(); From 4bde0dffa734f0588db077708cb5a098d40c9a97 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Wed, 23 Oct 2024 09:17:14 +0200 Subject: [PATCH 061/150] fix: fixed item packaging type field showing repetitive values --- .../components/CustomerSummaryTable.vue | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue index f6013dea9..ff1474fb9 100644 --- a/src/pages/Customer/components/CustomerSummaryTable.vue +++ b/src/pages/Customer/components/CustomerSummaryTable.vue @@ -101,7 +101,7 @@ const columns = computed(() => [ align: 'left', name: 'itemPackingTypeFk', label: t('ticketSale.packaging'), - format: (row) => getItemPackagingType(row), + format: (row) => getItemPackagingType(row.ticketSales), }, { align: 'right', @@ -151,13 +151,18 @@ const setShippedColor = (date) => { if (difference < 0) return 'success'; }; -const getItemPackagingType = (row) => { - const packagingType = row?.ticketSales - .map((sale) => sale.item?.itemPackingTypeFk || '-') - .filter((value) => value !== '-') - .join(', '); +const getItemPackagingType = (ticketSales) => { + if (!ticketSales || ticketSales.length === 0) return '-'; - return dashIfEmpty(packagingType); + const packagingTypes = ticketSales + .map((sale) => sale.item?.itemPackingTypeFk) + .filter((type) => type !== undefined && type !== null); + + const uniquePackagingTypes = [...new Set(packagingTypes)]; + + return dashIfEmpty( + uniquePackagingTypes.length > 0 ? uniquePackagingTypes.join(', ') : '-' + ); }; </script> From b40af0ce7bb73660464db2b2b945261920528ece Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 23 Oct 2024 09:49:48 +0200 Subject: [PATCH 062/150] fix: refs #7283 fix pr --- src/pages/Item/Card/ItemBasicData.vue | 8 ++- src/pages/Item/Card/ItemBotanical.vue | 7 --- src/pages/Item/Card/ItemDescriptor.vue | 13 ++++ src/pages/Item/Card/ItemTax.vue | 4 +- src/pages/Item/ItemRequest.vue | 34 ++++++++++- src/pages/Item/ItemTypeList.vue | 85 +++++++++----------------- 6 files changed, 84 insertions(+), 67 deletions(-) diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue index 6b6d89c6d..1b0342668 100644 --- a/src/pages/Item/Card/ItemBasicData.vue +++ b/src/pages/Item/Card/ItemBasicData.vue @@ -85,13 +85,19 @@ const onIntrastatCreated = (response, formData) => { <VnInput :label="t('item.basicData.reference')" v-model="data.comment" /> <VnInput :label="t('item.basicData.relevancy')" + type="number" v-model="data.relevancy" /> </VnRow> <VnRow class="row q-gutter-md q-mb-md"> - <VnInput :label="t('item.basicData.stems')" v-model="data.stems" /> + <VnInput + :label="t('item.basicData.stems')" + type="number" + v-model="data.stems" + /> <VnInput :label="t('item.basicData.multiplier')" + type="number" v-model="data.stemMultiplier" /> <VnSelectDialog diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue index 5f7f3a5e4..1aa6faed1 100644 --- a/src/pages/Item/Card/ItemBotanical.vue +++ b/src/pages/Item/Card/ItemBotanical.vue @@ -20,15 +20,8 @@ let itemBotanicalsForm = reactive({ itemFk: null }); const entityId = computed(() => { return route.params.id; }); -// onMounted(async () => { -// itemBotanicalsForm.itemFk = entityId.value; -// // itemBotanicals.value = await itemBotanicalsRef.value.fetch(); -// if (itemBotanicals.value.length > 0) -// Object.assign(itemBotanicalsForm, itemBotanicals.value[0]); -// }); async function handleItemBotanical(data) { itemBotanicalsForm = data; - // if (data.length > 0) Object.assign(itemBotanicalsForm, itemBotanicals.value[0]); } </script> <template> diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue index 09d6a8a4f..aa9795a74 100644 --- a/src/pages/Item/Card/ItemDescriptor.vue +++ b/src/pages/Item/Card/ItemDescriptor.vue @@ -189,6 +189,18 @@ const openCloneDialog = async () => { :value="entity.value7" /> </template> + <template #icons="{ entity }"> + <QCardActions v-if="entity" class="q-gutter-x-md"> + <QIcon + v-if="!entity.isActive" + name="vn:unavailable" + color="primary" + size="xs" + > + <QTooltip>{{ t('Inactive article') }}</QTooltip> + </QIcon> + </QCardActions> + </template> <template #actions="{}"> <QCardActions class="row justify-center"> <QBtn @@ -213,6 +225,7 @@ es: Regularize stock: Regularizar stock All its properties will be copied: Todas sus propiedades serán copiadas Do you want to clone this item?: ¿Desea clonar este artículo? + Inactive article: Artículo inactivo </i18n> <style lang="scss" scoped> diff --git a/src/pages/Item/Card/ItemTax.vue b/src/pages/Item/Card/ItemTax.vue index 489a2c7b2..9050db42e 100644 --- a/src/pages/Item/Card/ItemTax.vue +++ b/src/pages/Item/Card/ItemTax.vue @@ -22,7 +22,7 @@ const taxesFilter = { { relation: 'country', scope: { - fields: ['country'], + fields: ['name'], }, }, ], @@ -73,7 +73,7 @@ const submitTaxes = async (data) => { > <VnInput :label="t('tax.country')" - v-model="row.country.country" + v-model="row.country.name" disable /> <VnSelect diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 0eba6f9a4..ea265e706 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -5,7 +5,7 @@ import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.v import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import { useStateStore } from 'stores/useStateStore'; import { useArrayData } from 'composables/useArrayData'; -import { toCurrency } from 'filters/index'; +import { dashIfEmpty, toCurrency } from 'filters/index'; import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; import ItemRequestDenyForm from './ItemRequestDenyForm.vue'; @@ -134,6 +134,20 @@ const columns = computed(() => [ }, ]); +const getBadgeColor = (date) => { + const today = Date.vnNew(); + today.setHours(0, 0, 0, 0); + + const orderLanded = new Date(date); + orderLanded.setHours(0, 0, 0, 0); + + const difference = today - orderLanded; + + if (difference == 0) return 'warning'; + if (difference < 0) return 'success'; + if (difference > 0) return 'alert'; +}; + const changeQuantity = async (request) => { try { if (request.saleFk) { @@ -212,6 +226,24 @@ onMounted(async () => { auto-load :disable-option="{ card: true }" > + <template #column-ticketFk="{ row }"> + <span class="link"> + {{ row.ticketFk }} + <TicketDescriptorProxy :id="row.ticketFk" /> + </span> + </template> + <template #column-shipped="{ row }"> + <QTd> + <QBadge + :color="getBadgeColor(row.shipped)" + text-color="black" + class="q-pa-sm" + style="font-size: 14px" + > + {{ toDate(row.shipped) }} + </QBadge> + </QTd> + </template> <template #column-attenderName="{ row }"> <span class="link" @click.stop> {{ row.attenderName }} diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 5ebbec62b..9981a0d68 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -1,54 +1,14 @@ <script setup> import { useI18n } from 'vue-i18n'; -import { useRouter } from 'vue-router'; +import { useRoute } from 'vue-router'; import { ref, computed } from 'vue'; import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; -const router = useRouter(); +const route = useRoute(); const { t } = useI18n(); const tableRef = ref(); -const redirectToItemTypeSummary = (id) => { - router.push({ name: 'ItemTypeSummary', params: { id } }); -}; - -const redirectToCreateView = () => { - router.push({ name: 'ItemTypeCreate' }); -}; - -const exprBuilder = (param, value) => { - switch (param) { - case 'name': - return { - name: { like: `%${value}%` }, - }; - case 'code': - return { - code: { like: `%${value}%` }, - }; - case 'search': - if (value) { - if (!isNaN(value)) { - return { id: value }; - } else { - return { - or: [ - { - name: { - like: `%${value}%`, - }, - }, - { - code: { - like: `%${value}%`, - }, - }, - ], - }; - } - } - } -}; +const entityId = computed(() => route.params.id); const columns = computed(() => [ { @@ -75,20 +35,21 @@ const columns = computed(() => [ }, { align: 'left', - name: 'worker', + name: 'workerFk', label: t('worker'), create: true, component: 'select', attrs: { url: 'Workers', - fields: ['id', 'firstName'], + optionLabel: 'firstName', + optionValue: 'id', }, - cardVisible: true, + cardVisible: false, visible: false, }, { align: 'left', - name: 'ItemCategory', + name: 'categoryFk', label: t('ItemCategory'), create: true, component: 'select', @@ -96,7 +57,7 @@ const columns = computed(() => [ url: 'ItemCategories', fields: ['id', 'name'], }, - cardVisible: true, + cardVisible: false, visible: false, }, { @@ -109,7 +70,7 @@ const columns = computed(() => [ url: 'Temperatures', fields: ['id', 'name'], }, - cardVisible: true, + cardVisible: false, visible: false, }, ]); @@ -121,18 +82,13 @@ const columns = computed(() => [ ref="tableRef" data-key="ItemTypeList" :url="`ItemTypes`" - :url-create="`ItemTypes`" - save-url="ItemTypes/crud" - :filter="courseFilter" :create="{ urlCreate: 'ItemTypes', title: 'Create ItemTypes', onDataSaved: () => tableRef.reload(), - formInitialData: { - workerFk: entityId, - }, + formInitialData: {}, }" - order="id DESC" + order="code ASC" :columns="columns" auto-load :right-search="false" @@ -140,3 +96,20 @@ const columns = computed(() => [ :use-model="true" /> </template> + +<i18n> + es: + id: Id + code: Código + name: Nombre + worker: Encargado + ItemCategory: Categoría + Temperature: Temperatura + Create ItemTypes: Crear familia + en: + code: Code + name: Name + worker: Worker + ItemCategory: ItemCategory + Temperature: Temperature +</i18n> From d8b80cfa6d31087bee361d9b48a999b3f199e59a Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 23 Oct 2024 12:30:04 +0200 Subject: [PATCH 063/150] fix: refs #7283 fix pr --- src/pages/Item/Card/ItemBotanical.vue | 5 +---- src/pages/Item/Card/ItemDescriptor.vue | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue index 1aa6faed1..c4b561772 100644 --- a/src/pages/Item/Card/ItemBotanical.vue +++ b/src/pages/Item/Card/ItemBotanical.vue @@ -20,9 +20,6 @@ let itemBotanicalsForm = reactive({ itemFk: null }); const entityId = computed(() => { return route.params.id; }); -async function handleItemBotanical(data) { - itemBotanicalsForm = data; -} </script> <template> <FetchData @@ -41,7 +38,7 @@ async function handleItemBotanical(data) { :filter="{ where: { itemFk: entityId }, }" - @on-fetch="handleItemBotanical" + @on-fetch="(data) => (itemBotanicalsForm = data)" > <template #form="{ data }"> <VnRow> diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue index aa9795a74..635dd17c8 100644 --- a/src/pages/Item/Card/ItemDescriptor.vue +++ b/src/pages/Item/Card/ItemDescriptor.vue @@ -50,7 +50,6 @@ const entityId = computed(() => { }); const regularizeStockFormDialog = ref(null); -const salixUrl = ref(); const mounted = ref(); const arrayDataStock = useArrayData('descriptorStock', { @@ -58,7 +57,6 @@ const arrayDataStock = useArrayData('descriptorStock', { }); onMounted(async () => { - salixUrl.value = await getUrl('getVisibleAvailable'); await getItemConfigs(); mounted.value = true; }); From 37b3affdbfc2c406b72116ac89657218e38f7adc Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Wed, 23 Oct 2024 13:02:28 +0200 Subject: [PATCH 064/150] refactor: revert catalog changes --- src/components/ui/VnFilterPanel.vue | 12 +-- src/components/ui/VnSearchbar.vue | 2 - src/pages/Order/Card/OrderCatalog.vue | 5 +- src/pages/Order/Card/OrderCatalogFilter.vue | 108 ++++++++------------ 4 files changed, 50 insertions(+), 77 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 66e9df5f5..43d634ad9 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -185,9 +185,6 @@ async function remove(key) { } function formatValue(value) { - if (typeof value === 'object') { - return value; - } if (typeof value === 'boolean') return value ? t('Yes') : t('No'); if (isNaN(value) && !isNaN(Date.parse(value))) return toDate(value); @@ -196,13 +193,6 @@ function formatValue(value) { function sanitizer(params) { for (const [key, value] of Object.entries(params)) { - if (key == 'and') { - value.forEach((andValue) => { - params = { ...params, ...andValue }; - }); - delete params[key]; - } - if (value && typeof value === 'object') { const param = Object.values(value)[0]; if (typeof param == 'string') params[key] = param.replaceAll('%', ''); @@ -221,7 +211,7 @@ function sanitizer(params) { icon="search" @click="search()" ></QBtn> - <QForm @submit="search" id="filterPanelForm" @keyup.enter.prevent="search(true)"> + <QForm @submit="search" id="filterPanelForm" @keyup.enter="search()"> <QList dense> <QItem class="q-mt-xs"> <QItemSection top> diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index 569e0ed18..dc6d4751c 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -9,7 +9,6 @@ import { useStateStore } from 'src/stores/useStateStore'; const quasar = useQuasar(); const { t } = useI18n(); const state = useStateStore(); -const emit = defineEmits(['onSearch']); const props = defineProps({ dataKey: { @@ -119,7 +118,6 @@ async function search() { delete filter.params.search; } await arrayData.applyFilter(filter); - emit('onSearch', store.data); } </script> <template> diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index 110ea48af..f9bcca94c 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -86,8 +86,11 @@ function extractValueTags(items) { <div class="full-width"> <VnPaginate data-key="OrderCatalogList" + url="Orders/CatalogFilter" + :limit="50" + :user-params="catalogParams" + @on-fetch="extractTags" :update-router="false" - @on-change="extractTags" > <template #body="{ rows }"> <div class="catalog-list"> diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 0fe8c4d64..6de43e86a 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -1,13 +1,14 @@ <script setup> -import { computed, ref, onMounted, watch } from 'vue'; +import { computed, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import axios from 'axios'; 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 VnInput from 'src/components/common/VnInput.vue'; import getParamWhere from 'src/filters/getParamWhere'; -import VnFilterPanelChip from 'src/components/ui/VnFilterPanelChip.vue'; const { t } = useI18n(); @@ -47,19 +48,6 @@ const orderWayList = ref([ const orderBySelected = ref('relevancy DESC, name'); const orderWaySelected = ref('ASC'); -const routeQuery = JSON.parse(route?.query.params ?? '{}'); -const paramsSearch = ref({}); -const tagData = ref([]); - -onMounted(() => { - paramsSearch.value = JSON.parse(routeQuery.filter ?? '{}')?.where ?? {}; - if (Object.keys(paramsSearch.value).length > 0) vnFilterPanelRef.value.search(); - if (routeQuery.categoryFk && routeQuery.typeFk) { - selectedCategoryFk.value = routeQuery.categoryFk; - selectedTypeFk.value = routeQuery.typeFk; - } -}); - const createValue = (val, done) => { if (val.length > 2) { if (!tagOptions.value.includes(val)) { @@ -73,21 +61,19 @@ const resetCategory = () => { typeList.value = null; }; -const selectCategory = (category, search) => { - if (!paramsSearch.value?.filter) paramsSearch.value.filter = { where: {} }; - const where = paramsSearch.value.filter.where; - if (where.categoryFk === category?.id) { +const clearFilter = (key) => { + if (key === 'categoryFk') { resetCategory(); - where.categoryFk = null; - } else { - if (where.categoryFk && where.categoryFk !== category?.id) { - paramsSearch.value.typeFk = null; - selectedTypeFk.value = null; - typeList.value = []; - } + } +}; +const selectCategory = (params, category, search) => { + if (params.categoryFk === category?.id) { + resetCategory(); + params.categoryFk = null; + } else { selectedCategoryFk.value = category?.id; - where.categoryFk = category?.id; + params.categoryFk = category?.id; loadTypes(category?.id); } search(); @@ -100,10 +86,6 @@ const loadTypes = async (categoryFk = selectedCategoryFk.value) => { typeList.value = data; }; -watch(selectedTypeFk, (newValue) => { - paramsSearch.value.typeFk = newValue; -}); - const selectedCategory = computed(() => (categoryList.value || []).find( (category) => category?.id === selectedCategoryFk.value @@ -121,17 +103,26 @@ const selectedType = computed(() => { return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value); }); +function exprBuilder(param, value) { + switch (param) { + case 'categoryFk': + case 'typeFk': + return { [param]: value }; + case 'search': + if (/^\d+$/.test(value)) return { 'i.id': value }; + else return { 'i.name': { like: `%${value}%` } }; + } +} + const applyTagFilter = (params, search) => { if (!tagValues.value?.length) { params.tagGroups = null; search(); return; } - - if (!params.tagGroups || typeof params.tagGroups === 'string') { + if (!params.tagGroups) { params.tagGroups = []; } - params.tagGroups.push( JSON.stringify({ values: tagValues.value.filter((obj) => Object.keys(obj).length > 0), @@ -142,29 +133,17 @@ const applyTagFilter = (params, search) => { tagFk: selectedTag?.value?.tagFk, }) ); - - tagData.value.push(JSON.parse(params.tagGroups[params.tagGroups.length - 1])); search(); selectedTag.value = null; tagValues.value = [{}]; }; const removeTagChip = (selection, params, search) => { - if (typeof params.tagGroups === 'string') { - try { - params.tagGroups = JSON.parse(params.tagGroups); - } catch (error) { - console.error('Error parsing tagGroups:', error); - params.tagGroups = []; - } + if (params.tagGroups) { + params.tagGroups = (params.tagGroups || []).filter( + (value) => value !== selection + ); } - console.log('params.tagGroups: ', params.tagGroups); - console.log('selection: ', selection); - - if (Array.isArray(params.tagGroups)) { - params.tagGroups = params.tagGroups.filter((value) => value !== selection); - } - search(); }; @@ -199,12 +178,12 @@ function addOrder(value, field, params) { <VnFilterPanel ref="vnFilterPanelRef" :data-key="props.dataKey" - v-model="paramsSearch" - :redirect="false" - :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']" + :hidden-tags="['orderFk', 'orderBy']" + :un-removable-params="['orderFk', 'orderBy']" + :expr-builder="exprBuilder" :custom-tags="['tagGroups']" - :unremovable-params="['orderFk', 'orderBy']" - :disable-submit-event="true" + @remove="clearFilter" + :redirect="false" > <template #tags="{ tag, formatFn }"> <strong v-if="tag.label === 'categoryFk'"> @@ -222,13 +201,17 @@ function addOrder(value, field, params) { <template v-for="tag in customTags" :key="tag.label"> <template v-if="tag.label === 'tagGroups'"> <VnFilterPanelChip - v-for="chip in tagData" + v-for="chip in tag.value" :key="chip" removable @remove="removeTagChip(chip, params, searchFn)" > - <strong>{{ chip.tagSelection?.name }}: </strong> - <span>{{ chip.values[0].value }}</span> + <strong> {{ JSON.parse(chip).tagSelection?.name }}: </strong> + <span>{{ + (JSON.parse(chip).values || []) + .map((item) => item.value) + .join(' | ') + }}</span> </VnFilterPanelChip> </template> </template> @@ -243,7 +226,7 @@ function addOrder(value, field, params) { <QIcon :name="category.icon" class="category-icon" - @click="selectCategory(category, searchFn)" + @click="selectCategory(params, category, searchFn)" > <QTooltip> {{ t(category.name) }} @@ -337,7 +320,7 @@ function addOrder(value, field, params) { > <FetchData v-if="selectedTag" - :url="`Tags/${selectedTag.id}/filterValue`" + :url="`Tags/${selectedTag}/filterValue`" limit="30" auto-load @on-fetch="(data) => (tagOptions = data)" @@ -360,7 +343,7 @@ function addOrder(value, field, params) { @update:model-value="applyTagFilter(params, searchFn)" /> <VnSelect - v-else-if="selectedTag.id === 1" + v-else-if="selectedTag === 1" :label="t('params.value')" v-model="value.value" :options="tagOptions || []" @@ -375,7 +358,7 @@ function addOrder(value, field, params) { @new-value="createValue" @update:model-value="applyTagFilter(params, searchFn)" /> - <QInput + <VnInput v-else :label="t('params.value')" v-model="value.value" @@ -396,7 +379,6 @@ function addOrder(value, field, params) { icon="add_circle" shortcut="+" flat - size="md" class="filter-icon" @click="tagValues.push({})" /> From 1c8eabe2936e8a109a3580d3b3552b51d5f1712e Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 00:39:35 +0200 Subject: [PATCH 065/150] perf: refs #7283 #7283 declare composable inst4ead code duplicated --- src/pages/Item/Card/ItemDescriptor.vue | 34 +++------------------- src/pages/Item/ItemList.vue | 34 +++------------------- src/pages/Item/composables/cloneItem.js | 38 +++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 60 deletions(-) create mode 100644 src/pages/Item/composables/cloneItem.js diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue index 635dd17c8..6fbc5a4bd 100644 --- a/src/pages/Item/Card/ItemDescriptor.vue +++ b/src/pages/Item/Card/ItemDescriptor.vue @@ -1,20 +1,18 @@ <script setup> import { computed, ref, onMounted } from 'vue'; -import { useRoute, useRouter } from 'vue-router'; +import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { useQuasar } from 'quasar'; import CardDescriptor from 'src/components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; -import VnConfirm from 'components/ui/VnConfirm.vue'; import RegularizeStockForm from 'components/RegularizeStockForm.vue'; import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue'; import useCardDescription from 'src/composables/useCardDescription'; import axios from 'axios'; -import { getUrl } from 'src/composables/getUrl'; import { dashIfEmpty } from 'src/filters'; import { useArrayData } from 'src/composables/useArrayData'; +import { cloneItem } from 'src/pages/Item/composables/cloneItem'; const $props = defineProps({ id: { @@ -40,9 +38,8 @@ const $props = defineProps({ }, }); -const quasar = useQuasar(); +const { openCloneDialog } = cloneItem(); const route = useRoute(); -const router = useRouter(); const { t } = useI18n(); const warehouseConfig = ref(null); const entityId = computed(() => { @@ -96,29 +93,6 @@ const updateStock = async () => { const openRegularizeStockForm = () => { regularizeStockFormDialog.value.show(); }; - -const cloneItem = async () => { - try { - const { data } = await axios.post(`Items/${entityId.value}/clone`); - router.push({ name: 'ItemTags', params: { id: data.id } }); - } catch (err) { - console.error('Error cloning item'); - } -}; - -const openCloneDialog = async () => { - quasar - .dialog({ - component: VnConfirm, - componentProps: { - title: t('All its properties will be copied'), - message: t('Do you want to clone this item?'), - }, - }) - .onOk(async () => { - await cloneItem(); - }); -}; </script> <template> @@ -144,7 +118,7 @@ const openCloneDialog = async () => { </QDialog> </QItemSection> </QItem> - <QItem v-ripple clickable @click="openCloneDialog()"> + <QItem v-ripple clickable @click="openCloneDialog(entityId)"> <QItemSection> {{ t('globals.clone') }} </QItemSection> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 92c9d188b..30b86bc11 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -1,27 +1,24 @@ <script setup> import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRouter, useRoute } from 'vue-router'; +import { useRoute } from 'vue-router'; import VnImg from 'src/components/ui/VnImg.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { toDate } from 'src/filters'; -import axios from 'axios'; import FetchedTags from 'src/components/ui/FetchedTags.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import ItemSummary from '../Item/Card/ItemSummary.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue'; -import VnConfirm from 'src/components/ui/VnConfirm.vue'; -import { useQuasar } from 'quasar'; -const entityId = computed(() => route.params.id); +import { cloneItem } from 'src/pages/Item/composables/cloneItem'; +const entityId = computed(() => route.params.id); +const { openCloneDialog } = cloneItem(); const { viewSummary } = useSummaryDialog(); -const router = useRouter(); const { t } = useI18n(); const tableRef = ref(); const route = useRoute(); -const quasar = useQuasar(); const itemFilter = { include: [ @@ -297,29 +294,6 @@ const columns = computed(() => [ ], }, ]); - -const cloneItem = async () => { - try { - const { data } = await axios.post(`Items/${entityId.value}/clone`); - router.push({ name: 'ItemTags', params: { id: data.id } }); - } catch (err) { - console.error('Error cloning item'); - } -}; - -const openCloneDialog = async () => { - quasar - .dialog({ - component: VnConfirm, - componentProps: { - title: t('All its properties will be copied'), - message: t('Do you want to clone this item?'), - }, - }) - .onOk(async () => { - await cloneItem(); - }); -}; </script> <template> diff --git a/src/pages/Item/composables/cloneItem.js b/src/pages/Item/composables/cloneItem.js new file mode 100644 index 000000000..f1114c779 --- /dev/null +++ b/src/pages/Item/composables/cloneItem.js @@ -0,0 +1,38 @@ +import axios from 'axios'; +import { computed, ref, onMounted } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import { useI18n } from 'vue-i18n'; +import { useQuasar } from 'quasar'; +import VnConfirm from 'components/ui/VnConfirm.vue'; + +export function cloneItem() { + const { t } = useI18n(); + + const quasar = useQuasar(); + const route = useRoute(); + const router = useRouter(); + const cloneItem = async (entityId) => { + const { id } = entityId; + try { + const { data } = await axios.post(`Items/${id ?? entityId}/clone`); + router.push({ name: 'ItemTags', params: { id: data.id } }); + } catch (err) { + console.error('Error cloning item'); + } + }; + + const openCloneDialog = async (entityId) => { + quasar + .dialog({ + component: VnConfirm, + componentProps: { + title: t('All its properties will be copied'), + message: t('Do you want to clone this item?'), + }, + }) + .onOk(async () => { + await cloneItem(entityId); + }); + }; + return { openCloneDialog }; +} From 56ef811584970ae0c53f04f35604be941cd5a3ef Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 24 Oct 2024 07:47:26 +0200 Subject: [PATCH 066/150] fix: refs #7283 order translation --- src/pages/Item/ItemTypeList.vue | 4 +-- src/router/modules/item.js | 45 +++++++++++++++++---------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 9981a0d68..d9f7bba85 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -84,11 +84,11 @@ const columns = computed(() => [ :url="`ItemTypes`" :create="{ urlCreate: 'ItemTypes', - title: 'Create ItemTypes', + title: t('Create ItemTypes'), onDataSaved: () => tableRef.reload(), formInitialData: {}, }" - order="code ASC" + order="name ASC" :columns="columns" auto-load :right-search="false" diff --git a/src/router/modules/item.js b/src/router/modules/item.js index 48e19dd54..2838c3be7 100644 --- a/src/router/modules/item.js +++ b/src/router/modules/item.js @@ -48,6 +48,28 @@ export default { }, component: () => import('src/pages/Item/ItemList.vue'), }, + { + path: 'request', + name: 'ItemRequest', + meta: { + title: 'buyRequest', + icon: 'vn:buyrequest', + }, + component: () => import('src/pages/Item/ItemRequest.vue'), + }, + { + path: 'waste-breakdown', + name: 'WasteBreakdown', + meta: { + title: 'wasteBreakdown', + icon: 'vn:claims', + }, + beforeEnter: (to, from, next) => { + next({ name: 'ItemList' }); + window.location.href = + 'https://grafana.verdnatura.es/d/TTNXQAxVk'; + }, + }, { path: 'fixed-price', name: 'ItemFixedPrice', @@ -65,19 +87,7 @@ export default { }, component: () => import('src/pages/Item/ItemCreate.vue'), }, - { - path: 'waste-breakdown', - name: 'WasteBreakdown', - meta: { - title: 'wasteBreakdown', - icon: 'vn:claims', - }, - beforeEnter: (to, from, next) => { - next({ name: 'ItemList' }); - window.location.href = - 'https://grafana.verdnatura.es/d/TTNXQAxVk'; - }, - }, + { path: 'item-type-list', name: 'ItemTypeList', @@ -95,15 +105,6 @@ export default { }, component: () => import('src/pages/Item/ItemTypeCreate.vue'), }, - { - path: 'request', - name: 'ItemRequest', - meta: { - title: 'buyRequest', - icon: 'vn:buyrequest', - }, - component: () => import('src/pages/Item/ItemRequest.vue'), - }, ], }, { From 6f57d9e4906f7c74b50545a3271574b942efeac4 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 10:21:25 +0200 Subject: [PATCH 067/150] refactor: refs #7524 use VnTable --- src/components/VnTable/VnTable.vue | 6 +- src/pages/Ticket/TicketAdvance.vue | 605 +++++++++-------------- src/pages/Ticket/TicketAdvanceFilter.vue | 3 + 3 files changed, 234 insertions(+), 380 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 9209eaf7b..0afc5e8cf 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -447,7 +447,11 @@ function handleOnDataSaved(_) { /> </template> <template #header-cell="{ col }"> - <QTh v-if="col.visible ?? true"> + <QTh + v-if="col.visible ?? true" + :style="col.headerStyle" + :class="col.headerClass" + > <div class="column self-start q-ml-xs ellipsis" :class="`text-${col?.align ?? 'left'}`" diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index 2cce1dba8..8b109d778 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -1,24 +1,20 @@ <script setup> -import { onMounted, ref, computed, reactive } from 'vue'; +import { ref, computed, reactive, watch, h } from 'vue'; import { useI18n } from 'vue-i18n'; - import FetchData from 'components/FetchData.vue'; -import VnInput from 'src/components/common/VnInput.vue'; -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 VnProgress from 'src/components/common/VnProgressModal.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; import TicketAdvanceFilter from './TicketAdvanceFilter.vue'; - import { dashIfEmpty, toCurrency } from 'src/filters'; import { useVnConfirm } from 'composables/useVnConfirm'; -import { useArrayData } from 'composables/useArrayData'; import useNotify from 'src/composables/useNotify.js'; import { useState } from 'src/composables/useState'; import { toDateFormat } from 'src/filters/date.js'; import axios from 'axios'; +import VnTable from 'src/components/VnTable/VnTable.vue'; const state = useState(); const { t } = useI18n(); @@ -29,109 +25,58 @@ const user = state.getUser(); const itemPackingTypesOptions = ref([]); const zonesOptions = ref([]); const selectedTickets = ref([]); - -const exprBuilder = (param, value) => { - switch (param) { - case 'id': - case 'futureId': - case 'liters': - case 'futureLiters': - case 'lines': - case 'futureLines': - case 'totalWithVat': - case 'futureTotalWithVat': - case 'futureZone': - case 'notMovableLines': - case 'futureZoneFk': - return { [param]: value }; - case 'iptColFilter': - return { ipt: { like: `%${value}%` } }; - case 'futureIptColFilter': - return { futureIpt: { like: `%${value}%` } }; - } -}; - -const userParams = reactive({}); - -const arrayData = useArrayData('AdvanceTickets', { - url: 'Tickets/getTicketsAdvance', - userParams: userParams, - exprBuilder: exprBuilder, - limit: 0, +const vnTableRef = ref({}); +const originElRef = ref(null); +const destinationElRef = ref(null); +let today = Date.vnNew().toISOString(); +const tomorrow = new Date(today); +tomorrow.setDate(tomorrow.getDate() + 1); +const userParams = reactive({ + dateFuture: tomorrow, + dateToAdvance: today, + warehouseFk: user.value.warehouseFk, + ipt: 'H', + futureIpt: 'H', + isFullMovable: true, }); -const { store } = arrayData; -const tickets = computed(() => - (store.data || []).map((ticket, index) => ({ ...ticket, index: index })) -); - -const applyColumnFilter = async (col) => { - try { - const paramKey = col.columnFilter?.filterParamKey || col.field; - userParams[paramKey] = col.columnFilter.filterValue; - await arrayData.addFilter({ params: userParams }); - } catch (err) { - console.error('Error applying column filter', err); - } -}; - -const getInputEvents = (col) => { - return col.columnFilter.type === 'select' - ? { 'update:modelValue': () => applyColumnFilter(col) } - : { - 'keyup.enter': () => applyColumnFilter(col), - }; -}; const ticketColumns = computed(() => [ { label: '', name: 'icons', - align: 'left', - columnFilter: null, + hidden: true, + headerClass: 'horizontal-separator', }, { - label: t('advanceTickets.ticketId'), - name: 'ticketId', align: 'center', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - filterParamKey: 'id', - event: getInputEvents, - attrs: { - dense: true, - }, - }, + label: t('advanceTickets.ticketId'), + name: 'id', + headerClass: 'horizontal-separator', }, { + align: 'left', label: t('advanceTickets.ipt'), name: 'ipt', - field: 'ipt', - align: 'left', - sortable: true, columnFilter: { - component: VnSelect, - filterParamKey: 'iptColFilter', - type: 'select', - filterValue: null, - event: getInputEvents, + component: 'select', attrs: { - options: itemPackingTypesOptions.value, - 'option-value': 'code', - 'option-label': 'description', - dense: true, + url: 'itemPackingTypes', + fields: ['code', 'description'], + where: { isActive: true }, + optionValue: 'code', + optionLabel: 'description', + inWhere: false, }, }, - format: (val) => dashIfEmpty(val), + format: (row, dashIfEmpty) => dashIfEmpty(row.ipt), + headerClass: 'horizontal-separator', }, { + align: 'left', label: t('advanceTickets.state'), name: 'state', - align: 'left', - sortable: true, - columnFilter: null, + headerClass: 'horizontal-separator', + hidden: true, }, { label: t('advanceTickets.preparation'), @@ -139,171 +84,105 @@ const ticketColumns = computed(() => [ field: 'preparation', align: 'left', sortable: true, - columnFilter: null, + headerClass: 'horizontal-separator', + columnFilter: false, }, { - label: t('advanceTickets.liters'), - name: 'liters', - field: 'liters', align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, + label: t('advanceTickets.liters'), + headerClass: 'horizontal-separator', + name: 'liters', }, { + align: 'left', label: t('advanceTickets.lines'), name: 'lines', - field: 'lines', - align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, - format: (val) => dashIfEmpty(val), + headerClass: 'horizontal-separator', + format: (row, dashIfEmpty) => dashIfEmpty(row.lines), }, { + align: 'left', label: t('advanceTickets.import'), - field: 'import', - name: 'import', - align: 'left', - sortable: true, + name: 'totalWithVat', + hidden: true, + headerClass: 'horizontal-separator', + format: (row) => toCurrency(row.totalWithVat), }, { + align: 'left', label: t('advanceTickets.futureId'), name: 'futureId', - align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - filterParamKey: 'futureId', - event: getInputEvents, - attrs: { - dense: true, - }, - }, + headerClass: 'vertical-separator horizontal-separator', + columnClass: 'vertical-separator', }, { + align: 'left', label: t('advanceTickets.futureIpt'), name: 'futureIpt', - field: 'futureIpt', - align: 'left', - sortable: true, columnFilter: { - component: VnSelect, - filterParamKey: 'futureIptColFilter', - type: 'select', - filterValue: null, - event: getInputEvents, + component: 'select', attrs: { - options: itemPackingTypesOptions.value, - 'option-value': 'code', - 'option-label': 'description', - dense: true, + url: 'itemPackingTypes', + fields: ['code', 'description'], + where: { isActive: true }, + optionValue: 'code', + optionLabel: 'description', }, }, - format: (val) => dashIfEmpty(val), + headerClass: 'horizontal-separator', + format: (row, dashIfEmpty) => dashIfEmpty(row.futureIpt), }, { + align: 'left', label: t('advanceTickets.futureState'), name: 'futureState', - align: 'left', - sortable: true, - columnFilter: null, - format: (val) => dashIfEmpty(val), + headerClass: 'horizontal-separator', + hidden: true, }, { + align: 'left', label: t('advanceTickets.futureLiters'), + headerClass: 'horizontal-separator', name: 'futureLiters', - field: 'futureLiters', - align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, - format: (val) => dashIfEmpty(val), }, { + align: 'left', label: t('advanceTickets.futureZone'), - name: 'futureZoneName', - field: 'futureZoneName', - align: 'left', - sortable: true, + name: 'futureZoneFk', + columnClass: 'expand', columnFilter: { - component: VnSelect, - type: 'select', - filterValue: null, - filterParamKey: 'futureZoneFk', - event: getInputEvents, + component: 'select', + inWhere: true, attrs: { - options: zonesOptions.value, - 'option-value': 'id', - 'option-label': 'name', - dense: true, + url: 'Zones', + fields: ['id', 'name'], }, }, - format: (val) => dashIfEmpty(val), + columnField: { + component: null, + }, + headerClass: 'horizontal-separator', + format: (row, dashIfEmpty) => dashIfEmpty(row.futureZoneName), }, { + align: 'left', label: t('advanceTickets.notMovableLines'), + headerClass: 'horizontal-separator', name: 'notMovableLines', - field: 'notMovableLines', - align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, - format: (val) => dashIfEmpty(val), }, { + align: 'left', label: t('advanceTickets.futureLines'), + headerClass: 'horizontal-separator', name: 'futureLines', - field: 'futureLines', - align: 'left', - sortable: true, - columnFilter: { - component: VnInput, - type: 'text', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, - }, - format: (val) => dashIfEmpty(val), }, { - label: t('advanceTickets.futureImport'), - name: 'futureImport', align: 'left', - sortable: true, - columnFilter: null, + label: t('advanceTickets.futureImport'), + name: 'futureTotalWithVat', + hidden: true, + headerClass: 'horizontal-separator', + format: (row) => toCurrency(row.futureTotalWithVat), }, ]); @@ -329,7 +208,7 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => { const query = `tickets/${ticket.futureId}/componentUpdate`; if (!ticket.landed) { const newLanded = await getLanded({ - shipped: userParams.dateToAdvance, + shipped: vnTableRef.value.params.dateToAdvance, addressFk: ticket.futureAddressFk, agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk, warehouseFk: ticket.futureWarehouseFk, @@ -352,7 +231,7 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => { zoneFk: ticket.zoneFk ?? ticket.futureZoneFk, warehouseFk: ticket.futureWarehouseFk, companyFk: ticket.futureCompanyFk, - shipped: userParams.dateToAdvance, + shipped: vnTableRef.value.params.dateToAdvance, landed: ticket.landed, isDeleted: false, isWithoutNegatives, @@ -387,7 +266,7 @@ const moveTicketsAdvance = async () => { const params = { tickets: ticketsToMove }; await axios.post('Tickets/merge', params); - arrayData.fetch({ append: false }); + vnTableRef.value.reload(); selectedTickets.value = []; if (ticketsToMove.length) notify(t('advanceTickets.moveTicketSuccess'), 'positive'); @@ -437,7 +316,7 @@ const splitTickets = async () => { } catch (error) { console.error('Error splitting tickets', error); } finally { - arrayData.fetch({ append: false }); + vnTableRef.value.reload(); } }; @@ -455,21 +334,52 @@ const handleCloseProgressDialog = () => { const handleCancelProgress = () => (cancelProgress.value = true); -onMounted(async () => { - let today = Date.vnNew().toISOString(); - const tomorrow = new Date(today); - tomorrow.setDate(tomorrow.getDate() + 1); - userParams.dateFuture = tomorrow; - userParams.dateToAdvance = today; - userParams.warehouseFk = user.value.warehouseFk; - userParams.ipt = 'H'; - userParams.futureIpt = 'H'; - userParams.isFullMovable = true; - const filter = { limit: 0 }; - await arrayData.addFilter({ filter, userParams }); -}); -</script> +watch( + () => vnTableRef.value.tableRef?.$el, + ($el) => { + if (!$el) return; + const head = $el.querySelector('thead'); + const firstRow = $el.querySelector('thead > tr'); + const newRow = document.createElement('tr'); + destinationElRef.value = document.createElement('th'); + originElRef.value = document.createElement('th'); + + newRow.classList.add('bg-header'); + destinationElRef.value.classList.add('text-uppercase', 'color-vn-label'); + originElRef.value.classList.add('text-uppercase', 'color-vn-label'); + + destinationElRef.value.setAttribute('colspan', '7'); + originElRef.value.setAttribute('colspan', '9'); + + destinationElRef.value.textContent = `${t( + 'advanceTickets.destination' + )} ${toDateFormat(vnTableRef.value.params.dateToAdvance)}`; + originElRef.value.textContent = `${t('advanceTickets.origin')} ${toDateFormat( + vnTableRef.value.params.dateFuture + )}`; + + newRow.append(destinationElRef.value, originElRef.value); + head.insertBefore(newRow, firstRow); + }, + { once: true, inmmediate: true } +); + +watch( + () => vnTableRef.value.params, + () => { + if (originElRef.value && destinationElRef.value) { + destinationElRef.value.textContent = `${t( + 'advanceTickets.destination' + )} ${toDateFormat(vnTableRef.value.params.dateToAdvance)}`; + originElRef.value.textContent = `${t('advanceTickets.origin')} ${toDateFormat( + vnTableRef.value.params.dateFuture + )}`; + } + }, + { deep: true } +); +</script> <template> <FetchData url="itemPackingTypes" @@ -490,8 +400,9 @@ onMounted(async () => { auto-load @on-fetch="(data) => (zonesOptions = data)" /> + <!-- Fix searchbar #8154 --> <VnSearchbar - data-key="WeeklyTickets" + data-key="advanceTickets" :label="t('weeklyTickets.search')" :info="t('weeklyTickets.searchInfo')" /> @@ -538,173 +449,109 @@ onMounted(async () => { </VnSubToolbar> <RightMenu> <template #right-panel> - <TicketAdvanceFilter data-key="AdvanceTickets" /> + <TicketAdvanceFilter data-key="advanceTickets" /> </template> </RightMenu> <QPage class="column items-center q-pa-md"> - <QTable - :rows="tickets" + <VnTable + data-key="advanceTickets" + ref="vnTableRef" + url="Tickets/getTicketsAdvance" + search-url="advanceTickets" + :user-params="userParams" + :limit="0" :columns="ticketColumns" - row-key="index" - selection="multiple" + :table="{ + 'row-key': '$index', + selection: 'multiple', + }" v-model:selected="selectedTickets" :pagination="{ rowsPerPage: 0 }" :no-data-label="t('globals.noResults')" - style="max-width: 99%" + :right-search="false" + auto-load + :disable-option="{ card: true }" > - <template #header="props"> - <QTr :props="props"> - <QTh - class="horizontal-separator text-uppercase color-vn-label" - colspan="7" - translate - > - {{ t('advanceTickets.destination') }} - {{ toDateFormat(userParams.dateToAdvance) }} - </QTh> - <QTh - class="horizontal-separator text-uppercase color-vn-label" - colspan="9" - translate - > - {{ t('advanceTickets.origin') }} - {{ toDateFormat(userParams.dateFuture) }} - </QTh> - </QTr> - <QTr> - <QTh> - <QCheckbox v-model="props.selected" /> - </QTh> - <QTh - v-for="(col, index) in ticketColumns" - :key="index" - :class="{ 'vertical-separator': col.name === 'futureId' }" - > - {{ col.label }} - </QTh> - </QTr> + <template #column-icons="{ row }"> + <QIcon + v-if="row.futureAgency !== row.agency && row.agency" + color="primary" + name="vn:agency-term" + size="xs" + > + <QTooltip class="column"> + <span> + {{ + t('advanceTickets.originAgency', { + agency: row.futureAgency, + }) + }} + </span> + <span> + {{ + t('advanceTickets.destinationAgency', { + agency: row.agency, + }) + }} + </span> + </QTooltip> + </QIcon> </template> - <template #top-row="{ cols }"> - <QTr> - <QTd /> - <QTd - v-for="(col, index) in cols" - :key="index" - style="max-width: 100px" - > - <component - :is="col.columnFilter.component" - v-if="col.columnFilter" - v-model="col.columnFilter.filterValue" - v-bind="col.columnFilter.attrs" - v-on="col.columnFilter.event(col)" - dense - /> - </QTd> - </QTr> + <template #column-id="{ row }"> + <QBtn flat class="link"> + {{ row.id }} + <TicketDescriptorProxy :id="row.id" /> + </QBtn> </template> - <template #header-cell-availableLines="{ col }"> - <QTh class="vertical-separator"> - {{ col.label }} - </QTh> + <template #column-state="{ row }"> + <QBadge + v-if="row.state" + text-color="black" + :color="row.classColor" + class="q-ma-none" + dense + > + {{ row.state }} + </QBadge> + <span v-else> {{ dashIfEmpty(row.state) }}</span> </template> - <template #body-cell-icons="{ row }"> - <QTd class="q-gutter-x-xs"> - <QIcon - v-if="row.futureAgency !== row.agency && row.agency" - color="primary" - name="vn:agency-term" - size="xs" - > - <QTooltip class="column"> - <span> - {{ - t('advanceTickets.originAgency', { - agency: row.futureAgency, - }) - }} - </span> - <span> - {{ - t('advanceTickets.destinationAgency', { - agency: row.agency, - }) - }} - </span> - </QTooltip> - </QIcon> - </QTd> + <template #column-import="{ row }"> + <QBadge + :text-color="isLessThan50(row.totalWithVat) ? 'black' : 'white'" + :color="totalPriceColor(row.totalWithVat)" + class="q-ma-none" + dense + > + {{ toCurrency(row.totalWithVat || 0) }} + </QBadge> </template> - - <template #body-cell-ticketId="{ row }"> - <QTd> - <QBtn flat class="link"> - {{ row.id }} - <TicketDescriptorProxy :id="row.id" /> - </QBtn> - </QTd> + <template #column-futureId="{ row }"> + <QBtn flat class="link" dense> + {{ row.futureId }} + <TicketDescriptorProxy :id="row.futureId" /> + </QBtn> </template> - <template #body-cell-state="{ row }"> - <QTd> - <QBadge - v-if="row.state" - text-color="black" - :color="row.classColor" - class="q-ma-none" - dense - > - {{ row.state }} - </QBadge> - <span v-else> {{ dashIfEmpty(row.state) }}</span> - </QTd> + <template #column-futureState="{ row }"> + <QBadge + text-color="black" + :color="row.futureClassColor" + class="q-ma-none" + dense + > + {{ row.futureState }} + </QBadge> </template> - <template #body-cell-import="{ row }"> - <QTd> - <QBadge - :text-color="isLessThan50(row.totalWithVat) ? 'black' : 'white'" - :color="totalPriceColor(row.totalWithVat)" - class="q-ma-none" - dense - > - {{ toCurrency(row.totalWithVat || 0) }} - </QBadge> - </QTd> + <template #column-futureImport="{ row }"> + <QBadge + :text-color="isLessThan50(row.futureTotalWithVat) ? 'black' : 'white'" + :color="totalPriceColor(row.futureTotalWithVat)" + class="q-ma-none" + dense + > + {{ toCurrency(row.futureTotalWithVat || 0) }} + </QBadge> </template> - <template #body-cell-futureId="{ row }"> - <QTd class="vertical-separator"> - <QBtn flat class="link" dense> - {{ row.futureId }} - <TicketDescriptorProxy :id="row.futureId" /> - </QBtn> - </QTd> - </template> - <template #body-cell-futureState="{ row }"> - <QTd> - <QBadge - text-color="black" - :color="row.futureClassColor" - class="q-ma-none" - dense - > - {{ row.futureState }} - </QBadge> - </QTd> - </template> - <template #body-cell-futureImport="{ row }"> - <QTd> - <QBadge - :text-color=" - isLessThan50(row.futureTotalWithVat) ? 'black' : 'white' - " - :color="totalPriceColor(row.futureTotalWithVat)" - class="q-ma-none" - dense - > - {{ toCurrency(row.futureTotalWithVat || 0) }} - </QBadge> - </QTd> - </template> - </QTable> + </VnTable> <VnProgress :progress="progressPercentage" :cancelled="cancelProgress" @@ -723,11 +570,11 @@ onMounted(async () => { </template> <style scoped lang="scss"> -.vertical-separator { +:deep(.vertical-separator) { border-left: 4px solid white !important; } -.horizontal-separator { - border-bottom: 4px solid white !important; +:deep(.horizontal-separator) { + border-top: 4px solid white !important; } </style> diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue index 182f715a3..1d08d1ebf 100644 --- a/src/pages/Ticket/TicketAdvanceFilter.vue +++ b/src/pages/Ticket/TicketAdvanceFilter.vue @@ -57,6 +57,7 @@ onMounted(async () => await getItemPackingTypes()); auto-load /> <VnFilterPanel + search-url="advanceTickets" :data-key="props.dataKey" :search-button="true" :hidden-tags="['search']" @@ -100,6 +101,7 @@ onMounted(async () => await getItemPackingTypes()); dense outlined rounded + :use-like="false" > </VnSelect> </QItemSection> @@ -117,6 +119,7 @@ onMounted(async () => await getItemPackingTypes()); dense outlined rounded + :use-like="false" > </VnSelect> </QItemSection> From d918b76010c5ec8efa76cfec1298ba4c5cf0749b Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 10:24:28 +0200 Subject: [PATCH 068/150] chore: refs #7524 drop useless code --- src/pages/Ticket/TicketAdvance.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index 8b109d778..f6fb37e21 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -1,5 +1,5 @@ <script setup> -import { ref, computed, reactive, watch, h } from 'vue'; +import { ref, computed, reactive, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; From 87d2e0a39b1ee2d12e8468a0ca802168d32273f8 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 10:36:48 +0200 Subject: [PATCH 069/150] perf: refs #7283 #7283 handle composable i18n --- src/i18n/locale/es.yml | 108 ----------------------- src/pages/Item/Card/ItemDescriptor.vue | 2 - src/pages/Item/ItemList.vue | 2 - src/pages/Item/composables/cloneItem.js | 8 +- src/pages/Item/locale/en.yml | 112 ++++++++++++++++++++++++ src/pages/Item/locale/es.yml | 112 ++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 117 deletions(-) diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 7c368b7b7..bd414a793 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -1053,114 +1053,6 @@ travel: warehouse: Almacén travelFileDescription: 'Id envío { travelId }' file: Fichero -item: - searchbar: - label: Buscar artículo - descriptor: - item: Artículo - buyer: Comprador - color: Color - category: Categoría - stems: Tallos - visible: Visible - available: Disponible - warehouseText: 'Calculado sobre el almacén de { warehouseName }' - itemDiary: Registro de compra-venta - producer: Productor - list: - id: Identificador - grouping: Grouping - packing: Packing - description: Descripción - stems: Tallos - category: Reino - typeName: Tipo - intrastat: Intrastat - isActive: Activo - size: Medida - origin: Origen - weightByPiece: Peso (gramos)/tallo - userName: Comprador - stemMultiplier: Multiplicador - producer: Productor - landed: F. entrega - basicData: - type: Tipo - reference: Referencia - relevancy: Relevancia - stems: Tallos - multiplier: Multiplicador - generic: Genérico - intrastat: Intrastat - expense: Gasto - weightByPiece: Peso (gramos)/tallo - boxUnits: Unidades/caja - recycledPlastic: Plastico reciclado - nonRecycledPlastic: Plático no reciclado - isActive: Activo - hasKgPrice: Precio en kg - isFragile: Frágil - isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...) - isPhotoRequested: Hacer foto - isPhotoRequestedTooltip: Este artículo necesita una foto - description: Descripción - fixedPrice: - itemFk: ID Artículo - groupingPrice: Precio grouping - packingPrice: Precio packing - hasMinPrice: Tiene precio mínimo - minPrice: Precio min - started: Inicio - ended: Fin - warehouse: Almacén - create: - name: Nombre - tag: Etiqueta - priority: Prioridad - type: Tipo - intrastat: Intrastat - origin: Origen - summary: - basicData: 'Datos básicos' - otherData: 'Otros datos' - description: 'Descripción' - tax: 'IVA' - tags: 'Etiquetas' - botanical: 'Botánico' - barcode: 'Código de barras' - name: 'Nombre' - completeName: 'Nombre completo' - family: 'Familia' - size: 'Medida' - origin: 'Origen' - stems: 'Tallos' - multiplier: 'Multiplicador' - buyer: 'Comprador' - doPhoto: 'Hacer foto' - intrastatCode: 'Código intrastat' - intrastat: 'Intrastat' - ref: 'Referencia' - relevance: 'Relevancia' - weight: 'Peso (gramos)/tallo' - units: 'Unidades/caja' - expense: 'Gasto' - generic: 'Genérico' - recycledPlastic: 'Plástico reciclado' - nonRecycledPlastic: 'Plástico no reciclado' - minSalesQuantity: 'Cantidad mínima de venta' - genus: 'Genus' - specie: 'Specie' - buyRequest: - ticketId: 'ID Ticket' - shipped: 'F. envío' - requester: 'Solicitante' - requested: 'Solicitado' - price: 'Precio' - attender: 'Comprador' - item: 'Artículo' - achieved: 'Conseguido' - concept: 'Concepto' - state: 'Estado' components: topbar: {} itemsFilterPanel: diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue index 6fbc5a4bd..243d4c7cb 100644 --- a/src/pages/Item/Card/ItemDescriptor.vue +++ b/src/pages/Item/Card/ItemDescriptor.vue @@ -195,8 +195,6 @@ const openRegularizeStockForm = () => { <i18n> es: Regularize stock: Regularizar stock - All its properties will be copied: Todas sus propiedades serán copiadas - Do you want to clone this item?: ¿Desea clonar este artículo? Inactive article: Artículo inactivo </i18n> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 30b86bc11..cca5560fe 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -355,8 +355,6 @@ const columns = computed(() => [ <i18n> es: New item: Nuevo artículo - All its properties will be copied: Todas sus propiedades serán copiadas - Do you want to clone this item?: ¿Desea clonar este artículo? Preview: Vista previa Regularize stock: Regularizar stock </i18n> diff --git a/src/pages/Item/composables/cloneItem.js b/src/pages/Item/composables/cloneItem.js index f1114c779..2421c0808 100644 --- a/src/pages/Item/composables/cloneItem.js +++ b/src/pages/Item/composables/cloneItem.js @@ -1,6 +1,5 @@ import axios from 'axios'; -import { computed, ref, onMounted } from 'vue'; -import { useRoute, useRouter } from 'vue-router'; +import { useRouter } from 'vue-router'; import { useI18n } from 'vue-i18n'; import { useQuasar } from 'quasar'; import VnConfirm from 'components/ui/VnConfirm.vue'; @@ -9,7 +8,6 @@ export function cloneItem() { const { t } = useI18n(); const quasar = useQuasar(); - const route = useRoute(); const router = useRouter(); const cloneItem = async (entityId) => { const { id } = entityId; @@ -26,8 +24,8 @@ export function cloneItem() { .dialog({ component: VnConfirm, componentProps: { - title: t('All its properties will be copied'), - message: t('Do you want to clone this item?'), + title: t('item.descriptor.clone.title'), + message: t('item.descriptor.clone.subTitle'), }, }) .onOk(async () => { diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml index c32ee493c..034e39a17 100644 --- a/src/pages/Item/locale/en.yml +++ b/src/pages/Item/locale/en.yml @@ -88,3 +88,115 @@ itemType: worker: Worker category: Category temperature: Temperature +item: + searchbar: + label: Search item + descriptor: + item: Item + buyer: Buyer + color: Color + category: Category + stems: Stems + visible: Visible + available: Available + warehouseText: 'Calculated on the warehouse of { warehouseName }' + itemDiary: Item diary + producer: Producer + clone: + title: All its properties will be copied + subTitle: Do you want to clone this item? + + list: + id: Identifier + grouping: Grouping + packing: Packing + description: Description + stems: Stems + category: Category + typeName: Type + intrastat: Intrastat + isActive: Active + size: Size + origin: Origin + userName: Buyer + weightByPiece: Weight/Piece + stemMultiplier: Multiplier + producer: Producer + landed: Landed + basicData: + type: Type + reference: Reference + relevancy: Relevancy + stems: Stems + multiplier: Multiplier + generic: Generic + intrastat: Intrastat + expense: Expense + weightByPiece: Weight/Piece + boxUnits: Units/Box + recycledPlastic: Recycled Plastic + nonRecycledPlastic: Non recycled plastic + isActive: Active + hasKgPrice: Price in kg + isFragile: Fragile + isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...) + isPhotoRequested: Do photo + isPhotoRequestedTooltip: This item does need a photo + description: Description + fixedPrice: + itemFk: Item ID + groupingPrice: Grouping price + packingPrice: Packing price + hasMinPrice: Has min price + minPrice: Min price + started: Started + ended: Ended + warehouse: Warehouse + create: + name: Name + tag: Tag + priority: Priority + type: Type + intrastat: Intrastat + origin: Origin + buyRequest: + ticketId: 'Ticket ID' + shipped: 'Shipped' + requester: 'Requester' + requested: 'Requested' + price: 'Price' + attender: 'Attender' + item: 'Item' + achieved: 'Achieved' + concept: 'Concept' + state: 'State' + summary: + basicData: 'Basic data' + otherData: 'Other data' + description: 'Description' + tax: 'Tax' + tags: 'Tags' + botanical: 'Botanical' + barcode: 'Barcode' + name: 'Nombre' + completeName: 'Nombre completo' + family: 'Familia' + size: 'Medida' + origin: 'Origen' + stems: 'Tallos' + multiplier: 'Multiplicador' + buyer: 'Comprador' + doPhoto: 'Do photo' + intrastatCode: 'Código intrastat' + intrastat: 'Intrastat' + ref: 'Referencia' + relevance: 'Relevancia' + weight: 'Peso (gramos)/tallo' + units: 'Unidades/caja' + expense: 'Gasto' + generic: 'Genérico' + recycledPlastic: 'Plástico reciclado' + nonRecycledPlastic: 'Plástico no reciclado' + minSalesQuantity: 'Cantidad mínima de venta' + genus: 'Genus' + specie: 'Specie' diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml index d32cb7885..917bd7e5f 100644 --- a/src/pages/Item/locale/es.yml +++ b/src/pages/Item/locale/es.yml @@ -88,3 +88,115 @@ itemType: worker: Trabajador category: Reino temperature: Temperatura +item: + searchbar: + label: Buscar artículo + descriptor: + item: Artículo + buyer: Comprador + color: Color + category: Categoría + stems: Tallos + visible: Visible + available: Disponible + warehouseText: 'Calculado sobre el almacén de { warehouseName }' + itemDiary: Registro de compra-venta + producer: Productor + clone: + title: Todas sus propiedades serán copiadas + subTitle: ¿Desea clonar este artículo? + + list: + id: Identificador + grouping: Grouping + packing: Packing + description: Descripción + stems: Tallos + category: Reino + typeName: Tipo + intrastat: Intrastat + isActive: Activo + size: Medida + origin: Origen + weightByPiece: Peso (gramos)/tallo + userName: Comprador + stemMultiplier: Multiplicador + producer: Productor + landed: F. entrega + basicData: + type: Tipo + reference: Referencia + relevancy: Relevancia + stems: Tallos + multiplier: Multiplicador + generic: Genérico + intrastat: Intrastat + expense: Gasto + weightByPiece: Peso (gramos)/tallo + boxUnits: Unidades/caja + recycledPlastic: Plastico reciclado + nonRecycledPlastic: Plático no reciclado + isActive: Activo + hasKgPrice: Precio en kg + isFragile: Frágil + isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...) + isPhotoRequested: Hacer foto + isPhotoRequestedTooltip: Este artículo necesita una foto + description: Descripción + fixedPrice: + itemFk: ID Artículo + groupingPrice: Precio grouping + packingPrice: Precio packing + hasMinPrice: Tiene precio mínimo + minPrice: Precio min + started: Inicio + ended: Fin + warehouse: Almacén + create: + name: Nombre + tag: Etiqueta + priority: Prioridad + type: Tipo + intrastat: Intrastat + origin: Origen + summary: + basicData: 'Datos básicos' + otherData: 'Otros datos' + description: 'Descripción' + tax: 'IVA' + tags: 'Etiquetas' + botanical: 'Botánico' + barcode: 'Código de barras' + name: 'Nombre' + completeName: 'Nombre completo' + family: 'Familia' + size: 'Medida' + origin: 'Origen' + stems: 'Tallos' + multiplier: 'Multiplicador' + buyer: 'Comprador' + doPhoto: 'Hacer foto' + intrastatCode: 'Código intrastat' + intrastat: 'Intrastat' + ref: 'Referencia' + relevance: 'Relevancia' + weight: 'Peso (gramos)/tallo' + units: 'Unidades/caja' + expense: 'Gasto' + generic: 'Genérico' + recycledPlastic: 'Plástico reciclado' + nonRecycledPlastic: 'Plástico no reciclado' + minSalesQuantity: 'Cantidad mínima de venta' + genus: 'Genus' + specie: 'Specie' + buyRequest: + ticketId: 'ID Ticket' + shipped: 'F. envío' + requester: 'Solicitante' + requested: 'Solicitado' + price: 'Precio' + attender: 'Comprador' + item: 'Artículo' + achieved: 'Conseguido' + concept: 'Concepto' + state: 'Estado' From 2d8789312b41049ae833bc9f05320af1256e3af8 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 11:34:55 +0200 Subject: [PATCH 070/150] test: refs #6943 #6943 remove skip tests --- .../integration/client/clientList.spec.js | 17 ----------------- test/cypress/support/commands.js | 18 ------------------ 2 files changed, 35 deletions(-) diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index 1b41e55cd..f150fc190 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -43,21 +43,4 @@ describe('Client list', () => { cy.url().should('include', `/customer/${id}/summary`); }); }); - - it.skip('Client founded create ticket', () => { - const search = 'Jessica Jones'; - cy.searchByLabel('Name', search); - cy.clickButtonsDescriptor(2); - cy.waitForElement('#formModel'); - cy.waitForElement('.q-form'); - cy.checkValueSelectForm(1, search); - }); - it.skip('Client founded create order', () => { - const search = 'Jessica Jones'; - cy.searchByLabel('Name', search); - cy.clickButtonsDescriptor(3); - cy.waitForElement('#formModel'); - cy.waitForElement('.q-form'); - cy.checkValueForm(2, search); - }); }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 9106a64cd..33dfa85df 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -262,12 +262,6 @@ Cypress.Commands.add('openUserPanel', () => { ).click(); }); -Cypress.Commands.add('clickButtonsDescriptor', (id) => { - cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`) - .invoke('removeAttr', 'target') - .click(); -}); - Cypress.Commands.add('openActions', (row) => { cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); }); @@ -281,18 +275,6 @@ Cypress.Commands.add('checkNotification', (type) => { cy.get('.q-notification__message').should('have.text', values[type]); }); -Cypress.Commands.add('checkValueForm', (id, search) => { - cy.get( - `.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input` - ).should('have.value', search); -}); - -Cypress.Commands.add('checkValueSelectForm', (id, search) => { - cy.get( - `.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input` - ).should('have.value', search); -}); - Cypress.Commands.add('searchByLabel', (label, value) => { cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`); }); From f1350dece519b9a832656c6434eb910619fd618e Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 11:43:29 +0200 Subject: [PATCH 071/150] fix: refs #7524 changes --- src/pages/Ticket/TicketAdvance.vue | 57 ++++++++++++------------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index f6fb37e21..7db2b54b3 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -243,36 +243,31 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => { }; const moveTicketsAdvance = async () => { - try { - let ticketsToMove = []; - for (const ticket of selectedTickets.value) { - if (!ticket.id) { - try { - const { query, params } = await requestComponentUpdate(ticket, false); - axios.post(query, params); - } catch (e) { - console.error('Error moving ticket', e); - } - continue; + let ticketsToMove = []; + for (const ticket of selectedTickets.value) { + if (!ticket.id) { + try { + const { query, params } = await requestComponentUpdate(ticket, false); + axios.post(query, params); + } catch (e) { + console.error('Error moving ticket', e); } - ticketsToMove.push({ - originId: ticket.futureId, - destinationId: ticket.id, - originShipped: ticket.futureShipped, - destinationShipped: ticket.shipped, - workerFk: ticket.workerFk, - }); + continue; } - - const params = { tickets: ticketsToMove }; - await axios.post('Tickets/merge', params); - vnTableRef.value.reload(); - selectedTickets.value = []; - if (ticketsToMove.length) - notify(t('advanceTickets.moveTicketSuccess'), 'positive'); - } catch (error) { - console.error('Error moving tickets', error); + ticketsToMove.push({ + originId: ticket.futureId, + destinationId: ticket.id, + originShipped: ticket.futureShipped, + destinationShipped: ticket.shipped, + workerFk: ticket.workerFk, + }); } + + const params = { tickets: ticketsToMove }; + await axios.post('Tickets/merge', params); + vnTableRef.value.reload(); + selectedTickets.value = []; + if (ticketsToMove.length) notify(t('advanceTickets.moveTicketSuccess'), 'positive'); }; const progressLength = ref(0); @@ -313,8 +308,6 @@ const splitTickets = async () => { progressAdd(ticket.futureId); } } - } catch (error) { - console.error('Error splitting tickets', error); } finally { vnTableRef.value.reload(); } @@ -400,12 +393,6 @@ watch( auto-load @on-fetch="(data) => (zonesOptions = data)" /> - <!-- Fix searchbar #8154 --> - <VnSearchbar - data-key="advanceTickets" - :label="t('weeklyTickets.search')" - :info="t('weeklyTickets.searchInfo')" - /> <VnSubToolbar> <template #st-data> <QBtn From b02199eb07e63ac60ece266ab3139fdc74fba6ed Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Thu, 24 Oct 2024 12:03:49 +0200 Subject: [PATCH 072/150] fix: reload window when price is recalculated --- src/pages/Ticket/Card/TicketSale.vue | 1 + src/pages/Ticket/Card/TicketSaleMoreActions.vue | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index f179257f7..3e89e96bf 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -476,6 +476,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :ticket-config="ticketConfig" @get-mana="getMana()" @update-discounts="updateDiscount" + @re-calculated-price="resetChanges" /> <QBtn color="primary" diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 94db67be2..a35e21f3f 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -14,7 +14,7 @@ import { toDateFormat } from 'src/filters/date'; import { useRole } from 'src/composables/useRole'; import { useVnConfirm } from 'composables/useVnConfirm'; -const emit = defineEmits(['updateDiscounts', 'getMana']); +const emit = defineEmits(['updateDiscounts', 'getMana', 'reCalculatedPrice']); const props = defineProps({ disable: { @@ -105,8 +105,10 @@ const showSmsDialog = (template) => { const calculateSalePrice = async () => { if (!props.sales) return; + console.log('CALLED'); await axios.post(`Sales/recalculatePrice`, props.sales); notify(t('globals.dataSaved'), 'positive'); + emit('reCalculatedPrice', props.sales); }; const changeMultipleDiscount = () => { From c9e82f2caf728a475440d6faf4a0d447edc51129 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Thu, 24 Oct 2024 12:04:54 +0200 Subject: [PATCH 073/150] refactor: deleted log --- src/pages/Ticket/Card/TicketSaleMoreActions.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index a35e21f3f..43ba39b91 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -105,7 +105,6 @@ const showSmsDialog = (template) => { const calculateSalePrice = async () => { if (!props.sales) return; - console.log('CALLED'); await axios.post(`Sales/recalculatePrice`, props.sales); notify(t('globals.dataSaved'), 'positive'); emit('reCalculatedPrice', props.sales); From c9cfb2b1cf38854ca7c7d36217c25776540a8cd5 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 12:29:46 +0200 Subject: [PATCH 074/150] fix: refs #7524 changes --- src/pages/Ticket/TicketAdvance.vue | 57 ++++++++++++------------------ 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index f6fb37e21..7db2b54b3 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -243,36 +243,31 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => { }; const moveTicketsAdvance = async () => { - try { - let ticketsToMove = []; - for (const ticket of selectedTickets.value) { - if (!ticket.id) { - try { - const { query, params } = await requestComponentUpdate(ticket, false); - axios.post(query, params); - } catch (e) { - console.error('Error moving ticket', e); - } - continue; + let ticketsToMove = []; + for (const ticket of selectedTickets.value) { + if (!ticket.id) { + try { + const { query, params } = await requestComponentUpdate(ticket, false); + axios.post(query, params); + } catch (e) { + console.error('Error moving ticket', e); } - ticketsToMove.push({ - originId: ticket.futureId, - destinationId: ticket.id, - originShipped: ticket.futureShipped, - destinationShipped: ticket.shipped, - workerFk: ticket.workerFk, - }); + continue; } - - const params = { tickets: ticketsToMove }; - await axios.post('Tickets/merge', params); - vnTableRef.value.reload(); - selectedTickets.value = []; - if (ticketsToMove.length) - notify(t('advanceTickets.moveTicketSuccess'), 'positive'); - } catch (error) { - console.error('Error moving tickets', error); + ticketsToMove.push({ + originId: ticket.futureId, + destinationId: ticket.id, + originShipped: ticket.futureShipped, + destinationShipped: ticket.shipped, + workerFk: ticket.workerFk, + }); } + + const params = { tickets: ticketsToMove }; + await axios.post('Tickets/merge', params); + vnTableRef.value.reload(); + selectedTickets.value = []; + if (ticketsToMove.length) notify(t('advanceTickets.moveTicketSuccess'), 'positive'); }; const progressLength = ref(0); @@ -313,8 +308,6 @@ const splitTickets = async () => { progressAdd(ticket.futureId); } } - } catch (error) { - console.error('Error splitting tickets', error); } finally { vnTableRef.value.reload(); } @@ -400,12 +393,6 @@ watch( auto-load @on-fetch="(data) => (zonesOptions = data)" /> - <!-- Fix searchbar #8154 --> - <VnSearchbar - data-key="advanceTickets" - :label="t('weeklyTickets.search')" - :info="t('weeklyTickets.searchInfo')" - /> <VnSubToolbar> <template #st-data> <QBtn From 3f640c650b12152f3a086464d3b0140a5bc6198d Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 12:32:15 +0200 Subject: [PATCH 075/150] perf: refs #7283 #7283 handle i18n --- src/i18n/locale/en.yml | 108 ----------------------------------- src/pages/Item/locale/en.yml | 1 - src/pages/Item/locale/es.yml | 1 - 3 files changed, 110 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index b19363c12..c1748c8ff 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -1055,114 +1055,6 @@ travel: warehouse: Warehouse travelFileDescription: 'Travel id { travelId }' file: File -item: - searchbar: - label: Search item - descriptor: - item: Item - buyer: Buyer - color: Color - category: Category - stems: Stems - visible: Visible - available: Available - warehouseText: 'Calculated on the warehouse of { warehouseName }' - itemDiary: Item diary - producer: Producer - list: - id: Identifier - grouping: Grouping - packing: Packing - description: Description - stems: Stems - category: Category - typeName: Type - intrastat: Intrastat - isActive: Active - size: Size - origin: Origin - userName: Buyer - weightByPiece: Weight/Piece - stemMultiplier: Multiplier - producer: Producer - landed: Landed - basicData: - type: Type - reference: Reference - relevancy: Relevancy - stems: Stems - multiplier: Multiplier - generic: Generic - intrastat: Intrastat - expense: Expense - weightByPiece: Weight/Piece - boxUnits: Units/Box - recycledPlastic: Recycled Plastic - nonRecycledPlastic: Non recycled plastic - isActive: Active - hasKgPrice: Price in kg - isFragile: Fragile - isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...) - isPhotoRequested: Do photo - isPhotoRequestedTooltip: This item does need a photo - description: Description - fixedPrice: - itemFk: Item ID - groupingPrice: Grouping price - packingPrice: Packing price - hasMinPrice: Has min price - minPrice: Min price - started: Started - ended: Ended - warehouse: Warehouse - create: - name: Name - tag: Tag - priority: Priority - type: Type - intrastat: Intrastat - origin: Origin - buyRequest: - ticketId: 'Ticket ID' - shipped: 'Shipped' - requester: 'Requester' - requested: 'Requested' - price: 'Price' - attender: 'Attender' - item: 'Item' - achieved: 'Achieved' - concept: 'Concept' - state: 'State' - summary: - basicData: 'Basic data' - otherData: 'Other data' - description: 'Description' - tax: 'Tax' - tags: 'Tags' - botanical: 'Botanical' - barcode: 'Barcode' - name: 'Nombre' - completeName: 'Nombre completo' - family: 'Familia' - size: 'Medida' - origin: 'Origen' - stems: 'Tallos' - multiplier: 'Multiplicador' - buyer: 'Comprador' - doPhoto: 'Do photo' - intrastatCode: 'Código intrastat' - intrastat: 'Intrastat' - ref: 'Referencia' - relevance: 'Relevancia' - weight: 'Peso (gramos)/tallo' - units: 'Unidades/caja' - expense: 'Gasto' - generic: 'Genérico' - recycledPlastic: 'Plástico reciclado' - nonRecycledPlastic: 'Plástico no reciclado' - minSalesQuantity: 'Cantidad mínima de venta' - genus: 'Genus' - specie: 'Specie' components: topbar: {} itemsFilterPanel: diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml index 034e39a17..53310cbd0 100644 --- a/src/pages/Item/locale/en.yml +++ b/src/pages/Item/locale/en.yml @@ -105,7 +105,6 @@ item: clone: title: All its properties will be copied subTitle: Do you want to clone this item? - list: id: Identifier grouping: Grouping diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml index 917bd7e5f..1ade9b955 100644 --- a/src/pages/Item/locale/es.yml +++ b/src/pages/Item/locale/es.yml @@ -105,7 +105,6 @@ item: clone: title: Todas sus propiedades serán copiadas subTitle: ¿Desea clonar este artículo? - list: id: Identificador grouping: Grouping From 64f36c7d4b7a8210df9af7e087660094c0a28c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es> Date: Thu, 24 Oct 2024 12:35:11 +0200 Subject: [PATCH 076/150] feat: refs #8087 Traspasar redadas a travels --- src/i18n/locale/en.yml | 5 +---- src/i18n/locale/es.yml | 5 +---- src/pages/Entry/Card/EntryBasicData.vue | 6 ------ src/pages/Entry/Card/EntryDescriptor.vue | 4 ++-- src/pages/Entry/Card/EntrySummary.vue | 5 ----- src/pages/Entry/EntryList.vue | 4 ++-- src/pages/Travel/Card/TravelBasicData.vue | 4 ++++ src/pages/Travel/Card/TravelSummary.vue | 4 ++++ 8 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index aa8df17e2..d1ea5ceb9 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -106,6 +106,7 @@ globals: weight: Weight error: Ups! Something went wrong recalc: Recalculate + daysInForward: Forward days pageTitles: logIn: Login addressEdit: Update address @@ -369,7 +370,6 @@ entry: companyFk: Company travelFk: Travel isExcludedFromAvailable: Inventory - isRaid: Raid invoiceAmount: Import summary: commission: Commission @@ -380,7 +380,6 @@ entry: ordered: Ordered confirmed: Confirmed booked: Booked - raid: Raid excludedFromAvailable: Inventory travelReference: Reference travelAgency: Agency @@ -413,7 +412,6 @@ entry: ordered: Ordered confirmed: Confirmed booked: Booked - raid: Raid excludedFromAvailable: Inventory agency: Agency warehouseOut: Warehouse Out @@ -476,7 +474,6 @@ entry: packingOut: Package out landing: Landing isExcludedFromAvailable: Es inventory - isRaid: Raid ticket: pageTitles: tickets: Tickets diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 575e2c6c7..18e580893 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -108,6 +108,7 @@ globals: weight: Peso error: ¡Ups! Algo salió mal recalc: Recalcular + daysInForward: Días en el futuro pageTitles: logIn: Inicio de sesión addressEdit: Modificar consignatario @@ -371,7 +372,6 @@ entry: companyFk: Empresa travelFk: Envio isExcludedFromAvailable: Inventario - isRaid: Redada invoiceAmount: Importe summary: commission: Comisión @@ -382,7 +382,6 @@ entry: ordered: Pedida confirmed: Confirmada booked: Contabilizada - raid: Redada excludedFromAvailable: Inventario travelReference: Referencia travelAgency: Agencia @@ -415,7 +414,6 @@ entry: ordered: Pedida confirmed: Confirmado booked: Asentado - raid: Redada excludedFromAvailable: Inventario agency: Agencia warehouseOut: Alm. salida @@ -478,7 +476,6 @@ entry: packingOut: Embalaje envíos landing: Llegada isExcludedFromAvailable: Es inventario - isRaid: Redada ticket: pageTitles: tickets: Tickets diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue index b81b1db22..d60ed8645 100644 --- a/src/pages/Entry/Card/EntryBasicData.vue +++ b/src/pages/Entry/Card/EntryBasicData.vue @@ -168,12 +168,6 @@ const onFilterTravelSelected = (formData, id) => { v-model="data.isExcludedFromAvailable" :label="t('entry.basicData.excludedFromAvailable')" /> - <QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" /> - <QCheckbox - v-if="isAdministrative()" - v-model="data.isBooked" - :label="t('entry.basicData.booked')" - /> </VnRow> </template> </FormModel> diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue index b22d6ba53..d85e5e1a0 100644 --- a/src/pages/Entry/Card/EntryDescriptor.vue +++ b/src/pages/Entry/Card/EntryDescriptor.vue @@ -32,7 +32,6 @@ const entryFilter = { { relation: 'travel', scope: { - fields: ['id', 'landed', 'shipped', 'agencyModeFk', 'warehouseOutFk'], include: [ { relation: 'agency', @@ -143,8 +142,9 @@ watch; > <QTooltip>{{ t('Inventory entry') }}</QTooltip> </QIcon> + {{ console.log('currentEntry', currentEntry) }} <QIcon - v-if="currentEntry?.isRaid" + v-if="currentEntry?.travel?.daysInForward" name="vn:net" color="primary" size="xs" diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue index c2f1e6b57..62e13551a 100644 --- a/src/pages/Entry/Card/EntrySummary.vue +++ b/src/pages/Entry/Card/EntrySummary.vue @@ -259,11 +259,6 @@ const fetchEntryBuys = async () => { v-model="entry.isBooked" :disable="true" /> - <QCheckbox - :label="t('entry.summary.raid')" - v-model="entry.isRaid" - :disable="true" - /> <QCheckbox :label="t('entry.summary.excludedFromAvailable')" v-model="entry.isExcludedFromAvailable" diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue index 6f7ff1935..00b6c8626 100644 --- a/src/pages/Entry/EntryList.vue +++ b/src/pages/Entry/EntryList.vue @@ -168,8 +168,8 @@ const columns = computed(() => [ }, { align: 'left', - label: t('entry.list.tableVisibleColumns.isRaid'), - name: 'isRaid', + label: t('entry.list.tableVisibleColumns.daysInForward'), + name: 'daysInForward', chip: { color: null, condition: (value) => value, diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue index a3620a6ba..aaf8abb91 100644 --- a/src/pages/Travel/Card/TravelBasicData.vue +++ b/src/pages/Travel/Card/TravelBasicData.vue @@ -72,6 +72,10 @@ const agenciesOptions = ref([]); </VnRow> <VnRow> <VnInput v-model="data.m3" label="m3" /> + <VnInput + :label="t('globals.daysInForward')" + v-model="data.daysInForward" + /> </VnRow> <VnRow> <QCheckbox diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue index 4be198493..59059b0db 100644 --- a/src/pages/Travel/Card/TravelSummary.vue +++ b/src/pages/Travel/Card/TravelSummary.vue @@ -303,6 +303,10 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`; <VnLv :label="t('globals.reference')" :value="travel.ref" /> <VnLv label="m³" :value="travel.m3" /> <VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" /> + <VnLv + :label="t('globals.daysInForward')" + :value="travel?.daysInForward" + /> </QCard> <QCard class="full-width"> <VnTitle :text="t('travel.summary.entries')" /> From f52b4c9a598dc400c3379f800b2f1157f339159f Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 13:24:45 +0200 Subject: [PATCH 077/150] fix: refs #7524 vnProgressModal --- src/components/common/VnProgressModal.vue | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/components/common/VnProgressModal.vue b/src/components/common/VnProgressModal.vue index cfd948d5f..99bf15a7e 100644 --- a/src/components/common/VnProgressModal.vue +++ b/src/components/common/VnProgressModal.vue @@ -9,10 +9,6 @@ const $props = defineProps({ type: Number, //Progress value (1.0 > x > 0.0) required: true, }, - showDialog: { - type: Boolean, - required: true, - }, cancelled: { type: Boolean, required: false, @@ -24,25 +20,17 @@ const emit = defineEmits(['cancel', 'close']); const dialogRef = ref(null); -const _showDialog = computed({ - get: () => $props.showDialog, - set: (value) => { - if (value) dialogRef.value.show(); - }, +const showDialog = defineModel('showDialog', { + type: Boolean, + default: false, }); const _progress = computed(() => $props.progress); - const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`); - -const cancel = () => { - dialogRef.value.hide(); - emit('cancel'); -}; </script> <template> - <QDialog ref="dialogRef" v-model="_showDialog" @hide="onDialogHide"> + <QDialog ref="dialogRef" v-model="showDialog" @hide="emit('close')"> <QCard class="full-width dialog"> <QCardSection class="row"> <span class="text-h6">{{ t('Progress') }}</span> @@ -80,7 +68,7 @@ const cancel = () => { type="button" flat class="text-primary" - @click="cancel()" + v-close-popup > {{ t('globals.cancel') }} </QBtn> From 823c354ef1a376b988e34a7f2f50f6f210ee34b7 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 13:27:08 +0200 Subject: [PATCH 078/150] fix: refs #7524 vnProgressModal --- src/components/common/VnProgressModal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/VnProgressModal.vue b/src/components/common/VnProgressModal.vue index 99bf15a7e..23fb8ae70 100644 --- a/src/components/common/VnProgressModal.vue +++ b/src/components/common/VnProgressModal.vue @@ -35,7 +35,7 @@ const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`); <QCardSection class="row"> <span class="text-h6">{{ t('Progress') }}</span> <QSpace /> - <QBtn icon="close" flat round dense @click="emit('close')" /> + <QBtn icon="close" flat round dense v-close-popup /> </QCardSection> <QCardSection> <div class="column"> From 671d9fd6fb0f6686ade7b54bfe9e36b6d8a11a8c Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 24 Oct 2024 13:38:13 +0200 Subject: [PATCH 079/150] fix: refs #7283 itemtype fix --- src/pages/Item/ItemTypeList.vue | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index d9f7bba85..2c1153016 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -1,14 +1,14 @@ <script setup> import { useI18n } from 'vue-i18n'; -import { useRoute } from 'vue-router'; import { ref, computed } from 'vue'; import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; +import FetchData from 'components/FetchData.vue'; -const route = useRoute(); const { t } = useI18n(); const tableRef = ref(); -const entityId = computed(() => route.params.id); +const workerOptions = ref([]); +const ItemCategoriesOptions = ref([]); const columns = computed(() => [ { @@ -40,7 +40,7 @@ const columns = computed(() => [ create: true, component: 'select', attrs: { - url: 'Workers', + options: workerOptions.value, optionLabel: 'firstName', optionValue: 'id', }, @@ -54,8 +54,9 @@ const columns = computed(() => [ create: true, component: 'select', attrs: { - url: 'ItemCategories', + options: ItemCategoriesOptions.value, fields: ['id', 'name'], + order: 'name ASC', }, cardVisible: false, visible: false, @@ -77,6 +78,18 @@ const columns = computed(() => [ </script> <template> + <FetchData + url="Workers" + :filter="{ fields: ['id', 'firstName'], order: ['firstName ASC'] }" + @on-fetch="(data) => (workerOptions = data)" + auto-load + /> + <FetchData + url="ItemCategories" + :filter="{ fields: ['id', 'name'], order: ['name ASC'] }" + @on-fetch="(data) => (ItemCategoriesOptions = data)" + auto-load + /> <ItemTypeSearchbar /> <VnTable ref="tableRef" @@ -102,8 +115,8 @@ const columns = computed(() => [ id: Id code: Código name: Nombre - worker: Encargado - ItemCategory: Categoría + worker: Trabajador + ItemCategory: Reino Temperature: Temperatura Create ItemTypes: Crear familia en: From 70dcadc57d58831ae7149be462536f22e00469cd Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 11:52:13 +0000 Subject: [PATCH 080/150] Actualizar src/components/CreateNewPostcodeForm.vue --- src/components/CreateNewPostcodeForm.vue | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/CreateNewPostcodeForm.vue b/src/components/CreateNewPostcodeForm.vue index 030ca1388..afd3fc025 100644 --- a/src/components/CreateNewPostcodeForm.vue +++ b/src/components/CreateNewPostcodeForm.vue @@ -108,11 +108,11 @@ watch( watch( () => postcodeFormData.provinceFk, - async (newProvinceFk) => { + async (newProvinceFk, oldValueFk) => { if (Array.isArray(newProvinceFk)) { newProvinceFk = newProvinceFk[0]; } - if (newProvinceFk !== postcodeFormData.provinceFk) { + if (newProvinceFk !== oldValueFk) { await townsFetchDataRef.value.fetch({ where: { provinceFk: newProvinceFk }, }); @@ -147,13 +147,7 @@ async function handleCountries(data) { auto-load url="Towns/location" /> - <FetchData - @on-fetch="handleCountries" - :sort-by="['name ASC']" - :limit="30" - auto-load - url="Countries" - /> + <FormModelPopup url-create="postcodes" model="postcode" @@ -219,8 +213,11 @@ async function handleCountries(data) { @on-province-created="onProvinceCreated" /> <VnSelect + url="Countries" + :limit="30" + :sort-by="['name ASC']" :label="t('Country')" - :options="countriesOptions" + @update:options="handleCountries" hide-selected option-label="name" option-value="id" From c2043902f8f77a6f4957b82d7f7576f540bf8f1e Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 24 Oct 2024 14:08:11 +0200 Subject: [PATCH 081/150] fix: refs #7283 fix required --- src/pages/Item/ItemRequestDenyForm.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/Item/ItemRequestDenyForm.vue b/src/pages/Item/ItemRequestDenyForm.vue index be70fb799..c9a4cbe9c 100644 --- a/src/pages/Item/ItemRequestDenyForm.vue +++ b/src/pages/Item/ItemRequestDenyForm.vue @@ -10,6 +10,7 @@ defineProps({ requestId: { type: Number, default: null, + required: true, }, }); @@ -43,6 +44,7 @@ onMounted(async () => { type="textarea" v-model="data.observation" fill-input + :required="true" autogrow /> </div> From 4828e32c9eec24cb349bb5c39cae075653d08538 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 24 Oct 2024 14:26:16 +0200 Subject: [PATCH 082/150] fix: refs #7652 datakey --- src/pages/Worker/Card/WorkerDescriptor.vue | 7 ++++++- src/pages/Worker/Card/WorkerDescriptorProxy.vue | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue index 3675d40f8..b9ccedbfc 100644 --- a/src/pages/Worker/Card/WorkerDescriptor.vue +++ b/src/pages/Worker/Card/WorkerDescriptor.vue @@ -17,6 +17,11 @@ const $props = defineProps({ required: false, default: null, }, + dataKey: { + type: String, + required: false, + default: 'workerData', + }, }); const image = ref(null); @@ -70,7 +75,7 @@ const refetch = async () => await cardDescriptorRef.value.getData(); <CardDescriptor ref="cardDescriptorRef" module="Worker" - data-key="workerData" + :data-key="dataKey" url="Workers/descriptor" :filter="{ where: { id: entityId } }" title="user.nickname" diff --git a/src/pages/Worker/Card/WorkerDescriptorProxy.vue b/src/pages/Worker/Card/WorkerDescriptorProxy.vue index a142570f9..43deb7821 100644 --- a/src/pages/Worker/Card/WorkerDescriptorProxy.vue +++ b/src/pages/Worker/Card/WorkerDescriptorProxy.vue @@ -12,6 +12,11 @@ const $props = defineProps({ <template> <QPopupProxy> - <WorkerDescriptor v-if="$props.id" :id="$props.id" :summary="WorkerSummary" /> + <WorkerDescriptor + v-if="$props.id" + :id="$props.id" + :summary="WorkerSummary" + data-key="workerDescriptorProxy" + /> </QPopupProxy> </template> From 26de0cf72f4febeffa487d2b37202b6e1bc1df52 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 12:39:49 +0000 Subject: [PATCH 083/150] perf: remove limit --- src/components/CreateNewPostcodeForm.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/CreateNewPostcodeForm.vue b/src/components/CreateNewPostcodeForm.vue index afd3fc025..03cba8ac7 100644 --- a/src/components/CreateNewPostcodeForm.vue +++ b/src/components/CreateNewPostcodeForm.vue @@ -214,7 +214,6 @@ async function handleCountries(data) { /> <VnSelect url="Countries" - :limit="30" :sort-by="['name ASC']" :label="t('Country')" @update:options="handleCountries" From 855979d22d3397182210a661717880e7bb04ab2d Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 24 Oct 2024 14:59:14 +0200 Subject: [PATCH 084/150] perf: refs #7283 #7283 i18n params --- src/pages/Item/ItemRequest.vue | 1 + src/pages/Item/locale/en.yml | 13 +++++++++++++ src/pages/Item/locale/es.yml | 15 +++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index ea265e706..450031a0e 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -225,6 +225,7 @@ onMounted(async () => { :is-editable="true" auto-load :disable-option="{ card: true }" + chip-locale="item.params" > <template #column-ticketFk="{ row }"> <span class="link"> diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml index 53310cbd0..e99853760 100644 --- a/src/pages/Item/locale/en.yml +++ b/src/pages/Item/locale/en.yml @@ -89,6 +89,19 @@ itemType: category: Category temperature: Temperature item: + params: + daysOnward: Days onward + search: General search + ticketFk: Ticket id + attenderFk: Atender + clientFk: Client id + warehouseFk: Warehouse + requesterFk: Salesperson + from: From + to: To + mine: For me + state: State + myTeam: My team searchbar: label: Search item descriptor: diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml index 1ade9b955..56c6ec317 100644 --- a/src/pages/Item/locale/es.yml +++ b/src/pages/Item/locale/es.yml @@ -88,7 +88,22 @@ itemType: worker: Trabajador category: Reino temperature: Temperatura +params: + state: asfsdf item: + params: + daysOnward: Días adelante + search: Búsqueda general + ticketFk: Id ticket + attenderFk: Comprador + clientFk: Id cliente + warehouseFk: Almacén + requesterFk: Comercial + from: Desde + to: Hasta + mine: Para mi + state: Estado + myTeam: Mi equipo searchbar: label: Buscar artículo descriptor: From 92e147355fea8b083948f544daf608fc8db8734c Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Thu, 24 Oct 2024 15:13:05 +0200 Subject: [PATCH 085/150] fix: better performance --- src/components/VnTable/VnTable.vue | 7 ++++--- src/pages/Account/AccountAcls.vue | 28 +++++++++++++++++++++++++--- src/pages/Account/AccountFilter.vue | 1 + 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 908157610..c1680bf13 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -151,8 +151,8 @@ const tableModes = [ }, ]; onBeforeMount(() => { - setUserParams(route.query[$props.searchUrl]); - hasParams.value = params.value && Object.keys(params.value).length !== 0; + const urlParams = route.query[$props.searchUrl]; + hasParams.value = urlParams && Object.keys(urlParams).length !== 0; }); onMounted(() => { @@ -185,7 +185,8 @@ watch( watch( () => route.query[$props.searchUrl], - (val) => setUserParams(val) + (val) => setUserParams(val), + { immediate: true, deep: true } ); const isTableMode = computed(() => mode.value == TABLE_MODE); diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue index dd93a0cb5..63cdac9c7 100644 --- a/src/pages/Account/AccountAcls.vue +++ b/src/pages/Account/AccountAcls.vue @@ -9,6 +9,8 @@ import { useQuasar } from 'quasar'; import VnTable from 'components/VnTable/VnTable.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; import VnConfirm from 'components/ui/VnConfirm.vue'; +import FetchData from 'src/components/FetchData.vue'; +import { useValidator } from 'src/composables/useValidator'; defineProps({ id: { @@ -23,11 +25,18 @@ const stateStore = useStateStore(); const quasar = useQuasar(); const tableRef = ref(); - +const roles = ref(); +const validationsStore = useValidator(); +const { models } = validationsStore; const exprBuilder = (param, value) => { switch (param) { case 'search': - return { model: { like: `%${value}%` } }; + return { + or: [ + { model: { like: `%${value}%` } }, + { property: { like: `%${value}%` } }, + ], + }; default: return { [param]: value }; } @@ -47,6 +56,13 @@ const columns = computed(() => [ label: t('model'), cardVisible: true, create: true, + columnCreate: { + label: t('model'), + component: 'select', + attrs: { + options: Object.keys(models), + }, + }, }, { align: 'left', @@ -55,9 +71,10 @@ const columns = computed(() => [ cardVisible: true, component: 'select', attrs: { - url: 'VnRoles', + options: roles, optionLabel: 'name', optionValue: 'name', + inputDebounce: 0, }, create: true, }, @@ -130,6 +147,11 @@ const deleteAcl = async ({ id }) => { /> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> </QDrawer> + <FetchData + url="VnRoles?fields=['name']" + auto-load + @on-fetch="(data) => (roles = data)" + /> <VnTable ref="tableRef" data-key="AccountAcls" diff --git a/src/pages/Account/AccountFilter.vue b/src/pages/Account/AccountFilter.vue index 1775aa06b..3c8378d93 100644 --- a/src/pages/Account/AccountFilter.vue +++ b/src/pages/Account/AccountFilter.vue @@ -33,6 +33,7 @@ const rolesOptions = ref([]); :search-button="true" :hidden-tags="['search']" :redirect="false" + search-url="table" > <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> From 12c236276d1d321ae657a2a005630df210917af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es> Date: Thu, 24 Oct 2024 19:32:20 +0200 Subject: [PATCH 086/150] feat: refs #8087 Traspasar redadas a travels --- src/i18n/locale/en.yml | 3 +- src/i18n/locale/es.yml | 3 +- src/pages/Entry/Card/EntryDescriptor.vue | 19 +++++----- src/pages/Entry/EntryList.vue | 43 ++++++++++++---------- src/pages/Entry/locale/en.yml | 1 - src/pages/Entry/locale/es.yml | 1 - src/pages/Travel/Card/TravelBasicData.vue | 17 ++++++++- src/pages/Travel/Card/TravelDescriptor.vue | 17 +++++++++ src/pages/Travel/Card/TravelSummary.vue | 2 +- src/pages/Travel/TravelList.vue | 15 ++++++++ 10 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index d1ea5ceb9..928553293 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -106,7 +106,6 @@ globals: weight: Weight error: Ups! Something went wrong recalc: Recalculate - daysInForward: Forward days pageTitles: logIn: Login addressEdit: Update address @@ -313,6 +312,7 @@ globals: changePass: Change password deleteConfirmTitle: Delete selected elements changeState: Change state + raid: 'Raid {daysInForward} days' errors: statusUnauthorized: Access denied statusInternalServerError: An internal server error has ocurred @@ -1039,6 +1039,7 @@ travel: warehouseIn: Warehouse In delivered: Delivered received: Received + daysInForward: Days in forward thermographs: code: Code temperature: Temperature diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 18e580893..52bc821d1 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -108,7 +108,6 @@ globals: weight: Peso error: ¡Ups! Algo salió mal recalc: Recalcular - daysInForward: Días en el futuro pageTitles: logIn: Inicio de sesión addressEdit: Modificar consignatario @@ -317,6 +316,7 @@ globals: changePass: Cambiar contraseña deleteConfirmTitle: Eliminar los elementos seleccionados changeState: Cambiar estado + raid: 'Redada {daysInForward} días' errors: statusUnauthorized: Acceso denegado statusInternalServerError: Ha ocurrido un error interno del servidor @@ -1037,6 +1037,7 @@ travel: warehouseIn: Alm. entrada delivered: Enviada received: Recibida + daysInForward: Días redada thermographs: code: Código temperature: Temperatura diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue index d85e5e1a0..bbfc73138 100644 --- a/src/pages/Entry/Card/EntryDescriptor.vue +++ b/src/pages/Entry/Card/EntryDescriptor.vue @@ -7,7 +7,6 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import useCardDescription from 'src/composables/useCardDescription'; -import { useState } from 'src/composables/useState'; import { toDate } from 'src/filters'; import { usePrintService } from 'composables/usePrintService'; import { getUrl } from 'src/composables/getUrl'; @@ -23,7 +22,6 @@ const $props = defineProps({ const route = useRoute(); const { t } = useI18n(); const { openReport } = usePrintService(); -const state = useState(); const entryDescriptorRef = ref(null); const url = ref(); @@ -74,8 +72,6 @@ const data = ref(useCardDescription()); const setData = (entity) => (data.value = useCardDescription(entity.supplier?.nickname, entity.id)); -const currentEntry = computed(() => state.get('entry')); - const getEntryRedirectionFilter = (entry) => { let entryTravel = entry && entry.travel; @@ -132,24 +128,29 @@ watch; :value="entity.travel?.warehouseOut?.name" /> </template> - <template #icons> + <template #icons="{ entity }"> <QCardActions class="q-gutter-x-md"> <QIcon - v-if="currentEntry?.isExcludedFromAvailable" + v-if="entity?.isExcludedFromAvailable" name="vn:inventory" color="primary" size="xs" > <QTooltip>{{ t('Inventory entry') }}</QTooltip> </QIcon> - {{ console.log('currentEntry', currentEntry) }} <QIcon - v-if="currentEntry?.travel?.daysInForward" + v-if="entity?.travel?.daysInForward" name="vn:net" color="primary" size="xs" > - <QTooltip>{{ t('Virtual entry') }}</QTooltip> + <QTooltip> + {{ + t('globals.raid', { + daysInForward: entity?.travel?.daysInForward, + }) + }}</QTooltip + > </QIcon> </QCardActions> </template> diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue index 00b6c8626..518d72d82 100644 --- a/src/pages/Entry/EntryList.vue +++ b/src/pages/Entry/EntryList.vue @@ -41,6 +41,10 @@ const entryFilter = { }; const columns = computed(() => [ + { + name: 'status', + hidden: true, + }, { align: 'left', label: t('entry.list.tableVisibleColumns.id'), @@ -154,27 +158,8 @@ const columns = computed(() => [ cardVisible: true, }, { - align: 'left', label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'), name: 'isExcludedFromAvailable', - chip: { - color: null, - condition: (value) => value, - icon: 'vn:inventory', - }, - columnFilter: { - inWhere: true, - }, - }, - { - align: 'left', - label: t('entry.list.tableVisibleColumns.daysInForward'), - name: 'daysInForward', - chip: { - color: null, - condition: (value) => value, - icon: 'vn:net', - }, columnFilter: { inWhere: true, }, @@ -225,6 +210,26 @@ onMounted(async () => { auto-load :right-search="false" > + <template #column-status="{ row }"> + <div class="row q-gutter-xs"> + <QIcon + v-if="!!row.isExcludedFromAvailable" + name="vn:inventory" + color="primary" + > + <QTooltip>{{ + t('entry.list.tableVisibleColumns.isExcludedFromAvailable') + }}</QTooltip> + </QIcon> + <QIcon v-if="!!row.daysInForward" name="vn:net" color="primary"> + <QTooltip> + {{ + t('globals.raid', { daysInForward: row.daysInForward }) + }}</QTooltip + > + </QIcon> + </div> + </template> <template #column-supplierFk="{ row }"> <span class="link" @click.stop> {{ row.supplierName }} diff --git a/src/pages/Entry/locale/en.yml b/src/pages/Entry/locale/en.yml index f9dbd0589..cd5113d84 100644 --- a/src/pages/Entry/locale/en.yml +++ b/src/pages/Entry/locale/en.yml @@ -1,7 +1,6 @@ entryList: list: inventoryEntry: Inventory entry - virtualEntry: Virtual entry entryFilter: filter: search: General search diff --git a/src/pages/Entry/locale/es.yml b/src/pages/Entry/locale/es.yml index feeea1fc9..3007c5d44 100644 --- a/src/pages/Entry/locale/es.yml +++ b/src/pages/Entry/locale/es.yml @@ -4,7 +4,6 @@ You can search by entry reference: Puedes buscar por referencia de la entrada entryList: list: inventoryEntry: Es inventario - virtualEntry: Es una redada entryFilter: filter: search: Búsqueda general diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue index aaf8abb91..d6245e655 100644 --- a/src/pages/Travel/Card/TravelBasicData.vue +++ b/src/pages/Travel/Card/TravelBasicData.vue @@ -73,9 +73,15 @@ const agenciesOptions = ref([]); <VnRow> <VnInput v-model="data.m3" label="m3" /> <VnInput - :label="t('globals.daysInForward')" + :label="t('travel.basicData.daysInForward')" v-model="data.daysInForward" - /> + > + <template #append> + <QIcon name="info" class="cursor-info"> + <QTooltip>{{ t('raidDays') }}</QTooltip> + </QIcon> + </template> + </VnInput> </VnRow> <VnRow> <QCheckbox @@ -90,3 +96,10 @@ const agenciesOptions = ref([]); </template> </FormModel> </template> + +<i18n> +es: + raidDays: Al rellenarlo, generamos una redada. Indica los días que un travel se moverá automáticamente en el tiempo +en: + raidDays: When filling, a raid is generated. Enter the number of days the travel will automatically forward in time +</i18n> diff --git a/src/pages/Travel/Card/TravelDescriptor.vue b/src/pages/Travel/Card/TravelDescriptor.vue index bda29903b..6025ad045 100644 --- a/src/pages/Travel/Card/TravelDescriptor.vue +++ b/src/pages/Travel/Card/TravelDescriptor.vue @@ -32,6 +32,7 @@ const filter = { 'warehouseOutFk', 'cargoSupplierFk', 'agencyModeFk', + 'daysInForward', ], include: [ { @@ -77,6 +78,22 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity. <VnLv :label="t('globals.landed')" :value="toDate(entity.landed)" /> <VnLv :label="t('globals.totalEntries')" :value="entity.totalEntries" /> </template> + <template #icons="{ entity }"> + <QCardActions class="q-gutter-x-md"> + <QIcon + v-if="entity.daysInForward" + name="vn:net" + color="primary" + size="xs" + > + <QTooltip> + {{ + t('globals.raid', { daysInForward: entity.daysInForward }) + }}</QTooltip + > + </QIcon> + </QCardActions> + </template> <template #actions="{ entity }"> <QCardActions> <QBtn diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue index 59059b0db..7dc267671 100644 --- a/src/pages/Travel/Card/TravelSummary.vue +++ b/src/pages/Travel/Card/TravelSummary.vue @@ -304,7 +304,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`; <VnLv label="m³" :value="travel.m3" /> <VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" /> <VnLv - :label="t('globals.daysInForward')" + :label="t('travel.basicData.daysInForward')" :value="travel?.daysInForward" /> </QCard> diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue index a8c0e69cb..199746bb7 100644 --- a/src/pages/Travel/TravelList.vue +++ b/src/pages/Travel/TravelList.vue @@ -45,6 +45,10 @@ const redirectCreateEntryView = (travelData) => { }; const columns = computed(() => [ + { + name: 'status', + hidden: true, + }, { align: 'left', name: 'id', @@ -221,6 +225,17 @@ const columns = computed(() => [ :is-editable="false" :use-model="true" > + <template #column-status="{ row }"> + <div class="row"> + <QIcon v-if="!!row.daysInForward" name="vn:net" color="primary"> + <QTooltip> + {{ + t('globals.raid', { daysInForward: row.daysInForward }) + }}</QTooltip + > + </QIcon> + </div> + </template> <template #column-shipped="{ row }"> <QBadge text-color="black" From 994237260ed24fef644c6fdc5c4091a52607f89d Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 25 Oct 2024 06:48:09 +0200 Subject: [PATCH 087/150] refactor: changed name of emitted event --- src/pages/Ticket/Card/TicketSale.vue | 2 +- src/pages/Ticket/Card/TicketSaleMoreActions.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index 3e89e96bf..798d629c7 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -476,7 +476,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :ticket-config="ticketConfig" @get-mana="getMana()" @update-discounts="updateDiscount" - @re-calculated-price="resetChanges" + @refresh-table="resetChanges" /> <QBtn color="primary" diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 43ba39b91..221cf6fad 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -14,7 +14,7 @@ import { toDateFormat } from 'src/filters/date'; import { useRole } from 'src/composables/useRole'; import { useVnConfirm } from 'composables/useVnConfirm'; -const emit = defineEmits(['updateDiscounts', 'getMana', 'reCalculatedPrice']); +const emit = defineEmits(['updateDiscounts', 'getMana', 'refreshTable']); const props = defineProps({ disable: { @@ -107,7 +107,7 @@ const calculateSalePrice = async () => { await axios.post(`Sales/recalculatePrice`, props.sales); notify(t('globals.dataSaved'), 'positive'); - emit('reCalculatedPrice', props.sales); + emit('refreshTable', props.sales); }; const changeMultipleDiscount = () => { From e8d2a40dafbc8ccec24957715bf6cfea82e4bc25 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Fri, 25 Oct 2024 08:11:31 +0200 Subject: [PATCH 088/150] fix: entryFilters --- src/pages/Entry/EntryLatestBuys.vue | 65 ++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/pages/Entry/EntryLatestBuys.vue b/src/pages/Entry/EntryLatestBuys.vue index 61c430b23..119808176 100644 --- a/src/pages/Entry/EntryLatestBuys.vue +++ b/src/pages/Entry/EntryLatestBuys.vue @@ -23,7 +23,6 @@ const columns = [ return { id: row.id, size: '50x50', - width: '50px', }; }, }, @@ -34,21 +33,37 @@ const columns = [ label: t('entry.latestBuys.tableVisibleColumns.itemFk'), name: 'itemFk', isTitle: true, + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.packing'), name: 'packing', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.grouping'), name: 'grouping', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.quantity'), name: 'quantity', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', @@ -59,6 +74,10 @@ const columns = [ align: 'left', label: t('entry.latestBuys.tableVisibleColumns.size'), name: 'size', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', @@ -84,6 +103,10 @@ const columns = [ align: 'left', label: t('entry.latestBuys.tableVisibleColumns.weightByPiece'), name: 'weightByPiece', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', @@ -99,26 +122,46 @@ const columns = [ align: 'left', label: t('entry.latestBuys.tableVisibleColumns.entryFk'), name: 'entryFk', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.buyingValue'), name: 'buyingValue', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.freightValue'), name: 'freightValue', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.comissionValue'), name: 'comissionValue', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.packageValue'), name: 'packageValue', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', @@ -129,16 +172,28 @@ const columns = [ align: 'left', label: t('entry.latestBuys.tableVisibleColumns.price2'), name: 'price2', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.price3'), name: 'price3', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.minPrice'), name: 'minPrice', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', @@ -149,11 +204,19 @@ const columns = [ align: 'left', label: t('entry.latestBuys.tableVisibleColumns.weight'), name: 'weight', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', label: t('entry.latestBuys.tableVisibleColumns.packagingFk'), name: 'packagingFk', + columnFilter: { + component: 'number', + inWhere: true, + }, }, { align: 'left', From 1daa8be63597f770ae0d0a9a7588628622556c00 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 25 Oct 2024 09:41:28 +0200 Subject: [PATCH 089/150] perf: optimize get packing type function --- .../components/CustomerSummaryTable.vue | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue index ff1474fb9..e9bb36be7 100644 --- a/src/pages/Customer/components/CustomerSummaryTable.vue +++ b/src/pages/Customer/components/CustomerSummaryTable.vue @@ -152,17 +152,20 @@ const setShippedColor = (date) => { }; const getItemPackagingType = (ticketSales) => { - if (!ticketSales || ticketSales.length === 0) return '-'; + if (!ticketSales?.length) return '-'; - const packagingTypes = ticketSales - .map((sale) => sale.item?.itemPackingTypeFk) - .filter((type) => type !== undefined && type !== null); + const packagingTypes = ticketSales.reduce((types, sale) => { + const { itemPackingTypeFk } = sale.item; + if ( + !types.includes(itemPackingTypeFk) && + (itemPackingTypeFk === 'H' || itemPackingTypeFk === 'V') + ) { + types.push(itemPackingTypeFk); + } + return types; + }, []); - const uniquePackagingTypes = [...new Set(packagingTypes)]; - - return dashIfEmpty( - uniquePackagingTypes.length > 0 ? uniquePackagingTypes.join(', ') : '-' - ); + return dashIfEmpty(packagingTypes.join(', ') || '-'); }; </script> From 4c1e4aedd2099bd08526c2a51d7d9308627a01dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es> Date: Fri, 25 Oct 2024 10:03:09 +0200 Subject: [PATCH 090/150] feat: refs #8087 Traspasar redadas a travels --- src/pages/Entry/Card/EntryBasicData.vue | 6 ++++++ src/pages/Entry/Card/EntryDescriptor.vue | 8 ++++++++ src/pages/Entry/EntryList.vue | 2 +- src/pages/Travel/TravelList.vue | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue index d60ed8645..b81b1db22 100644 --- a/src/pages/Entry/Card/EntryBasicData.vue +++ b/src/pages/Entry/Card/EntryBasicData.vue @@ -168,6 +168,12 @@ const onFilterTravelSelected = (formData, id) => { v-model="data.isExcludedFromAvailable" :label="t('entry.basicData.excludedFromAvailable')" /> + <QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" /> + <QCheckbox + v-if="isAdministrative()" + v-model="data.isBooked" + :label="t('entry.basicData.booked')" + /> </VnRow> </template> </FormModel> diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue index bbfc73138..d66185aa9 100644 --- a/src/pages/Entry/Card/EntryDescriptor.vue +++ b/src/pages/Entry/Card/EntryDescriptor.vue @@ -30,6 +30,14 @@ const entryFilter = { { relation: 'travel', scope: { + fields: [ + 'id', + 'landed', + 'shipped', + 'agencyModeFk', + 'warehouseOutFk', + 'daysInForward', + ], include: [ { relation: 'agency', diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue index 518d72d82..e9179c239 100644 --- a/src/pages/Entry/EntryList.vue +++ b/src/pages/Entry/EntryList.vue @@ -43,7 +43,7 @@ const entryFilter = { const columns = computed(() => [ { name: 'status', - hidden: true, + columnFilter: false, }, { align: 'left', diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue index 199746bb7..334640bff 100644 --- a/src/pages/Travel/TravelList.vue +++ b/src/pages/Travel/TravelList.vue @@ -47,7 +47,7 @@ const redirectCreateEntryView = (travelData) => { const columns = computed(() => [ { name: 'status', - hidden: true, + columnFilter: false, }, { align: 'left', From 9e57c5e4527b8ffade7be3d0a02889d0c2550346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es> Date: Fri, 25 Oct 2024 10:13:09 +0200 Subject: [PATCH 091/150] feat: refs #8087 Traspasar redadas a travels --- src/pages/Entry/Card/EntryBasicData.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue index b81b1db22..3288616fb 100644 --- a/src/pages/Entry/Card/EntryBasicData.vue +++ b/src/pages/Entry/Card/EntryBasicData.vue @@ -168,7 +168,6 @@ const onFilterTravelSelected = (formData, id) => { v-model="data.isExcludedFromAvailable" :label="t('entry.basicData.excludedFromAvailable')" /> - <QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" /> <QCheckbox v-if="isAdministrative()" v-model="data.isBooked" From ff559384aad8a7d59a9ebfcb7263ffd93cc40c60 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Fri, 25 Oct 2024 10:33:59 +0200 Subject: [PATCH 092/150] perf: refs #6943 #6943 merge command --- .../route/agency/agencyWorkCenter.spec.js | 6 +++--- .../worker/workerNotificationsManager.spec.js | 4 +++- test/cypress/support/commands.js | 20 ++++++++----------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js index 6a3cab664..311c0130c 100644 --- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js +++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js @@ -11,16 +11,16 @@ describe('AgencyWorkCenter', () => { // create cy.get(createButton).click(); cy.get(workCenterCombobox).type('workCenterOne{enter}'); - cy.hasNotify('Data created'); + cy.checkNotification('created'); // expect error when duplicate cy.get(createButton).click(); cy.get('[data-cy="FormModelPopup_save"]').click(); - cy.hasNotify('This workCenter is already assigned to this agency'); + cy.checkNotification('This workCenter is already assigned to this agency'); cy.get('[data-cy="FormModelPopup_cancel"]').click(); // delete cy.get('.q-item__section--side > .q-btn > .q-btn__content > .q-icon').click(); - cy.hasNotify('WorkCenter removed successfully'); + cy.checkNotification('WorkCenter removed successfully'); }); }); diff --git a/test/cypress/integration/worker/workerNotificationsManager.spec.js b/test/cypress/integration/worker/workerNotificationsManager.spec.js index 367287a5a..f121b3894 100644 --- a/test/cypress/integration/worker/workerNotificationsManager.spec.js +++ b/test/cypress/integration/worker/workerNotificationsManager.spec.js @@ -17,7 +17,9 @@ describe('WorkerNotificationsManager', () => { cy.login('developer'); cy.visit(`/#/worker/${salesPersonId}/notifications`); cy.get(firstAvailableNotification).click(); - cy.hasNotify('The notification subscription of this worker cant be modified'); + cy.checkNotification( + 'The notification subscription of this worker cant be modified' + ); }); it('should active a notification that is yours', () => { diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index dcfb54a3e..7d5a44f78 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -274,21 +274,13 @@ Cypress.Commands.add('openActions', (row) => { cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); }); -Cypress.Commands.add('checkNotification', (type) => { - const values = { +Cypress.Commands.add('checkNotification', (tag) => { + const defaultTags = { created: 'Data created', updated: 'Data saved', deleted: 'Data deleted', - }; - cy.get('.q-notification__message').should('have.text', values[type]); -}); - -Cypress.Commands.add('searchByLabel', (label, value) => { - cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`); -}); - -Cypress.Commands.add('hasNotify', (text) => { - //last + }; //last + const text = defaultTags[tag] ?? tag; cy.get('.q-notification') .should('be.visible') .last() @@ -297,3 +289,7 @@ Cypress.Commands.add('hasNotify', (text) => { throw new Error(`Notification not found: "${text}"`); }); }); + +Cypress.Commands.add('searchByLabel', (label, value) => { + cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`); +}); From 02f682ac0b5d153c43339812b41fab0605c861c3 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Fri, 25 Oct 2024 10:53:19 +0200 Subject: [PATCH 093/150] perf: refs #6943 #6943 merge command --- test/cypress/integration/client/clientList.spec.js | 2 +- .../integration/route/agency/agencyWorkCenter.spec.js | 2 +- test/cypress/support/commands.js | 8 +------- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index f150fc190..22bca15ac 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -28,7 +28,7 @@ describe('Client list', () => { cy.get('.q-mt-lg > .q-btn--standard').click(); - cy.checkNotification('created'); + cy.checkNotification('Data created'); cy.url().should('include', '/summary'); }); it('Client list search client', () => { diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js index 311c0130c..fdfcd4286 100644 --- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js +++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js @@ -11,7 +11,7 @@ describe('AgencyWorkCenter', () => { // create cy.get(createButton).click(); cy.get(workCenterCombobox).type('workCenterOne{enter}'); - cy.checkNotification('created'); + cy.checkNotification('Data created'); // expect error when duplicate cy.get(createButton).click(); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 7d5a44f78..c9b1a748e 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -274,13 +274,7 @@ Cypress.Commands.add('openActions', (row) => { cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); }); -Cypress.Commands.add('checkNotification', (tag) => { - const defaultTags = { - created: 'Data created', - updated: 'Data saved', - deleted: 'Data deleted', - }; //last - const text = defaultTags[tag] ?? tag; +Cypress.Commands.add('checkNotification', (text) => { cy.get('.q-notification') .should('be.visible') .last() From bb51a9e6870af70d95a0b70c6e63e49c1633b749 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Fri, 25 Oct 2024 11:38:00 +0200 Subject: [PATCH 094/150] fix: refs #7943 use summary --- src/pages/Worker/Card/WorkerCard.vue | 2 +- src/pages/Worker/Card/WorkerSummary.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Worker/Card/WorkerCard.vue b/src/pages/Worker/Card/WorkerCard.vue index 5f9fa0f8e..428731a5c 100644 --- a/src/pages/Worker/Card/WorkerCard.vue +++ b/src/pages/Worker/Card/WorkerCard.vue @@ -6,7 +6,7 @@ import WorkerFilter from '../WorkerFilter.vue'; <template> <VnCard data-key="Worker" - base-url="Workers" + custom-url="Workers/summary" :descriptor="WorkerDescriptor" :filter-panel="WorkerFilter" search-data-key="WorkerList" diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue index ed34e771d..4a78afb52 100644 --- a/src/pages/Worker/Card/WorkerSummary.vue +++ b/src/pages/Worker/Card/WorkerSummary.vue @@ -45,7 +45,7 @@ onBeforeMount(async () => { ref="summary" :url="`Workers/summary`" :filter="{ where: { id: entityId } }" - data-key="WorkerSummary" + data-key="Worker" > <template #header="{ entity }"> <div>{{ entity.id }} - {{ entity.firstName }} {{ entity.lastName }}</div> From cdb73f7a17588b70d628af92d349ece882aa336b Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Fri, 25 Oct 2024 11:50:20 +0200 Subject: [PATCH 095/150] fix: refs #7943 use correct data-key --- src/pages/Worker/WorkerList.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/Worker/WorkerList.vue b/src/pages/Worker/WorkerList.vue index 7a3f760bc..022cecdc6 100644 --- a/src/pages/Worker/WorkerList.vue +++ b/src/pages/Worker/WorkerList.vue @@ -169,7 +169,7 @@ async function autofillBic(worker) { </script> <template> <VnSearchbar - data-key="Worker" + data-key="WorkerList" :label="t('Search worker')" :info="t('You can search by worker id or name')" /> @@ -191,13 +191,13 @@ async function autofillBic(worker) { /> <RightMenu> <template #right-panel> - <WorkerFilter data-key="Worker" /> + <WorkerFilter data-key="WorkerList" /> </template> </RightMenu> <VnTable v-if="defaultPayMethod" ref="tableRef" - data-key="Worker" + data-key="WorkerList" url="Workers/filter" :create="{ urlCreate: 'Workers/new', From 5b24aa457074fbf0f153dadfe49c4b15c805e0cb Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 25 Oct 2024 12:58:23 +0200 Subject: [PATCH 096/150] fix: performance addEventListeners --- src/boot/mainShortcutMixin.js | 38 ----------------------------- src/boot/quasar.js | 2 -- src/components/VnTable/VnColumn.vue | 2 +- src/components/VnTable/VnFilter.vue | 2 +- src/layouts/MainLayout.vue | 37 ++++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 42 deletions(-) delete mode 100644 src/boot/mainShortcutMixin.js diff --git a/src/boot/mainShortcutMixin.js b/src/boot/mainShortcutMixin.js deleted file mode 100644 index 8e5f147db..000000000 --- a/src/boot/mainShortcutMixin.js +++ /dev/null @@ -1,38 +0,0 @@ -import routes from 'src/router/modules'; -import { useRouter } from 'vue-router'; - -let isNotified = false; - -export default { - created: function () { - const router = useRouter(); - const keyBindingMap = routes - .filter((route) => route.meta.keyBinding) - .reduce((map, route) => { - map['Key' + route.meta.keyBinding.toUpperCase()] = route.path; - return map; - }, {}); - - const handleKeyDown = (event) => { - const { ctrlKey, altKey, code } = event; - - if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) { - event.preventDefault(); - router.push(keyBindingMap[code]); - isNotified = true; - } - }; - - const handleKeyUp = (event) => { - const { ctrlKey, altKey } = event; - - // Resetea la bandera cuando se sueltan las teclas ctrl o alt - if (!ctrlKey || !altKey) { - isNotified = false; - } - }; - - window.addEventListener('keydown', handleKeyDown); - window.addEventListener('keyup', handleKeyUp); - }, -}; diff --git a/src/boot/quasar.js b/src/boot/quasar.js index 5db6edd24..f39d1f0be 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -1,13 +1,11 @@ import { boot } from 'quasar/wrappers'; import qFormMixin from './qformMixin'; -import mainShortcutMixin from './mainShortcutMixin'; import keyShortcut from './keyShortcut'; import useNotify from 'src/composables/useNotify.js'; const { notify } = useNotify(); export default boot(({ app }) => { app.mixin(qFormMixin); - app.mixin(mainShortcutMixin); app.directive('shortcut', keyShortcut); app.config.errorHandler = function (err) { console.error(err); diff --git a/src/components/VnTable/VnColumn.vue b/src/components/VnTable/VnColumn.vue index ed34e9eee..9e9bfad69 100644 --- a/src/components/VnTable/VnColumn.vue +++ b/src/components/VnTable/VnColumn.vue @@ -1,5 +1,5 @@ <script setup> -import { markRaw, computed, defineModel } from 'vue'; +import { markRaw, computed } from 'vue'; import { QIcon, QCheckbox } from 'quasar'; import { dashIfEmpty } from 'src/filters'; diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue index b17fd4407..9da0b26a4 100644 --- a/src/components/VnTable/VnFilter.vue +++ b/src/components/VnTable/VnFilter.vue @@ -1,5 +1,5 @@ <script setup> -import { markRaw, computed, defineModel } from 'vue'; +import { markRaw, computed } from 'vue'; import { QCheckbox } from 'quasar'; import { useArrayData } from 'composables/useArrayData'; diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 51cd20071..754b084fc 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -1,7 +1,44 @@ <script setup> import { useQuasar } from 'quasar'; import Navbar from 'src/components/NavBar.vue'; +import { useRouter } from 'vue-router'; +import routes from 'src/router/modules'; +import { onMounted } from 'vue'; + const quasar = useQuasar(); + +onMounted(() => { + let isNotified = false; + + const router = useRouter(); + const keyBindingMap = routes + .filter((route) => route.meta.keyBinding) + .reduce((map, route) => { + map['Key' + route.meta.keyBinding.toUpperCase()] = route.path; + return map; + }, {}); + + const handleKeyDown = (event) => { + const { ctrlKey, altKey, code } = event; + + if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) { + event.preventDefault(); + router.push(keyBindingMap[code]); + isNotified = true; + } + }; + + const handleKeyUp = (event) => { + const { ctrlKey, altKey } = event; + + if (!ctrlKey || !altKey) { + isNotified = false; + } + }; + + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); +}); </script> <template> From 8edba36a7401bad871b994d2e6f2a870c8ed92d0 Mon Sep 17 00:00:00 2001 From: guillermo <guillermo@verdnatura.es> Date: Fri, 25 Oct 2024 13:46:36 +0200 Subject: [PATCH 097/150] feat: refs #7006 itemType basic data new inputs --- src/pages/ItemType/Card/ItemTypeBasicData.vue | 36 ++++++++++++++++++- src/pages/ItemType/locale/en.yml | 3 ++ src/pages/ItemType/locale/es.yml | 3 ++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue index f4013e126..e447f565b 100644 --- a/src/pages/ItemType/Card/ItemTypeBasicData.vue +++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue @@ -8,12 +8,14 @@ import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; +import VnAvatar from 'src/components/ui/VnAvatar.vue'; const route = useRoute(); const { t } = useI18n(); const categoriesOptions = ref([]); const temperaturesOptions = ref([]); +const itemPackingTypesOptions = ref([]); </script> <template> <FetchData @@ -28,6 +30,16 @@ const temperaturesOptions = ref([]); :filter="{ order: 'name ASC', fields: ['code', 'name'] }" auto-load /> + <FetchData + url="ItemPackingTypes" + @on-fetch="(data) => (itemPackingTypesOptions = data)" + :filter="{ + where: { isActive: true }, + order: 'description ASC', + fields: ['code', 'description'], + }" + auto-load + /> <FormModel :url="`ItemTypes/${route.params.id}`" :url-update="`ItemTypes/${route.params.id}`" @@ -50,7 +62,15 @@ const temperaturesOptions = ref([]); option-label="nickname" option-value="id" hide-selected - ><template #option="scope"> + > + <template #prepend> + <VnAvatar + :worker-id="data.workerFk" + color="primary" + :title="title" + /> + </template> + <template #option="scope"> <QItem v-bind="scope.itemProps"> <QItemSection> <QItemLabel>{{ scope.opt?.name }}</QItemLabel> @@ -82,6 +102,20 @@ const temperaturesOptions = ref([]); /> <VnInput v-model="data.life" :label="t('shared.life')" /> </VnRow> + <VnRow> + <VnSelect + v-model="data.itemPackingTypeFk" + :label="t('shared.itemPackingType')" + :options="itemPackingTypesOptions" + option-value="code" + option-label="description" + hide-selected + /> + <VnInput v-model="data.maxRefs" :label="t('shared.maxRefs')" /> + </VnRow> + <VnRow> + <QCheckbox v-model="data.isFragile" :label="t('shared.fragile')" /> + </VnRow> </template> </FormModel> </template> diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/ItemType/locale/en.yml index 4b203bd68..575d5e402 100644 --- a/src/pages/ItemType/locale/en.yml +++ b/src/pages/ItemType/locale/en.yml @@ -5,6 +5,9 @@ shared: category: Category temperature: Temperature life: Life + itemPackingType: Item packing type + maxRefs: Maximum references + fragile: Fragile summary: id: id life: Life diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/ItemType/locale/es.yml index 43699c332..09ee5a1f7 100644 --- a/src/pages/ItemType/locale/es.yml +++ b/src/pages/ItemType/locale/es.yml @@ -5,6 +5,9 @@ shared: category: Reino temperature: Temperatura life: Vida + itemPackingType: Tipo de embalaje + maxRefs: Referencias máximas + fragile: Frágil summary: id: id code: Código From 494fc66c06377838c2d23d6f6cf945c91c753f67 Mon Sep 17 00:00:00 2001 From: guillermo <guillermo@verdnatura.es> Date: Fri, 25 Oct 2024 14:14:51 +0200 Subject: [PATCH 098/150] feat: refs #7006 itemType basic data new inputs --- src/pages/Item/ItemTypeList.vue | 15 ++++----------- src/pages/ItemType/Card/ItemTypeBasicData.vue | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 2c1153016..13dd56a42 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -7,7 +7,6 @@ import FetchData from 'components/FetchData.vue'; const { t } = useI18n(); const tableRef = ref(); -const workerOptions = ref([]); const ItemCategoriesOptions = ref([]); const columns = computed(() => [ @@ -40,12 +39,12 @@ const columns = computed(() => [ create: true, component: 'select', attrs: { - options: workerOptions.value, - optionLabel: 'firstName', + url: 'Workers/search', + optionLabel: 'nickname', optionValue: 'id', }, - cardVisible: false, - visible: false, + cardVisible: true, + visible: true, }, { align: 'left', @@ -78,12 +77,6 @@ const columns = computed(() => [ </script> <template> - <FetchData - url="Workers" - :filter="{ fields: ['id', 'firstName'], order: ['firstName ASC'] }" - @on-fetch="(data) => (workerOptions = data)" - auto-load - /> <FetchData url="ItemCategories" :filter="{ fields: ['id', 'name'], order: ['name ASC'] }" diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue index e447f565b..1a4a7c9f3 100644 --- a/src/pages/ItemType/Card/ItemTypeBasicData.vue +++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue @@ -58,7 +58,6 @@ const itemPackingTypesOptions = ref([]); :label="t('shared.worker')" sort-by="nickname ASC" :fields="['id', 'nickname']" - :params="{ departmentCodes: ['shopping'] }" option-label="nickname" option-value="id" hide-selected From 1da86900e653a072bbe898f2d75997a5851c59be Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Sun, 27 Oct 2024 13:43:38 +0100 Subject: [PATCH 099/150] fix: refs #7524 e2e & worker module --- src/components/ui/VnRow.vue | 3 -- src/composables/useAdvancedSummary.js | 11 ++++++ src/pages/Ticket/Card/TicketExpedition.vue | 1 + src/pages/Ticket/TicketAdvance.vue | 1 - src/pages/Worker/Card/WorkerBasicData.vue | 36 +++++++++---------- src/pages/Worker/Card/WorkerLocker.vue | 3 +- src/pages/Worker/Card/WorkerSummary.vue | 32 ++++++++--------- src/router/modules/worker.js | 7 ++++ .../ticket/ticketExpedition.spec.js | 12 +++---- .../integration/worker/workerList.spec.js | 2 +- .../integration/worker/workerLocker.spec.js | 11 +++--- test/cypress/support/commands.js | 2 +- 12 files changed, 63 insertions(+), 58 deletions(-) create mode 100644 src/composables/useAdvancedSummary.js diff --git a/src/components/ui/VnRow.vue b/src/components/ui/VnRow.vue index 16bcfab7d..40dabf610 100644 --- a/src/components/ui/VnRow.vue +++ b/src/components/ui/VnRow.vue @@ -1,6 +1,3 @@ -<script setup> -defineProps({ wrap: { type: Boolean, default: false } }); -</script> <template> <div class="vn-row q-gutter-md q-mb-md"> <slot /> diff --git a/src/composables/useAdvancedSummary.js b/src/composables/useAdvancedSummary.js new file mode 100644 index 000000000..98b998d2a --- /dev/null +++ b/src/composables/useAdvancedSummary.js @@ -0,0 +1,11 @@ +import axios from 'axios'; +import { useRole } from './useRole'; + +export async function useAdvancedSummary(model, id, roles = ['hr']) { + if (useRole().hasAny(roles)) { + const { data } = await axios.get(`${model}/advancedSummary`, { + params: { filter: { where: { id } } }, + }); + return Array.isArray(data) ? data[0] : data; + } +} diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue index 8041ad069..d682d0408 100644 --- a/src/pages/Ticket/Card/TicketExpedition.vue +++ b/src/pages/Ticket/Card/TicketExpedition.vue @@ -211,6 +211,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <template #st-actions> <QBtnGroup push class="q-gutter-x-sm" flat> <VnBtnSelect + data-cy="change-state" :disable="!hasSelectedRows" color="primary" :label="t('globals.changeState')" diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index 7db2b54b3..bdd980c07 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.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 VnProgress from 'src/components/common/VnProgressModal.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; import TicketAdvanceFilter from './TicketAdvanceFilter.vue'; diff --git a/src/pages/Worker/Card/WorkerBasicData.vue b/src/pages/Worker/Card/WorkerBasicData.vue index d131fea3e..6a13e3f39 100644 --- a/src/pages/Worker/Card/WorkerBasicData.vue +++ b/src/pages/Worker/Card/WorkerBasicData.vue @@ -1,5 +1,5 @@ <script setup> -import { ref } from 'vue'; +import { ref, onBeforeMount } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import VnInputDate from 'src/components/common/VnInputDate.vue'; @@ -8,32 +8,22 @@ import FormModel from 'src/components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; +import { useAdvancedSummary } from 'src/composables/useAdvancedSummary'; -const route = useRoute(); const { t } = useI18n(); - const educationLevels = ref([]); const countries = ref([]); const maritalStatus = [ { code: 'M', name: t('Married') }, { code: 'S', name: t('Single') }, ]; +const advancedSummary = ref({}); -const workerFilter = { - include: [ - { - relation: 'user', - scope: { - fields: ['name', 'emailVerified'], - include: { relation: 'emailUser', scope: { fields: ['email'] } }, - }, - }, - { relation: 'sip', scope: { fields: ['extension', 'secret'] } }, - { relation: 'department', scope: { include: { relation: 'department' } } }, - ], -}; +onBeforeMount(async () => { + advancedSummary.value = + (await useAdvancedSummary('Workers', +useRoute().params.id)) ?? {}; +}); </script> - <template> <FetchData :filter="{ fields: ['id', 'name'], order: 'name ASC' }" @@ -48,10 +38,16 @@ const workerFilter = { auto-load /> <FormModel - :filter="workerFilter" - :url="`Workers/${route.params.id}`" + :filter="{ where: { id: +$route.params.id } }" + url="Workers/summary" + :url-update="`Workers/${$route.params.id}`" auto-load model="Worker" + @on-fetch=" + async (data) => { + Object.assign(data, advancedSummary); + } + " > <template #form="{ data }"> <VnRow> @@ -134,7 +130,7 @@ const workerFilter = { <VnInput v-model="data.fi" :label="t('fi')" /> <VnInputDate :label="t('birth')" v-model="data.birth" /> </VnRow> - <VnRow> + <VnRow wrap> <QCheckbox size="sm" :label="t('isFreelance')" diff --git a/src/pages/Worker/Card/WorkerLocker.vue b/src/pages/Worker/Card/WorkerLocker.vue index 4a19e472c..015bced35 100644 --- a/src/pages/Worker/Card/WorkerLocker.vue +++ b/src/pages/Worker/Card/WorkerLocker.vue @@ -18,7 +18,7 @@ const { store } = useArrayData('Worker'); const entityId = computed(() => useRoute().params.id); const filter = computed(() => ({ where: { - gender: store.data?.sex, + gender: store.data?.[0]?.sex, or: [{ workerFk: null }, { workerFk: entityId.value }], }, })); @@ -51,6 +51,7 @@ const init = async (data) => { > <template #form="{ data }"> <VnSelect + data-cy="locker" :label="t('Locker')" v-model="data.id" :options="lockers" diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue index 4a78afb52..0a0694fdf 100644 --- a/src/pages/Worker/Card/WorkerSummary.vue +++ b/src/pages/Worker/Card/WorkerSummary.vue @@ -2,7 +2,6 @@ import { ref, onBeforeMount, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import axios from 'axios'; import { dashIfEmpty, toDate } from 'src/filters'; import VnLv from 'src/components/ui/VnLv.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; @@ -11,7 +10,7 @@ import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnTitle from 'src/components/common/VnTitle.vue'; import RoleDescriptorProxy from 'src/pages/Account/Role/Card/RoleDescriptorProxy.vue'; import DepartmentDescriptorProxy from 'src/pages/Department/Card/DepartmentDescriptorProxy.vue'; -import { useRole } from 'src/composables/useRole'; +import { useAdvancedSummary } from 'src/composables/useAdvancedSummary'; const route = useRoute(); const { t } = useI18n(); @@ -25,18 +24,11 @@ const $props = defineProps({ const entityId = computed(() => $props.id || route.params.id); const basicDataUrl = ref(null); -const isHr = computed(() => useRole().hasAny(['hr'])); const advancedSummary = ref(); onBeforeMount(async () => { - if (isHr.value) { - advancedSummary.value = ( - await axios.get('Workers/advancedSummary', { - params: { filter: { where: { id: entityId.value } } }, - }) - ).data[0]; - basicDataUrl.value = `#/worker/${entityId.value}/basic-data`; - } + advancedSummary.value = await useAdvancedSummary('Workers', entityId.value); + basicDataUrl.value = `#/worker/${entityId.value}/basic-data`; }); </script> @@ -101,21 +93,27 @@ onBeforeMount(async () => { :label="t('worker.summary.seniority')" :value="toDate(worker.seniority)" /> - <VnLv :label="t('worker.summary.fi')" :value="worker.fi" /> - <VnLv :label="t('worker.summary.birth')" :value="toDate(worker.birth)" /> + <VnLv :label="t('worker.summary.fi')" :value="advancedSummary.fi" /> + <VnLv + :label="t('worker.summary.birth')" + :value="toDate(advancedSummary.birth)" + /> <VnLv :label="t('worker.summary.isFreelance')" - :value="worker.isFreelance" + :value="advancedSummary.isFreelance" /> <VnLv :label="t('worker.summary.isSsDiscounted')" - :value="worker.isSsDiscounted" + :value="advancedSummary.isSsDiscounted" /> <VnLv :label="t('worker.summary.hasMachineryAuthorized')" - :value="worker.hasMachineryAuthorized" + :value="advancedSummary.hasMachineryAuthorized" + /> + <VnLv + :label="t('worker.summary.isDisable')" + :value="advancedSummary.isDisable" /> - <VnLv :label="t('worker.summary.isDisable')" :value="worker.isDisable" /> </QCard> <QCard class="vn-one"> <VnTitle :text="t('worker.summary.userData')" /> diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js index c2a9e668f..d1feff23d 100644 --- a/src/router/modules/worker.js +++ b/src/router/modules/worker.js @@ -87,6 +87,13 @@ export default { meta: { title: 'basicData', icon: 'vn:settings', + acls: [ + { + model: 'Worker', + props: 'updateAttributes', + accessType: 'WRITE', + }, + ], }, component: () => import('src/pages/Worker/Card/WorkerBasicData.vue'), }, diff --git a/test/cypress/integration/ticket/ticketExpedition.spec.js b/test/cypress/integration/ticket/ticketExpedition.spec.js index 5eb2c1a2a..d4afd401f 100644 --- a/test/cypress/integration/ticket/ticketExpedition.spec.js +++ b/test/cypress/integration/ticket/ticketExpedition.spec.js @@ -11,15 +11,13 @@ describe('Ticket expedtion', () => { it('should change the state', () => { cy.visit('#/ticket/1/expedition'); - cy.intercept('GET', /\/api\/Expeditions\/filter/).as('expeditions'); - cy.intercept('POST', /\/api\/Expeditions\/crud/).as('crud'); - - cy.wait('@expeditions'); + cy.intercept('GET', /\/api\/Expeditions\/filter/).as('show'); + cy.intercept('POST', /\/api\/ExpeditionStates\/addExpeditionState/).as('add'); + cy.wait('@show'); cy.selectRows([1, 2]); - cy.get('#subToolbar [aria-controls]:nth-child(1)').click(); - cy.get('.q-menu .q-item').contains('Perdida').click(); - cy.wait('@crud'); + cy.selectOption('[data-cy="change-state"]', 'Perdida'); + cy.wait('@add'); cy.get(`${tableContent} tr:nth-child(-n+2) ${stateTd}`).each(($el) => { cy.wrap($el).contains('Perdida'); diff --git a/test/cypress/integration/worker/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js index 8a8bea443..c1c37fd32 100644 --- a/test/cypress/integration/worker/workerList.spec.js +++ b/test/cypress/integration/worker/workerList.spec.js @@ -11,7 +11,7 @@ describe('WorkerList', () => { it('should open the worker summary', () => { cy.get(inputName).type('jessica{enter}'); cy.get(searchBtn).click(); - cy.intercept('GET', /\/api\/Workers\/\d+/).as('worker'); + cy.intercept('GET', /\/api\/Workers\/summary+/).as('worker'); cy.wait('@worker').then(() => cy.get(descriptorTitle).should('include.text', 'Jessica') ); diff --git a/test/cypress/integration/worker/workerLocker.spec.js b/test/cypress/integration/worker/workerLocker.spec.js index 8a169dfb2..c222414fd 100644 --- a/test/cypress/integration/worker/workerLocker.spec.js +++ b/test/cypress/integration/worker/workerLocker.spec.js @@ -1,8 +1,7 @@ describe('WorkerLocker', () => { const productionId = 49; - const lockerCode = '2F'; - const input = '.q-card input'; - const thirdOpt = '[role="listbox"] .q-item:nth-child(1)'; + const lockerCode = '4F'; + const lockerSelect = '[data-cy="locker"]'; beforeEach(() => { cy.viewport(1280, 720); cy.login('productionBoss'); @@ -10,10 +9,8 @@ describe('WorkerLocker', () => { }); it('should allocates a locker', () => { - cy.get(input).click(); - cy.waitForElement('[role="listbox"]'); - cy.get(thirdOpt).click(); + cy.selectOption(lockerSelect, lockerCode); cy.saveCard(); - cy.get(input).invoke('val').should('eq', lockerCode); + cy.get(lockerSelect).invoke('val').should('eq', lockerCode); }); }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index c9b1a748e..76bdefd27 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -82,7 +82,7 @@ Cypress.Commands.add('getValue', (selector) => { // Fill Inputs Cypress.Commands.add('selectOption', (selector, option) => { cy.waitForElement(selector); - cy.get(selector).find('.q-select__dropdown-icon').click(); + cy.get(selector).click(); cy.get('.q-menu .q-item').contains(option).click(); }); From 0cc0e82aaaba838cbcd6c7ced6d52347af7438d0 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Sun, 27 Oct 2024 14:48:44 +0100 Subject: [PATCH 100/150] feat: refs #8078 add shortcut multi selection --- src/components/VnTable/VnTable.vue | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index c1680bf13..74a96e0e3 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -331,6 +331,16 @@ function handleScroll() { const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) <= 40; if (isAtBottom) CrudModelRef.value.vnPaginateRef.paginate(); } + +function handleSelection({ evt, added, rows: selectedRows }, rows) { + if (evt?.shiftKey && added) { + const rowIndex = selectedRows[0].$index; + for (const row of rows) { + if (row.$index > rowIndex) break; + selected.value.push(row); + } + } +} </script> <template> <QDrawer @@ -431,6 +441,7 @@ function handleScroll() { @virtual-scroll="handleScroll" @row-click="(_, row) => rowClickFunction && rowClickFunction(row)" @update:selected="emit('update:selected', $event)" + @selection="(details) => handleSelection(details, rows)" > <template #top-left v-if="!$props.withoutHeader"> <slot name="top-left"></slot> From ca6547e174b12865105cd819ccb3f1ccedf47803 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 07:48:11 +0100 Subject: [PATCH 101/150] feat: refs #7970 notify changes --- src/components/ui/VnConfirm.vue | 23 ++++++--- src/pages/Ticket/Card/TicketSale.vue | 76 ++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index 4fa374b62..0fbc55cfb 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -30,10 +30,10 @@ const props = defineProps({ }, }); -defineEmits(['confirm', ...useDialogPluginComponent.emits]); -defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() }); +defineEmits([...useDialogPluginComponent.emits]); -const { dialogRef, onDialogOK } = useDialogPluginComponent(); +const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = + useDialogPluginComponent(); const title = props.title || t('Confirm'); const message = @@ -53,9 +53,13 @@ async function confirm() { } onDialogOK(props.data); } + +function cancel() { + onDialogCancel(); +} </script> <template> - <QDialog ref="dialogRef"> + <QDialog ref="dialogRef" @hide="onDialogHide"> <QCard class="q-pa-sm"> <QCardSection class="row items-center q-pb-none"> <QAvatar @@ -67,7 +71,14 @@ async function confirm() { /> <span class="text-h6">{{ title }}</span> <QSpace /> - <QBtn icon="close" :disable="isLoading" flat round dense v-close-popup /> + <QBtn + icon="close" + :disable="isLoading" + flat + round + dense + @click="cancel()" + /> </QCardSection> <QCardSection class="q-pb-none"> <span v-if="message !== false" v-html="message" /> @@ -81,7 +92,7 @@ async function confirm() { color="primary" :disable="isLoading" flat - v-close-popup + @click="cancel()" /> <QBtn :label="t('globals.confirm')" diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index c786c67e3..3426db880 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -2,6 +2,7 @@ import { onMounted, ref, computed, onUnmounted, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRouter, useRoute } from 'vue-router'; +import { useQuasar } from 'quasar'; import FetchData from 'components/FetchData.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; @@ -23,6 +24,7 @@ import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; import VnTable from 'src/components/VnTable/VnTable.vue'; import VnUsesMana from 'src/components/ui/VnUsesMana.vue'; +import VnConfirm from 'src/components/ui/VnConfirm.vue'; const route = useRoute(); const router = useRouter(); @@ -32,7 +34,7 @@ const { notify } = useNotify(); const { openConfirmationModal } = useVnConfirm(); const editPriceProxyRef = ref(null); const stateBtnDropdownRef = ref(null); - +const quasar = useQuasar(); const arrayData = useArrayData('ticketData'); const { store } = arrayData; const selectedRows = ref([]); @@ -51,6 +53,7 @@ const transfer = ref({ sales: [], }); const tableRef = ref([]); +const canProceed = ref(); watch( () => route.params.id, @@ -214,7 +217,9 @@ const addSale = async (sale) => { } }; -const changeQuantity = (sale) => { +const changeQuantity = async (sale) => { + canProceed.value = await isSalePrepared(sale); + if (!canProceed.value) return; if ( !sale.itemFk || sale.quantity == null || @@ -226,6 +231,8 @@ const changeQuantity = (sale) => { }; const updateConcept = async (sale) => { + canProceed.value = await isSalePrepared(sale); + if (!canProceed.value) return; try { const data = { newConcept: sale.concept }; await axios.post(`Sales/${sale.id}/updateConcept`, data); @@ -286,6 +293,8 @@ const onOpenEditDiscountPopover = async (sale) => { }; const updatePrice = async (sale) => { + canProceed.value = await isSalePrepared(sale); + if (!canProceed.value) return; try { const newPrice = edit.value.price; if (newPrice != null && newPrice != sale.price) { @@ -300,12 +309,18 @@ const updatePrice = async (sale) => { } }; -const changeDiscount = (sale) => { +const changeDiscount = async (sale) => { + canProceed.value = await isSalePrepared(sale); + if (!canProceed.value) return; const newDiscount = edit.value.discount; if (newDiscount != null && newDiscount != sale.discount) updateDiscount([sale]); }; const updateDiscount = async (sales, newDiscount = null) => { + for (const sale of sales) { + const canProceed = await isSalePrepared(sale); + if (!canProceed) return; + } const saleIds = sales.map((sale) => sale.id); const _newDiscount = newDiscount || edit.value.discount; const params = { @@ -433,7 +448,9 @@ onUnmounted(() => (stateStore.rightDrawer = false)); const items = ref([]); const newRow = ref({}); -const updateItem = (row) => { +const updateItem = async (row) => { + canProceed.value = await isSalePrepared(row); + if (!canProceed.value) return; const selectedItem = items.value.find((item) => item.id === row.itemFk); if (selectedItem) { row.item = selectedItem; @@ -476,6 +493,55 @@ const endNewRow = (row) => { } }; +async function isSalePrepared(item) { + const filter = { + params: { + where: { ticketFk: route.params.id }, + order: ['concept ASC', 'quantity DESC'], + }, + }; + const { data } = await axios.get(`SaleTrackings/${route.params.id}/filter`, { + params: { + filter: JSON.stringify(filter), + }, + }); + + const matchingSale = data.find((sale) => sale.itemFk === item.itemFk); + if (!matchingSale) { + return true; + } + + if ( + matchingSale.hasSaleGroupDetail || + matchingSale.isControled || + matchingSale.isPrepared || + matchingSale.isPrevious || + matchingSale.isPreviousSelected + ) { + try { + await new Promise((resolve, reject) => { + quasar + .dialog({ + component: VnConfirm, + componentProps: { + title: t('Item prepared'), + message: t( + 'This item is already prepared. Do you want to continue?' + ), + data: item, + }, + }) + .onOk(() => resolve(true)) + .onCancel(() => reject(new Error('cancelled'))); + }); + } catch (error) { + tableRef.value.reload(); + return false; + } + } + return true; +} + watch( () => newRow.value.itemFk, (newItemFk) => { @@ -820,4 +886,6 @@ es: You are going to delete lines of the ticket: Vas a eliminar lineas del ticket Add item: Añadir artículo Transfer lines: Transferir líneas + Item prepared: Artículo preparado + This item is already prepared. Do you want to continue?: Este artículo ya esta preparado. Desea continuar? </i18n> From cca255507a593dce4f57fb949f2170dfb2187dd2 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 28 Oct 2024 09:57:25 +0100 Subject: [PATCH 102/150] fix: itemType redirection and fix filters --- src/components/common/VnComponent.vue | 2 +- src/components/ui/VnFilterPanel.vue | 2 +- .../ItemType/Card/ItemTypeBasicData.vue | 0 .../{ => Item}/ItemType/Card/ItemTypeCard.vue | 4 +- .../ItemType/Card/ItemTypeDescriptor.vue | 0 .../{ => Item}/ItemType/Card/ItemTypeLog.vue | 0 .../ItemType/Card/ItemTypeSummary.vue | 0 .../{ => Item}/ItemType/ItemTypeFilter.vue | 35 +++++++ .../{ => Item}/ItemType/ItemTypeSearchbar.vue | 1 + src/pages/{ => Item}/ItemType/locale/en.yml | 0 src/pages/{ => Item}/ItemType/locale/es.yml | 0 src/pages/Item/ItemTypeCreate.vue | 98 ------------------- src/pages/Item/ItemTypeList.vue | 58 +++++++++-- .../Ticket/Card/TicketDescriptorMenu.vue | 1 - src/router/modules/item.js | 8 -- src/router/modules/itemType.js | 9 +- 16 files changed, 93 insertions(+), 125 deletions(-) rename src/pages/{ => Item}/ItemType/Card/ItemTypeBasicData.vue (100%) rename src/pages/{ => Item}/ItemType/Card/ItemTypeCard.vue (75%) rename src/pages/{ => Item}/ItemType/Card/ItemTypeDescriptor.vue (100%) rename src/pages/{ => Item}/ItemType/Card/ItemTypeLog.vue (100%) rename src/pages/{ => Item}/ItemType/Card/ItemTypeSummary.vue (100%) rename src/pages/{ => Item}/ItemType/ItemTypeFilter.vue (57%) rename src/pages/{ => Item}/ItemType/ItemTypeSearchbar.vue (94%) rename src/pages/{ => Item}/ItemType/locale/en.yml (100%) rename src/pages/{ => Item}/ItemType/locale/es.yml (100%) delete mode 100644 src/pages/Item/ItemTypeCreate.vue diff --git a/src/components/common/VnComponent.vue b/src/components/common/VnComponent.vue index bd25c787c..580bcf348 100644 --- a/src/components/common/VnComponent.vue +++ b/src/components/common/VnComponent.vue @@ -1,5 +1,5 @@ <script setup> -import { computed, defineModel } from 'vue'; +import { computed } from 'vue'; const model = defineModel(undefined, { required: true }); const $props = defineProps({ diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 43d634ad9..e902214b2 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', 'search', 'or', 'and'], + default: () => ['filter', 'or', 'and'], }, customTags: { type: Array, diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/Item/ItemType/Card/ItemTypeBasicData.vue similarity index 100% rename from src/pages/ItemType/Card/ItemTypeBasicData.vue rename to src/pages/Item/ItemType/Card/ItemTypeBasicData.vue diff --git a/src/pages/ItemType/Card/ItemTypeCard.vue b/src/pages/Item/ItemType/Card/ItemTypeCard.vue similarity index 75% rename from src/pages/ItemType/Card/ItemTypeCard.vue rename to src/pages/Item/ItemType/Card/ItemTypeCard.vue index 9daec7921..0a2706a07 100644 --- a/src/pages/ItemType/Card/ItemTypeCard.vue +++ b/src/pages/Item/ItemType/Card/ItemTypeCard.vue @@ -1,7 +1,7 @@ <script setup> import VnCard from 'components/common/VnCard.vue'; -import ItemTypeDescriptor from 'src/pages/ItemType/Card/ItemTypeDescriptor.vue'; -import ItemTypeFilter from 'src/pages/ItemType/ItemTypeFilter.vue'; +import ItemTypeDescriptor from 'src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue'; +import ItemTypeFilter from 'src/pages/Item/ItemType/ItemTypeFilter.vue'; import ItemTypeSearchbar from '../ItemTypeSearchbar.vue'; </script> <template> diff --git a/src/pages/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue similarity index 100% rename from src/pages/ItemType/Card/ItemTypeDescriptor.vue rename to src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue diff --git a/src/pages/ItemType/Card/ItemTypeLog.vue b/src/pages/Item/ItemType/Card/ItemTypeLog.vue similarity index 100% rename from src/pages/ItemType/Card/ItemTypeLog.vue rename to src/pages/Item/ItemType/Card/ItemTypeLog.vue diff --git a/src/pages/ItemType/Card/ItemTypeSummary.vue b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue similarity index 100% rename from src/pages/ItemType/Card/ItemTypeSummary.vue rename to src/pages/Item/ItemType/Card/ItemTypeSummary.vue diff --git a/src/pages/ItemType/ItemTypeFilter.vue b/src/pages/Item/ItemType/ItemTypeFilter.vue similarity index 57% rename from src/pages/ItemType/ItemTypeFilter.vue rename to src/pages/Item/ItemType/ItemTypeFilter.vue index 2a86795c2..f6fb73098 100644 --- a/src/pages/ItemType/ItemTypeFilter.vue +++ b/src/pages/Item/ItemType/ItemTypeFilter.vue @@ -12,6 +12,39 @@ const props = defineProps({ }); const emit = defineEmits(['search']); +const exprBuilder = (param, value) => { + switch (param) { + case 'name': + return { + name: { like: `%${value}%` }, + }; + case 'code': + return { + code: { like: `%${value}%` }, + }; + case 'search': + if (value) { + if (!isNaN(value)) { + return { id: value }; + } else { + return { + or: [ + { + name: { + like: `%${value}%`, + }, + }, + { + code: { + like: `%${value}%`, + }, + }, + ], + }; + } + } + } +}; </script> <template> @@ -19,6 +52,8 @@ const emit = defineEmits(['search']); :data-key="props.dataKey" :search-button="true" @search="emit('search')" + search-url="table" + :expr-builder="exprBuilder" > <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> diff --git a/src/pages/ItemType/ItemTypeSearchbar.vue b/src/pages/Item/ItemType/ItemTypeSearchbar.vue similarity index 94% rename from src/pages/ItemType/ItemTypeSearchbar.vue rename to src/pages/Item/ItemType/ItemTypeSearchbar.vue index 749033d43..87903a517 100644 --- a/src/pages/ItemType/ItemTypeSearchbar.vue +++ b/src/pages/Item/ItemType/ItemTypeSearchbar.vue @@ -10,6 +10,7 @@ const { t } = useI18n(); url="ItemTypes" :label="t('Search item type')" :info="t('Search itemType by id, name or code')" + search-url="table" /> </template> <i18n> diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/Item/ItemType/locale/en.yml similarity index 100% rename from src/pages/ItemType/locale/en.yml rename to src/pages/Item/ItemType/locale/en.yml diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/Item/ItemType/locale/es.yml similarity index 100% rename from src/pages/ItemType/locale/es.yml rename to src/pages/Item/ItemType/locale/es.yml diff --git a/src/pages/Item/ItemTypeCreate.vue b/src/pages/Item/ItemTypeCreate.vue deleted file mode 100644 index 60c037510..000000000 --- a/src/pages/Item/ItemTypeCreate.vue +++ /dev/null @@ -1,98 +0,0 @@ -<script setup> -import { reactive, ref } from 'vue'; -import { useI18n } from 'vue-i18n'; -import { useRouter } from 'vue-router'; - -import FetchData from 'components/FetchData.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; -import FormModel from 'components/FormModel.vue'; -import VnRow from 'components/ui/VnRow.vue'; -import VnInput from 'src/components/common/VnInput.vue'; -import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; - -const { t } = useI18n(); -const router = useRouter(); - -const newItemTypeForm = reactive({}); - -const categoriesOptions = ref([]); -const temperaturesOptions = ref([]); - -const redirectToItemTypeBasicData = (_, { id }) => { - router.push({ name: 'ItemTypeBasicData', params: { id } }); -}; -</script> - -<template> - <FetchData - url="ItemCategories" - @on-fetch="(data) => (categoriesOptions = data)" - :filter="{ order: 'name ASC', fields: ['id', 'name'] }" - auto-load - /> - <FetchData - url="Temperatures" - @on-fetch="(data) => (temperaturesOptions = data)" - :filter="{ order: 'name ASC', fields: ['code', 'name'] }" - auto-load - /> - <QPage> - <VnSubToolbar /> - <FormModel - url-create="ItemTypes" - model="itemTypeCreate" - :form-initial-data="newItemTypeForm" - observe-form-changes - @on-data-saved="redirectToItemTypeBasicData" - > - <template #form="{ data }"> - <VnRow> - <VnInput v-model="data.code" :label="t('itemType.shared.code')" /> - <VnInput v-model="data.name" :label="t('itemType.shared.name')" /> - </VnRow> - <VnRow> - <VnSelect - url="Workers/search" - v-model="data.workerFk" - :label="t('shared.worker')" - sort-by="nickname ASC" - :fields="['id', 'nickname']" - :params="{ departmentCodes: ['shopping'] }" - option-label="nickname" - option-value="id" - hide-selected - ><template #option="scope"> - <QItem v-bind="scope.itemProps"> - <QItemSection> - <QItemLabel>{{ scope.opt?.name }}</QItemLabel> - <QItemLabel caption - >{{ scope.opt?.nickname }}, - {{ scope.opt?.code }}</QItemLabel - > - </QItemSection> - </QItem> - </template> - </VnSelect> - <VnSelect - v-model="data.categoryFk" - :label="t('itemType.shared.category')" - :options="categoriesOptions" - option-value="id" - option-label="name" - hide-selected - /> - </VnRow> - <VnRow> - <VnSelect - v-model="data.temperatureFk" - :label="t('itemType.shared.temperature')" - :options="temperaturesOptions" - option-value="code" - option-label="name" - hide-selected - /> - </VnRow> - </template> - </FormModel> - </QPage> -</template> diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 13dd56a42..149de482d 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -1,13 +1,16 @@ <script setup> import { useI18n } from 'vue-i18n'; import { ref, computed } from 'vue'; -import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue'; +import ItemTypeSearchbar from 'src/pages/Item/ItemType/ItemTypeSearchbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import FetchData from 'components/FetchData.vue'; +import RightMenu from 'src/components/common/RightMenu.vue'; +import ItemTypeFilter from './ItemType/ItemTypeFilter.vue'; const { t } = useI18n(); const tableRef = ref(); -const ItemCategoriesOptions = ref([]); +const itemCategoriesOptions = ref([]); +const temperatureOptions = ref([]); const columns = computed(() => [ { @@ -34,7 +37,6 @@ const columns = computed(() => [ }, { align: 'left', - name: 'workerFk', label: t('worker'), create: true, component: 'select', @@ -45,6 +47,19 @@ const columns = computed(() => [ }, cardVisible: true, visible: true, + columnField: { + component: 'userLink', + attrs: ({ row }) => { + return { + workerId: row?.worker?.id, + name: row.worker?.user?.name, + defaultName: true, + }; + }, + }, + columnFilter: { + name: 'workerFk', + }, }, { align: 'left', @@ -53,9 +68,7 @@ const columns = computed(() => [ create: true, component: 'select', attrs: { - options: ItemCategoriesOptions.value, - fields: ['id', 'name'], - order: 'name ASC', + options: itemCategoriesOptions.value, }, cardVisible: false, visible: false, @@ -67,8 +80,7 @@ const columns = computed(() => [ create: true, component: 'select', attrs: { - url: 'Temperatures', - fields: ['id', 'name'], + options: temperatureOptions.value, }, cardVisible: false, visible: false, @@ -80,26 +92,52 @@ const columns = computed(() => [ <FetchData url="ItemCategories" :filter="{ fields: ['id', 'name'], order: ['name ASC'] }" - @on-fetch="(data) => (ItemCategoriesOptions = data)" + @on-fetch="(data) => (itemCategoriesOptions = data)" auto-load /> + <FetchData + url="Temperatures" + :filter="{ fields: ['id', 'name'], order: ['name ASC'] }" + @on-fetch="(data) => (temperatureOptions = data)" + auto-load + /> + <RightMenu> + <template #right-panel> + <ItemTypeFilter data-key="ItemTypeList" /> + </template> + </RightMenu> <ItemTypeSearchbar /> <VnTable ref="tableRef" data-key="ItemTypeList" - :url="`ItemTypes`" + url="ItemTypes" :create="{ urlCreate: 'ItemTypes', title: t('Create ItemTypes'), onDataSaved: () => tableRef.reload(), formInitialData: {}, }" + :user-filter="{ + include: { + relation: 'worker', + scope: { + fields: ['id'], + include: { + relation: 'user', + scope: { + fields: ['id', 'name'], + }, + }, + }, + }, + }" order="name ASC" :columns="columns" auto-load :right-search="false" :is-editable="false" :use-model="true" + redirect="item/item-type" /> </template> diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 834fced87..bf4a1efb4 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -348,7 +348,6 @@ async function hasDocuware() { } async function uploadDocuware(force) { - console.log('force: ', force); if (!force) return quasar .dialog({ diff --git a/src/router/modules/item.js b/src/router/modules/item.js index 2838c3be7..0f810434c 100644 --- a/src/router/modules/item.js +++ b/src/router/modules/item.js @@ -97,14 +97,6 @@ export default { }, component: () => import('src/pages/Item/ItemTypeList.vue'), }, - { - path: 'item-type-list/create', - name: 'ItemTypeCreate', - meta: { - title: 'itemTypeCreate', - }, - component: () => import('src/pages/Item/ItemTypeCreate.vue'), - }, ], }, { diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js index 0fd3797e6..1ceecd4cc 100644 --- a/src/router/modules/itemType.js +++ b/src/router/modules/itemType.js @@ -18,7 +18,7 @@ export default { { name: 'ItemTypeCard', path: ':id', - component: () => import('src/pages/ItemType/Card/ItemTypeCard.vue'), + component: () => import('src/pages/Item/ItemType/Card/ItemTypeCard.vue'), redirect: { name: 'ItemTypeSummary' }, children: [ { @@ -28,7 +28,7 @@ export default { title: 'summary', }, component: () => - import('src/pages/ItemType/Card/ItemTypeSummary.vue'), + import('src/pages/Item/ItemType/Card/ItemTypeSummary.vue'), }, { name: 'ItemTypeBasicData', @@ -38,7 +38,7 @@ export default { icon: 'vn:settings', }, component: () => - import('src/pages/ItemType/Card/ItemTypeBasicData.vue'), + import('src/pages/Item/ItemType/Card/ItemTypeBasicData.vue'), }, { path: 'log', @@ -47,7 +47,8 @@ export default { title: 'log', icon: 'vn:History', }, - component: () => import('src/pages/ItemType/Card/ItemTypeLog.vue'), + component: () => + import('src/pages/Item/ItemType/Card/ItemTypeLog.vue'), }, ], }, From bc8a87b267921f339843e6639de268a55a482938 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 28 Oct 2024 09:58:07 +0100 Subject: [PATCH 103/150] chore: typo --- src/components/ui/VnFilterPanel.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index e902214b2..43d634ad9 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', 'or', 'and'], + default: () => ['filter', 'search', 'or', 'and'], }, customTags: { type: Array, From df1c123444e577336fed9c5b1c17d093ecf37d68 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 10:39:45 +0100 Subject: [PATCH 104/150] feat: refs #7193 modified parking to use the scope and corrected small errors --- src/i18n/locale/en.yml | 3 +-- src/i18n/locale/es.yml | 3 +-- src/pages/Parking/Card/ParkingCard.vue | 6 ------ src/pages/Parking/Card/ParkingDescriptor.vue | 1 + src/pages/Parking/ParkingList.vue | 4 +--- 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index c1748c8ff..24fda7aff 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -278,6 +278,7 @@ globals: RouteExtendedList: Router wasteRecalc: Waste recaclulate operator: Operator + parking: Parking supplier: Supplier created: Created worker: Worker @@ -663,8 +664,6 @@ parking: sector: Sector row: Row column: Column - pageTitles: - parking: Parking searchBar: info: You can search by parking code label: Search parking... diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index bd414a793..1571f7b92 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -282,6 +282,7 @@ globals: medical: Mutua wasteRecalc: Recalcular mermas operator: Operario + parking: Parking supplier: Proveedor created: Fecha creación worker: Trabajador @@ -710,8 +711,6 @@ parking: pickingOrder: Orden de recogida row: Fila column: Columna - pageTitles: - parking: Parking searchBar: info: Puedes buscar por código de parking label: Buscar parking... diff --git a/src/pages/Parking/Card/ParkingCard.vue b/src/pages/Parking/Card/ParkingCard.vue index ad37eb630..337106986 100644 --- a/src/pages/Parking/Card/ParkingCard.vue +++ b/src/pages/Parking/Card/ParkingCard.vue @@ -2,17 +2,11 @@ import VnCard from 'components/common/VnCard.vue'; import ParkingDescriptor from 'pages/Parking/Card/ParkingDescriptor.vue'; import ParkingFilter from 'pages/Parking/ParkingFilter.vue'; - -const filter = { - fields: ['id', 'sectorFk', 'code', 'pickingOrder', 'row', 'column'], - include: [{ relation: 'sector', scope: { fields: ['id', 'description'] } }], -}; </script> <template> <VnCard data-key="Parking" base-url="Parkings" - :filter="filter" :descriptor="ParkingDescriptor" :filter-panel="ParkingFilter" search-data-key="ParkingList" diff --git a/src/pages/Parking/Card/ParkingDescriptor.vue b/src/pages/Parking/Card/ParkingDescriptor.vue index b57bfb0cc..d36ea16fc 100644 --- a/src/pages/Parking/Card/ParkingDescriptor.vue +++ b/src/pages/Parking/Card/ParkingDescriptor.vue @@ -29,6 +29,7 @@ const filter = { :url="`Parkings/${entityId}`" title="code" :filter="filter" + :to-module="{ name: 'ParkingList' }" > <template #body="{ entity }"> <VnLv :label="t('globals.code')" :value="entity.code" /> diff --git a/src/pages/Parking/ParkingList.vue b/src/pages/Parking/ParkingList.vue index b6f4e8146..109613383 100644 --- a/src/pages/Parking/ParkingList.vue +++ b/src/pages/Parking/ParkingList.vue @@ -22,7 +22,6 @@ onUnmounted(() => (stateStore.rightDrawer = false)); const filter = { fields: ['id', 'sectorFk', 'code', 'pickingOrder'], - include: [{ relation: 'sector', scope: { fields: ['id', 'description'] } }], }; function exprBuilder(param, value) { @@ -55,10 +54,9 @@ function exprBuilder(param, value) { <VnPaginate data-key="ParkingList" url="Parkings" - :filter="filter" + :user-filter="filter" :expr-builder="exprBuilder" :limit="20" - auto-load order="code" > <template #body="{ rows }"> From 2cde8501327333e6f259f8e7d1bea88e1d5be1a9 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 11:37:32 +0100 Subject: [PATCH 105/150] fix: refs #7193 fixed e2e test --- test/cypress/integration/parking/parkingList.spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cypress/integration/parking/parkingList.spec.js b/test/cypress/integration/parking/parkingList.spec.js index b78a660d1..f1efaa375 100644 --- a/test/cypress/integration/parking/parkingList.spec.js +++ b/test/cypress/integration/parking/parkingList.spec.js @@ -1,5 +1,6 @@ /// <reference types="cypress" /> describe('ParkingList', () => { + const searchbar = '#searchbar input'; const firstCard = '.q-card:nth-child(1)'; const firstChipId = ':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content'; @@ -14,6 +15,7 @@ describe('ParkingList', () => { }); it('should redirect on clicking a parking', () => { + cy.get(searchbar).type('{enter}'); cy.get(firstChipId) .invoke('text') .then((content) => { @@ -24,6 +26,7 @@ describe('ParkingList', () => { }); it('should open the details', () => { + cy.get(searchbar).type('{enter}'); cy.get(firstDetailBtn).click(); cy.get(summaryHeader).contains('Basic data'); }); From 9347bb1ef5384aee2e84f73afa3d3b48b449b500 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 11:44:49 +0100 Subject: [PATCH 106/150] refactor: refs #7970 refactored VnConfirm to emit events --- src/components/ui/VnConfirm.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index 0fbc55cfb..c5d2b43f5 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -30,7 +30,7 @@ const props = defineProps({ }, }); -defineEmits([...useDialogPluginComponent.emits]); +defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]); const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); From 17b1921af6603db83744efc8095c6dbc4eb34141 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 12:21:26 +0100 Subject: [PATCH 107/150] feat: added composable to confirm orders --- src/composables/confirmOrder.js | 22 +++++++++++++++++++++ src/pages/Order/Card/OrderLines.vue | 28 +++++++++++++-------------- src/pages/Order/Card/OrderSummary.vue | 21 +++++++++----------- 3 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 src/composables/confirmOrder.js diff --git a/src/composables/confirmOrder.js b/src/composables/confirmOrder.js new file mode 100644 index 000000000..33e05a1d7 --- /dev/null +++ b/src/composables/confirmOrder.js @@ -0,0 +1,22 @@ +import axios from 'axios'; +import { useQuasar } from 'quasar'; +import { useI18n } from 'vue-i18n'; + +export function confirmOrder() { + const quasar = useQuasar(); + const { t } = useI18n(); + + async function confirm(route) { + const { data } = await axios.post(`Orders/${route}/confirm`); + if (data) { + quasar.notify({ + message: t('globals.confirm'), + type: 'positive', + }); + return data; + } + return null; + } + + return { confirm }; +} diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index ee883b73a..90586dc15 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -6,6 +6,7 @@ import { useQuasar } from 'quasar'; import axios from 'axios'; import { useStateStore } from 'stores/useStateStore'; import { useArrayData } from 'composables/useArrayData'; +import { confirmOrder } from 'composables/confirmOrder'; import { toCurrency, toDate } from 'src/filters'; import VnConfirm from 'components/ui/VnConfirm.vue'; @@ -31,7 +32,7 @@ const orderSummary = ref({ }); const getTotalRef = ref(); const getVATRef = ref(); - +const { confirm } = confirmOrder(); const lineFilter = ref({ include: [ { @@ -204,20 +205,17 @@ async function remove(item) { getVATRef.value.fetch(); } -async function confirmOrder() { - await axios.post(`Orders/${route.params.id}/confirm`); - quasar.notify({ - message: t('globals.confirm'), - type: 'positive', - }); - router.push({ - name: 'TicketList', - query: { - table: JSON.stringify({ clientFk: descriptorData.store.data.clientFk }), - }, - }); +async function handleConfirm() { + const result = await confirm(route.params.id); + if (result) { + router.push({ + name: 'TicketList', + query: { + table: JSON.stringify({ clientFk: descriptorData.store.data.clientFk }), + }, + }); + } } - watch( () => router.currentRoute.value.params.id, () => { @@ -314,7 +312,7 @@ watch( </template> </VnTable> <QPageSticky :offset="[20, 20]" v-if="!order?.isConfirmed" style="z-index: 2"> - <QBtn fab icon="check" color="primary" @click="confirmOrder()" /> + <QBtn fab icon="check" color="primary" @click="handleConfirm()" /> <QTooltip> {{ t('confirm') }} </QTooltip> diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue index 9bc89ad28..3669fe8d8 100644 --- a/src/pages/Order/Card/OrderSummary.vue +++ b/src/pages/Order/Card/OrderSummary.vue @@ -2,10 +2,9 @@ import { computed, ref } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { useQuasar } from 'quasar'; -import axios from 'axios'; import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters'; import { useArrayData } from 'composables/useArrayData'; +import { confirmOrder } from 'composables/confirmOrder'; import VnLv from 'components/ui/VnLv.vue'; import CardSummary from 'components/ui/CardSummary.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; @@ -25,8 +24,8 @@ const $props = defineProps({ const entityId = computed(() => $props.id || route.params.id); const summary = ref(); -const quasar = useQuasar(); const descriptorData = useArrayData('orderData'); +const { confirm } = confirmOrder(); const detailsColumns = ref([ { name: 'item', @@ -56,14 +55,12 @@ const detailsColumns = ref([ }, ]); -async function confirmOrder() { - await axios.post(`Orders/${route.params.id}/confirm`); - quasar.notify({ - message: t('globals.confirm'), - type: 'positive', - }); - summary.value.fetch({}); - descriptorData.fetch({}); +async function handleConfirm() { + const result = await confirm(route.params.id); + if (result) { + summary.value.fetch({}); + descriptorData.fetch({}); + } } </script> @@ -84,7 +81,7 @@ async function confirmOrder() { text-color="white" :disabled="isConfirmed" :label="t('order.summary.confirm')" - @click="confirmOrder()" + @click="handleConfirm()" > <QTooltip>{{ t('order.summary.confirmLines') }}</QTooltip> </QBtn> From 3d4ca2bc672a2375a14df8c2f2918b137839ffef Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 12:45:13 +0100 Subject: [PATCH 108/150] refactor: refs #7970 added emit --- src/components/ui/VnConfirm.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index c5d2b43f5..ec9d0f48b 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -30,7 +30,7 @@ const props = defineProps({ }, }); -defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]); +const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]); const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); @@ -55,6 +55,7 @@ async function confirm() { } function cancel() { + emit('cancel'); onDialogCancel(); } </script> From cab5a165d2acb6c4e5d92859e5ed96611029d938 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 28 Oct 2024 12:59:25 +0100 Subject: [PATCH 109/150] refactor: changed confirmOrder directory --- src/{ => pages/Order}/composables/confirmOrder.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ => pages/Order}/composables/confirmOrder.js (100%) diff --git a/src/composables/confirmOrder.js b/src/pages/Order/composables/confirmOrder.js similarity index 100% rename from src/composables/confirmOrder.js rename to src/pages/Order/composables/confirmOrder.js From f2803a63136a9478e492d112d4f6f1b6d16b5c73 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 28 Oct 2024 13:08:19 +0100 Subject: [PATCH 110/150] fix: refs #7283 item filters --- src/pages/Item/ItemList.vue | 35 +++++++++++++++++++++------------- src/pages/Item/ItemRequest.vue | 24 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index cca5560fe..c9422a0fe 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -40,6 +40,12 @@ const itemFilter = { fields: ['id', 'name'], }, }, + { + relation: 'production', + scope: { + fields: ['id', 'name'], + }, + }, ], }; const columns = computed(() => [ @@ -161,19 +167,13 @@ const columns = computed(() => [ name: 'intrastat', align: 'left', component: 'select', - attrs: { - url: 'Intrastats', - optionValue: 'description', - optionLabel: 'description', - }, columnFilter: { - name: 'description', + name: 'intrastat', attrs: { url: 'Intrastats', optionValue: 'description', optionLabel: 'description', }, - alias: 'intr', }, columnField: { component: null, @@ -211,14 +211,19 @@ const columns = computed(() => [ label: t('item.list.userName'), name: 'userName', align: 'left', + component: 'select', columnFilter: { name: 'workerFk', attrs: { - url: 'Users', + url: 'VnUsers', optionValue: 'id', - optionLabel: 'userName', + optionLabel: 'nickname', }, }, + + columnField: { + component: null, + }, }, { label: t('item.list.weightByPiece'), @@ -255,9 +260,13 @@ const columns = computed(() => [ name: 'producer', align: 'left', component: 'select', - attrs: { - url: 'Producers', - fields: ['id', 'name'], + columnFilter: { + name: 'producerFk', + attrs: { + url: 'Producers', + optionValue: 'id', + optionLabel: 'name', + }, }, columnField: { component: null, @@ -331,7 +340,7 @@ const columns = computed(() => [ <template #column-userName="{ row }"> <span class="link" @click.stop> {{ row.userName }} - <WorkerDescriptorProxy :id="row.buyerFk" /> + <WorkerDescriptorProxy :id="row.workerFk" /> </span> </template> <template #column-description="{ row }"> diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 450031a0e..7a737dd71 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -70,6 +70,18 @@ const columns = computed(() => [ { label: t('item.buyRequest.requester'), name: 'requesterName', + component: 'select', + columnFilter: { + name: 'requesterFk', + attrs: { + url: 'VnUsers', + optionValue: 'id', + optionLabel: 'nickname', + }, + }, + columnField: { + component: null, + }, columnClass: 'shrink', }, { @@ -88,6 +100,18 @@ const columns = computed(() => [ label: t('item.buyRequest.attender'), name: 'attenderName', align: 'left', + component: 'select', + columnFilter: { + name: 'attenderFk', + attrs: { + url: 'VnUsers', + optionValue: 'id', + optionLabel: 'nickname', + }, + }, + columnField: { + component: null, + }, columnClass: 'shrink', }, { From 965c2f7b20bce7ce702fddb758654b810d94e121 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 28 Oct 2024 13:16:51 +0100 Subject: [PATCH 111/150] fix: refs #7283 preview --- src/pages/Item/ItemList.vue | 2 +- src/pages/Item/ItemRequest.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index c9422a0fe..a6873d10c 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -215,7 +215,7 @@ const columns = computed(() => [ columnFilter: { name: 'workerFk', attrs: { - url: 'VnUsers', + url: 'VnUsers/preview', optionValue: 'id', optionLabel: 'nickname', }, diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 7a737dd71..4122b0c12 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -104,7 +104,7 @@ const columns = computed(() => [ columnFilter: { name: 'attenderFk', attrs: { - url: 'VnUsers', + url: 'VnUsers/preview', optionValue: 'id', optionLabel: 'nickname', }, From d252f6d2a3deb600cc0290a5892c26b614decc76 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 28 Oct 2024 13:22:33 +0100 Subject: [PATCH 112/150] fix: refs #7283 fix preview --- src/pages/Item/ItemRequest.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 4122b0c12..36da0368b 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -74,7 +74,7 @@ const columns = computed(() => [ columnFilter: { name: 'requesterFk', attrs: { - url: 'VnUsers', + url: 'VnUsers/preview', optionValue: 'id', optionLabel: 'nickname', }, From a1105f2ef19b15eb3cc54891b737d4495b20b8a9 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 28 Oct 2024 13:24:46 +0100 Subject: [PATCH 113/150] feat: refs #7524 add front test --- .../pages/Tickets/TicketAdvance.spec.js | 116 ++++++++++++++++++ test/vitest/helper.js | 2 + 2 files changed, 118 insertions(+) create mode 100644 test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js diff --git a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js new file mode 100644 index 000000000..6fae2788e --- /dev/null +++ b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js @@ -0,0 +1,116 @@ +import { vi, describe, expect, it, beforeAll, afterEach, beforeEach } from 'vitest'; +import { createWrapper, axios } from 'app/test/vitest/helper'; +import TicketAdvance from 'pages/Ticket/TicketAdvance.vue'; +import { Notify } from 'quasar'; + +describe('TicketAdvance', () => { + let wrapper; + let vm; + + beforeAll(() => { + vi.spyOn(axios, 'get').mockImplementation(() => ({ data: [] })); + wrapper = createWrapper(TicketAdvance); + vm = wrapper.vm; + // vm.vnTableRef.value = { reload: vi.fn(), params: {} }; + }); + beforeEach(() => { + Notify.create = vi.fn(); + }); + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('requestComponentUpdate()', () => { + const mockTicket = { + futureId: 1, + futureClientFk: 1, + nickname: 'test', + futureAddressFk: 1, + futureAgencyModeFk: 1, + futureWarehouseFk: 1, + futureCompanyFk: 1, + landed: '2023-01-01', + zoneFk: 1, + }; + const mockParams = { + clientFk: 1, + nickname: 'test', + agencyModeFk: 1, + addressFk: 1, + zoneFk: 1, + warehouseFk: 1, + companyFk: 1, + landed: '2023-01-01', + isDeleted: false, + isWithoutNegatives: false, + newTicket: undefined, + keepPrice: true, + }; + const queryResult = 'tickets/1/componentUpdate'; + + it('should return query and params when ticket has no landed', async () => { + const mockLanded = { landed: '2023-01-01', zoneFk: 1 }; + vi.spyOn(vm, 'getLanded').mockResolvedValue(mockLanded); + + const { query, params } = await vm.requestComponentUpdate(mockTicket, false); + + expect(query).toBe(queryResult); + expect(params).toEqual(mockParams); + }); + + it('should return query and params when ticket has landed', async () => { + const { query, params } = await vm.requestComponentUpdate(mockTicket, false); + + expect(query).toBe(queryResult); + expect(params).toEqual(mockParams); + }); + }); + + describe('moveTicketsAdvance()', () => { + it('should move tickets and notify success', async () => { + const tickets = [ + { + id: 1, + futureId: 2, + futureShipped: '2023-01-01', + shipped: '2023-01-02', + workerFk: 1, + }, + { + id: 2, + futureId: 3, + futureShipped: '2023-01-01', + shipped: '2023-01-02', + workerFk: 1, + }, + ]; + vm.selectedTickets.value = tickets; + vi.spyOn(axios, 'post').mockResolvedValue({}); + await vm.moveTicketsAdvance(); + + expect(axios.post).toHaveBeenCalledOnce('Tickets/merge', { + tickets: [ + { + originId: 2, + destinationId: 1, + originShipped: '2023-01-01', + destinationShipped: '2023-01-02', + workerFk: 1, + }, + { + originId: 3, + destinationId: 2, + originShipped: '2023-01-01', + destinationShipped: '2023-01-02', + workerFk: 1, + }, + ], + }); + expect(Notify.create).toHaveBeenCalledWith({ + type: 'positive', + message: 'advanceTickets.moveTicketSuccess', + }); + expect(vm.selectedTickets).toEqual([]); + }); + }); +}); diff --git a/test/vitest/helper.js b/test/vitest/helper.js index e201535ec..4bfae5dc8 100644 --- a/test/vitest/helper.js +++ b/test/vitest/helper.js @@ -70,8 +70,10 @@ class FormDataMock { vi.fn(); } } + global.FormData = FormDataMock; global.URL = class URL {}; +global.Date.vnNew = () => new Date(Date.UTC(2001, 0, 1, 11)); export function createWrapper(component, options) { const defaultOptions = { From 15977357d9c4c3e76d79cd5798ee78a222e367ff Mon Sep 17 00:00:00 2001 From: guillermo <guillermo@verdnatura.es> Date: Mon, 28 Oct 2024 14:11:34 +0100 Subject: [PATCH 114/150] refactor: refs #7266 Changed method name --- src/pages/Entry/EntryBuysTableDialog.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Entry/EntryBuysTableDialog.vue b/src/pages/Entry/EntryBuysTableDialog.vue index 0f9be6298..23a6a0021 100644 --- a/src/pages/Entry/EntryBuysTableDialog.vue +++ b/src/pages/Entry/EntryBuysTableDialog.vue @@ -121,7 +121,7 @@ const entriesTableColumns = computed(() => [ :loading="isLoading" @click=" openReport( - `Entries/${props.row.id}/buy-label` + `Entries/${props.row.id}/buy-label-supplier` ) " unelevated From 934b32092bb27eebcbbd298674bb5e5f7e832515 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 28 Oct 2024 14:21:03 +0100 Subject: [PATCH 115/150] chore: refs #7524 fix test --- .../pages/Tickets/TicketAdvance.spec.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js index 6fae2788e..ab1a47544 100644 --- a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js +++ b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js @@ -2,6 +2,7 @@ import { vi, describe, expect, it, beforeAll, afterEach, beforeEach } from 'vite import { createWrapper, axios } from 'app/test/vitest/helper'; import TicketAdvance from 'pages/Ticket/TicketAdvance.vue'; import { Notify } from 'quasar'; +import { nextTick } from 'vue'; describe('TicketAdvance', () => { let wrapper; @@ -11,7 +12,8 @@ describe('TicketAdvance', () => { vi.spyOn(axios, 'get').mockImplementation(() => ({ data: [] })); wrapper = createWrapper(TicketAdvance); vm = wrapper.vm; - // vm.vnTableRef.value = { reload: vi.fn(), params: {} }; + vi.spyOn(vm.vnTableRef, 'reload').mockImplementation(() => vi.fn()); + vm.vnTableRef.value = { params: {} }; }); beforeEach(() => { Notify.create = vi.fn(); @@ -29,7 +31,7 @@ describe('TicketAdvance', () => { futureAgencyModeFk: 1, futureWarehouseFk: 1, futureCompanyFk: 1, - landed: '2023-01-01', + landed: '2023-01-02', zoneFk: 1, }; const mockParams = { @@ -40,7 +42,8 @@ describe('TicketAdvance', () => { zoneFk: 1, warehouseFk: 1, companyFk: 1, - landed: '2023-01-01', + landed: '2023-01-02', + shipped: '2023-01-01', isDeleted: false, isWithoutNegatives: false, newTicket: undefined, @@ -49,7 +52,10 @@ describe('TicketAdvance', () => { const queryResult = 'tickets/1/componentUpdate'; it('should return query and params when ticket has no landed', async () => { - const mockLanded = { landed: '2023-01-01', zoneFk: 1 }; + vm.vnTableRef.params.dateToAdvance = '2023-01-01'; + await nextTick(); + + const mockLanded = { landed: '2023-01-02', zoneFk: 1 }; vi.spyOn(vm, 'getLanded').mockResolvedValue(mockLanded); const { query, params } = await vm.requestComponentUpdate(mockTicket, false); @@ -84,7 +90,7 @@ describe('TicketAdvance', () => { workerFk: 1, }, ]; - vm.selectedTickets.value = tickets; + vm.selectedTickets = tickets; vi.spyOn(axios, 'post').mockResolvedValue({}); await vm.moveTicketsAdvance(); @@ -106,10 +112,8 @@ describe('TicketAdvance', () => { }, ], }); - expect(Notify.create).toHaveBeenCalledWith({ - type: 'positive', - message: 'advanceTickets.moveTicketSuccess', - }); + expect(vm.vnTableRef.reload).toHaveBeenCalled(); + expect(Notify.create).toHaveBeenCalled(); expect(vm.selectedTickets).toEqual([]); }); }); From d5639471cf358c89c9dfb28b3f3b9eb11b2d4a23 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 28 Oct 2024 15:40:36 +0100 Subject: [PATCH 116/150] feat: refs #8078 add tests --- .../__tests__/components/VnTable.spec.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/vitest/__tests__/components/VnTable.spec.js diff --git a/test/vitest/__tests__/components/VnTable.spec.js b/test/vitest/__tests__/components/VnTable.spec.js new file mode 100644 index 000000000..d11c2b6c3 --- /dev/null +++ b/test/vitest/__tests__/components/VnTable.spec.js @@ -0,0 +1,47 @@ +import { describe, expect, it, beforeAll, beforeEach } from 'vitest'; +import { createWrapper } from 'app/test/vitest/helper'; +import VnTable from 'src/components/VnTable/VnTable.vue'; + +describe('VnTable', () => { + let wrapper; + let vm; + + beforeAll(() => { + wrapper = createWrapper(VnTable, { + propsData: { + columns: [], + }, + }); + vm = wrapper.vm; + }); + + beforeEach(() => (vm.selected = [])); + + describe('handleSelection()', () => { + const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }]; + const selectedRows = [{ $index: 1 }]; + it('should add rows to selected when shift key is pressed and rows are added', () => { + vm.handleSelection( + { evt: { shiftKey: true }, added: true, rows: selectedRows }, + rows + ); + expect(vm.selected).toEqual([{ $index: 0 }, { $index: 1 }]); + }); + + it('should not add rows to selected when shift key is not pressed', () => { + vm.handleSelection( + { evt: { shiftKey: false }, added: true, rows: selectedRows }, + rows + ); + expect(vm.selected).toEqual([]); + }); + + it('should not add rows to selected when rows are not added', () => { + vm.handleSelection( + { evt: { shiftKey: true }, added: false, rows: selectedRows }, + rows + ); + expect(vm.selected).toEqual([]); + }); + }); +}); From 7e3e194b465f0ba3abfa4a57aa762f58f63d24c1 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 28 Oct 2024 16:19:13 +0100 Subject: [PATCH 117/150] fix: refs #8078 e2e #7970 --- src/components/ui/VnConfirm.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index ec9d0f48b..89726c891 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -31,9 +31,9 @@ const props = defineProps({ }); const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]); - const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); +defineExpose({ dialogRef }); const title = props.title || t('Confirm'); const message = From 57488503aa1c9494586f6bf3c1b6b7860e7ccf6b Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 28 Oct 2024 16:19:24 +0100 Subject: [PATCH 118/150] fix: refs #8078 e2e #7970 --- src/pages/Ticket/Card/TicketDescriptorMenu.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index bf4a1efb4..8efd0f1e1 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -659,7 +659,7 @@ async function uploadDocuware(force) { </QList> </QMenu> </QItem> - <QItem @click="$refs.weightDialog.show()" v-ripple clickable> + <QItem @click="$refs.weightDialog.dialogRef.show()" v-ripple clickable> <QItemSection avatar> <QIcon name="weight" /> </QItemSection> From 1e16e3312e3e22d0c6170195acd9c3e4670e5b5a Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 28 Oct 2024 16:21:00 +0100 Subject: [PATCH 119/150] fix: refs #8078 improve cy command --- src/components/ui/CardDescriptor.vue | 1 + test/cypress/integration/ticket/ticketDescriptor.spec.js | 4 ++++ test/cypress/support/commands.js | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue index 7f35f0a28..83af77442 100644 --- a/src/components/ui/CardDescriptor.vue +++ b/src/components/ui/CardDescriptor.vue @@ -167,6 +167,7 @@ const toModule = computed(() => icon="more_vert" round size="md" + data-cy="descriptor-more-opts" > <QTooltip> {{ t('components.cardDescriptor.moreOptions') }} diff --git a/test/cypress/integration/ticket/ticketDescriptor.spec.js b/test/cypress/integration/ticket/ticketDescriptor.spec.js index 0ba2723a2..cd9f288f5 100644 --- a/test/cypress/integration/ticket/ticketDescriptor.spec.js +++ b/test/cypress/integration/ticket/ticketDescriptor.spec.js @@ -14,6 +14,8 @@ describe('Ticket descriptor', () => { it('should clone the ticket without warehouse', () => { cy.visit('/#/ticket/1/summary'); + cy.intercept('GET', /\/api\/Tickets\/\d/).as('ticket'); + cy.wait('@ticket'); cy.openActionsDescriptor(); cy.contains(listItem, toCloneOpt).click(); cy.clickConfirm(); @@ -28,6 +30,8 @@ describe('Ticket descriptor', () => { it('should set the weight of the ticket', () => { cy.visit('/#/ticket/10/summary'); + cy.intercept('GET', /\/api\/Tickets\/\d/).as('ticket'); + cy.wait('@ticket'); cy.openActionsDescriptor(); cy.contains(listItem, setWeightOpt).click(); cy.intercept('POST', /\/api\/Tickets\/\d+\/setWeight/).as('weight'); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 76bdefd27..1e9ca93b5 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -261,7 +261,7 @@ Cypress.Commands.add('openActionDescriptor', (opt) => { }); Cypress.Commands.add('openActionsDescriptor', () => { - cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click(); + cy.get('[data-cy="descriptor-more-opts"]').click(); }); Cypress.Commands.add('openUserPanel', () => { From 0de4dfd4f8bcf5f55a56a48c5a89fa8f702745e9 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 02:15:02 +0100 Subject: [PATCH 120/150] feat: apply changes for customerModule --- src/components/ui/VnLinkMail.vue | 16 +++ src/pages/Customer/Card/CustomerAddress.vue | 2 +- src/pages/Customer/Card/CustomerBalance.vue | 120 +++++++++++------- src/pages/Customer/Card/CustomerCredits.vue | 4 +- .../Customer/Card/CustomerDescriptor.vue | 38 +++++- .../Customer/Card/CustomerDescriptorMenu.vue | 28 ---- .../Customer/Card/CustomerFiscalData.vue | 7 +- src/pages/Customer/Card/CustomerGreuges.vue | 5 + .../Customer/Card/CustomerRecoveries.vue | 1 + src/pages/Customer/Card/CustomerSummary.vue | 26 +++- src/pages/Customer/CustomerList.vue | 6 +- .../components/CustomerAddressCreate.vue | 15 ++- .../components/CustomerNewPayment.vue | 10 +- .../components/CustomerSummaryTable.vue | 14 +- .../Customer/composables/getClientRisk.js | 12 ++ .../integration/client/clientList.spec.js | 19 ++- test/cypress/support/commands.js | 33 +++-- 17 files changed, 251 insertions(+), 105 deletions(-) create mode 100644 src/components/ui/VnLinkMail.vue create mode 100644 src/pages/Customer/composables/getClientRisk.js diff --git a/src/components/ui/VnLinkMail.vue b/src/components/ui/VnLinkMail.vue new file mode 100644 index 000000000..a54f463f5 --- /dev/null +++ b/src/components/ui/VnLinkMail.vue @@ -0,0 +1,16 @@ +<script setup> +defineProps({ email: { type: [String], default: null } }); +</script> +<template> + <QBtn + v-if="email" + flat + round + icon="email" + size="sm" + color="primary" + padding="none" + :href="`mailto:${email}`" + @click.stop + /> +</template> diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue index 166c33e1a..294e49cc8 100644 --- a/src/pages/Customer/Card/CustomerAddress.vue +++ b/src/pages/Customer/Card/CustomerAddress.vue @@ -27,7 +27,7 @@ const addressFilter = { 'isLogifloraAllowed', 'postalCode', ], - order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'], + order: ['isDefaultAddress DESC', 'isActive DESC', 'id DESC', 'nickname ASC'], include: [ { relation: 'observations', diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue index f148194f8..f6fc3eed2 100644 --- a/src/pages/Customer/Card/CustomerBalance.vue +++ b/src/pages/Customer/Card/CustomerBalance.vue @@ -5,7 +5,7 @@ import { useRoute } from 'vue-router'; import { useAcl } from 'src/composables/useAcl'; import axios from 'axios'; import { useQuasar } from 'quasar'; -import FetchData from 'components/FetchData.vue'; +import { getClientRisk } from '../composables/getClientRisk'; import { toCurrency, toDate, toDateHourMin } from 'src/filters'; import { useState } from 'composables/useState'; @@ -16,7 +16,7 @@ import { useVnConfirm } from 'composables/useVnConfirm'; import VnTable from 'components/VnTable/VnTable.vue'; import VnInput from 'components/common/VnInput.vue'; import VnSubToolbar from 'components/ui/VnSubToolbar.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; +import VnFilter from 'components/VnTable/VnFilter.vue'; import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; @@ -25,7 +25,7 @@ const { openConfirmationModal } = useVnConfirm(); const { sendEmail, openReport } = usePrintService(); const { t } = useI18n(); const { hasAny } = useAcl(); -const currentBalance = ref({}); + const quasar = useQuasar(); const route = useRoute(); const state = useState(); @@ -33,28 +33,53 @@ const stateStore = useStateStore(); const user = state.getUser(); const clientRisk = ref([]); -const companies = ref([]); const tableRef = ref(); -const companyId = ref(user.value.companyFk); +const companyId = ref(); +const companyUser = ref(user.value.companyFk); const balances = ref([]); const vnFilterRef = ref({}); const filter = computed(() => { return { clientId: route.params.id, - companyId: companyId.value ?? user.value.companyFk, + companyId: companyId.value ?? companyUser.value, }; }); +const companyFilterColumn = { + align: 'left', + name: 'companyId', + label: t('Company'), + component: 'select', + attrs: { + url: 'Companies', + optionLabel: 'code', + optionValue: 'id', + sortBy: 'code', + }, + columnFilter: { + event: { + remove: () => (companyId.value = null), + 'update:modelValue': (newCompanyFk) => { + if (!newCompanyFk) return; + vnFilterRef.value.addFilter(newCompanyFk); + companyUser.value = newCompanyFk; + }, + blur: () => !companyId.value && (companyId.value = companyUser.value), + }, + }, + visible: false, +}; + const columns = computed(() => [ { - align: 'right', + align: 'left', name: 'payed', label: t('Date'), format: ({ payed }) => toDate(payed), cardVisible: true, }, { - align: 'right', + align: 'left', name: 'created', label: t('Creation date'), format: ({ created }) => toDateHourMin(created), @@ -65,7 +90,12 @@ const columns = computed(() => [ label: t('Employee'), columnField: { component: 'userLink', - attrs: ({ row }) => ({ workerId: row.workerFk, name: row.userName }), + attrs: ({ row }) => { + return { + workerId: row.workerFk, + name: row.userName, + }; + }, }, cardVisible: true, }, @@ -77,13 +107,13 @@ const columns = computed(() => [ class: 'extend', }, { - align: 'right', + align: 'left', name: 'bankFk', label: t('Bank'), cardVisible: true, }, { - align: 'right', + align: 'left', name: 'debit', label: t('Debit'), format: ({ debit }) => debit && toCurrency(debit), @@ -136,20 +166,37 @@ const columns = computed(() => [ onBeforeMount(() => { stateStore.rightDrawer = true; + companyId.value = companyUser.value; }); -async function getCurrentBalance(data) { - currentBalance.value[companyId.value] = { - amount: 0, - code: companies.value.find((c) => c.id === companyId.value)?.code, +async function getClientRisks() { + const filter = { + where: { clientFk: route.params.id, companyFk: companyUser.value }, }; + const { data } = await getClientRisk(filter); + clientRisk.value = data; + return clientRisk.value; +} - for (const balance of data) { - currentBalance.value[balance.companyFk] = { - code: balance.company.code, - amount: balance.amount, - }; +async function getCurrentBalance() { + const currentBalance = (await getClientRisks()).find((balance) => { + return balance.companyFk === companyId.value; + }); + return currentBalance && currentBalance.amount; +} + +async function onFetch(data) { + balances.value = []; + for (const [index, balance] of data.entries()) { + if (index === 0) { + balance.balance = await getCurrentBalance(); + continue; + } + const previousBalance = data[index - 1]; + balance.balance = + previousBalance?.balance - (previousBalance?.debit - previousBalance?.credit); } + balances.value = data; } const showNewPaymentDialog = () => { @@ -169,43 +216,25 @@ const showBalancePdf = ({ id }) => { </script> <template> - <FetchData - url="Companies" - auto-load - @on-fetch="(data) => (companies = data)" - ></FetchData> - <FetchData - v-if="companies.length > 0" - url="clientRisks" - :filter="{ - include: { relation: 'company', scope: { fields: ['code'] } }, - where: { clientFk: route.params.id }, - }" - auto-load - @on-fetch="getCurrentBalance" - ></FetchData> - <VnSubToolbar class="q-mb-md"> <template #st-data> <div class="column justify-center q-px-md q-py-sm"> <span class="text-bold">{{ t('Total by company') }}</span> - <div class="row justify-center"> - {{ currentBalance[companyId]?.code }}: - {{ toCurrency(currentBalance[companyId]?.amount) }} + <div class="row justify-center" v-if="clientRisk?.length"> + {{ clientRisk[0].company.code }}: + {{ toCurrency(clientRisk[0].amount) }} </div> </div> </template> <template #st-actions> <div> - <VnSelect - :label="t('Company')" + <VnFilter ref="vnFilterRef" v-model="companyId" data-key="CustomerBalance" - :options="companies" - option-label="code" - option-value="id" - ></VnSelect> + :column="companyFilterColumn" + search-url="balance" + /> </div> </template> </VnSubToolbar> @@ -219,6 +248,7 @@ const showBalancePdf = ({ id }) => { :right-search="false" :is-editable="false" :column-search="false" + @on-fetch="onFetch" :disable-option="{ card: true }" auto-load > diff --git a/src/pages/Customer/Card/CustomerCredits.vue b/src/pages/Customer/Card/CustomerCredits.vue index 377d95412..1fa7047e5 100644 --- a/src/pages/Customer/Card/CustomerCredits.vue +++ b/src/pages/Customer/Card/CustomerCredits.vue @@ -49,7 +49,9 @@ const columns = computed(() => [ name: 'credit', create: true, visible: false, - attrs: { + columnCreate: { + component: 'number', + required: true, autofocus: true, }, }, diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index 43103bd68..40166aefe 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -150,7 +150,7 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit </QCardActions> </template> <template #actions="{ entity }"> - <QCardActions class="flex justify-center"> + <QCardActions class="flex justify-center" style="padding-inline: 0"> <QBtn :to="{ name: 'TicketList', @@ -168,6 +168,23 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit > <QTooltip>{{ t('Customer ticket list') }}</QTooltip> </QBtn> + <QBtn + :to="{ + name: 'TicketList', + query: { + table: JSON.stringify({ + clientFk: entity.id, + }), + createForm: JSON.stringify({ clientId: entity.id }), + }, + }" + size="md" + color="primary" + target="_blank" + icon="vn:ticketAdd" + > + <QTooltip>{{ t('New ticket') }}</QTooltip> + </QBtn> <QBtn :to="{ name: 'InvoiceOutList', @@ -179,6 +196,23 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit > <QTooltip>{{ t('Customer invoice out list') }}</QTooltip> </QBtn> + <QBtn + :to="{ + name: 'OrderList', + query: { + table: JSON.stringify({ + clientFk: entity.id, + }), + createForm: JSON.stringify({ clientFk: entity.id }), + }, + }" + size="md" + target="_blank" + icon="vn:basketadd" + color="primary" + > + <QTooltip>{{ t('New order') }}</QTooltip> + </QBtn> <QBtn :to="{ name: 'AccountSummary', @@ -215,6 +249,8 @@ es: Go to module index: Ir al índice del módulo Customer ticket list: Listado de tickets del cliente Customer invoice out list: Listado de facturas del cliente + New order: Nuevo pedido + New ticket: Nuevo ticket Go to user: Ir al usuario Go to supplier: Ir al proveedor Customer unpaid: Cliente impago diff --git a/src/pages/Customer/Card/CustomerDescriptorMenu.vue b/src/pages/Customer/Card/CustomerDescriptorMenu.vue index 89b10a4fe..a0fbc7f7b 100644 --- a/src/pages/Customer/Card/CustomerDescriptorMenu.vue +++ b/src/pages/Customer/Card/CustomerDescriptorMenu.vue @@ -8,9 +8,6 @@ import { useQuasar } from 'quasar'; import useNotify from 'src/composables/useNotify'; import VnSmsDialog from 'src/components/common/VnSmsDialog.vue'; -import TicketCreateDialog from 'src/pages/Ticket/TicketCreateDialog.vue'; -import OrderCreateDialog from 'src/pages/Order/Card/OrderCreateDialog.vue'; -import { ref } from 'vue'; const $props = defineProps({ customer: { @@ -43,34 +40,9 @@ const sendSms = async (payload) => { notify(error.message, 'positive'); } }; - -const ticketCreateFormDialog = ref(null); -const openTicketCreateForm = () => { - ticketCreateFormDialog.value.show(); -}; -const orderCreateFormDialog = ref(null); -const openOrderCreateForm = () => { - orderCreateFormDialog.value.show(); -}; </script> <template> - <QItem v-ripple clickable @click="openTicketCreateForm()"> - <QItemSection> - {{ t('globals.pageTitles.createTicket') }} - <QDialog ref="ticketCreateFormDialog"> - <TicketCreateDialog /> - </QDialog> - </QItemSection> - </QItem> - <QItem v-ripple clickable @click="openOrderCreateForm()"> - <QItemSection> - {{ t('globals.pageTitles.createOrder') }} - <QDialog ref="orderCreateFormDialog"> - <OrderCreateDialog :client-fk="customer.id" /> - </QDialog> - </QItemSection> - </QItem> <QItem v-ripple clickable> <QItemSection @click="showSmsDialog()">{{ t('Send SMS') }}</QItemSection> </QItem> diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue index 1f5775715..6c5086149 100644 --- a/src/pages/Customer/Card/CustomerFiscalData.vue +++ b/src/pages/Customer/Card/CustomerFiscalData.vue @@ -53,11 +53,11 @@ function handleLocation(data, location) { </QIcon> </template> </VnInput> - <VnInput :label="t('Tax number')" clearable v-model="data.fi" /> + <VnInput :label="t('Tax number')" clearable v-model="data.fi" required /> </VnRow> <VnRow> - <VnInput :label="t('Street')" clearable v-model="data.street" /> + <VnInput :label="t('Street')" clearable v-model="data.street" required /> </VnRow> <VnRow> @@ -68,6 +68,7 @@ function handleLocation(data, location) { option-label="vat" option-value="id" v-model="data.sageTaxTypeFk" + :required="data.isTaxDataChecked" /> <VnSelect :label="t('Sage transaction type')" @@ -76,6 +77,7 @@ function handleLocation(data, location) { option-label="transaction" option-value="id" v-model="data.sageTransactionTypeFk" + :required="data.isTaxDataChecked" > <template #option="scope"> <QItem v-bind="scope.itemProps"> @@ -96,6 +98,7 @@ function handleLocation(data, location) { :roles-allowed-to-create="['deliveryAssistant', 'administrative']" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :location="data" + :required="true" @update:model-value="(location) => handleLocation(data, location)" /> </VnRow> diff --git a/src/pages/Customer/Card/CustomerGreuges.vue b/src/pages/Customer/Card/CustomerGreuges.vue index 1d8b8585f..dcf297d12 100644 --- a/src/pages/Customer/Card/CustomerGreuges.vue +++ b/src/pages/Customer/Card/CustomerGreuges.vue @@ -80,6 +80,11 @@ const columns = computed(() => [ align: 'left', name: 'amount', label: t('Amount'), + columnCreate: { + component: 'number', + autofocus: true, + required: true, + }, format: ({ amount }) => toCurrency(amount), create: true, }, diff --git a/src/pages/Customer/Card/CustomerRecoveries.vue b/src/pages/Customer/Card/CustomerRecoveries.vue index 48576ca20..3a8cffff8 100644 --- a/src/pages/Customer/Card/CustomerRecoveries.vue +++ b/src/pages/Customer/Card/CustomerRecoveries.vue @@ -56,6 +56,7 @@ const columns = computed(() => [ label: t('Period'), create: true, ...componentColumn('number'), + required: true, }, { align: 'left', diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue index 8b5f0f2c2..8e41119ef 100644 --- a/src/pages/Customer/Card/CustomerSummary.vue +++ b/src/pages/Customer/Card/CustomerSummary.vue @@ -1,13 +1,15 @@ <script setup> -import { computed, ref } from 'vue'; +import { computed, ref, onMounted } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; import { toCurrency, toPercentage, toDate } from 'src/filters'; import CardSummary from 'components/ui/CardSummary.vue'; +import { getUrl } from 'src/composables/getUrl'; import VnLv from 'src/components/ui/VnLv.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; +import VnLinkMail from 'src/components/ui/VnLinkMail.vue'; import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryTable.vue'; import VnTitle from 'src/components/common/VnTitle.vue'; import VnRow from 'src/components/ui/VnRow.vue'; @@ -25,6 +27,11 @@ const $props = defineProps({ const entityId = computed(() => $props.id || route.params.id); const customer = computed(() => summary.value.entity); const summary = ref(); +const clientUrl = ref(); + +onMounted(async () => { + clientUrl.value = (await getUrl('client/')) + entityId.value + '/'; +}); const balanceDue = computed(() => { return ( @@ -67,6 +74,7 @@ const sumRisk = ({ clientRisks }) => { ref="summary" :url="`Clients/${entityId}/summary`" data-key="CustomerSummary" + module-name="Customer" > <template #body="{ entity }"> <QCard class="vn-one"> @@ -89,7 +97,11 @@ const sumRisk = ({ clientRisks }) => { <VnLinkPhone :phone-number="entity.mobile" /> </template> </VnLv> - <VnLv :label="t('customer.summary.email')" :value="entity.email" copy /> + <VnLv :value="entity.email" copy + ><template #label> + {{ t('customer.summary.email') }} + <VnLinkMail email="entity.email"></VnLinkMail> </template + ></VnLv> <VnLv :label="t('customer.summary.salesPerson')" :value="entity?.salesPersonUser?.name" @@ -166,7 +178,7 @@ const sumRisk = ({ clientRisks }) => { <QCard class="vn-one"> <VnTitle :url="`#/customer/${entityId}/billing-data`" - :text="t('customer.summary.payMethodFk')" + :text="t('customer.summary.billingData')" /> <VnLv :label="t('customer.summary.payMethod')" @@ -222,6 +234,7 @@ const sumRisk = ({ clientRisks }) => { </QCard> <QCard class="vn-one" v-if="entity.account"> <VnTitle + target="_blank" :url="`${grafanaUrl}/d/adjlxzv5yjt34d/analisis-de-clientes-7c-crm?orgId=1&var-clientFk=${entityId}`" :text="t('customer.summary.businessData')" icon="vn:grafana" @@ -235,6 +248,7 @@ const sumRisk = ({ clientRisks }) => { :value="toCurrency(entity?.mana?.mana)" /> <VnLv + v-if="entity.claimsRatio" :label="t('customer.summary.priceIncreasingRate')" :value="toPercentage(priceIncreasingRate)" /> @@ -243,12 +257,14 @@ const sumRisk = ({ clientRisks }) => { :value="toCurrency(entity?.averageInvoiced?.invoiced)" /> <VnLv + v-if="entity.claimsRatio" :label="t('customer.summary.claimRate')" :value="toPercentage(claimRate)" /> </QCard> <QCard class="vn-one" v-if="entity.account"> <VnTitle + target="_blank" :url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`" :text="t('customer.summary.payMethodFk')" icon="vn:grafana" @@ -268,10 +284,12 @@ const sumRisk = ({ clientRisks }) => { /> <VnLv + v-if="entity.creditInsurance" :label="t('customer.summary.securedCredit')" :value="toCurrency(entity.creditInsurance)" :info="t('customer.summary.securedCreditInfo')" /> + <VnLv :label="t('customer.summary.balance')" :value="toCurrency(sumRisk(entity)) || toCurrency(0)" @@ -301,7 +319,7 @@ const sumRisk = ({ clientRisks }) => { :value="entity.recommendedCredit" /> </QCard> - <QCard> + <QCard class="vn-one"> <VnTitle :text="t('Latest tickets')" /> <CustomerSummaryTable /> </QCard> diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue index 63f5149e8..815ec57fa 100644 --- a/src/pages/Customer/CustomerList.vue +++ b/src/pages/Customer/CustomerList.vue @@ -68,7 +68,6 @@ const columns = computed(() => [ fields: ['id', 'name'], where: { role: 'salesPerson' }, optionFilter: 'firstName', - useLike: false, }, create: false, columnField: { @@ -429,9 +428,10 @@ function handleLocation(data, location) { :params="{ departmentCodes: ['VT', 'shopping'], }" - :fields="['id', 'nickname']" + :fields="['id', 'nickname', 'code']" sort-by="nickname ASC" - :use-like="false" + option-label="nickname" + option-value="id" emit-value auto-load > diff --git a/src/pages/Customer/components/CustomerAddressCreate.vue b/src/pages/Customer/components/CustomerAddressCreate.vue index 659114744..e3fef8e5f 100644 --- a/src/pages/Customer/components/CustomerAddressCreate.vue +++ b/src/pages/Customer/components/CustomerAddressCreate.vue @@ -85,15 +85,26 @@ function handleLocation(data, location) { <QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" /> <VnRow> - <VnInput :label="t('Consignee')" clearable v-model="data.nickname" /> + <VnInput + :label="t('Consignee')" + required + clearable + v-model="data.nickname" + /> - <VnInput :label="t('Street address')" clearable v-model="data.street" /> + <VnInput + :label="t('Street address')" + clearable + v-model="data.street" + required + /> </VnRow> <VnLocation :rules="validate('Worker.postcode')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" v-model="data.location" + :required="true" @update:model-value="(location) => handleLocation(data, location)" /> diff --git a/src/pages/Customer/components/CustomerNewPayment.vue b/src/pages/Customer/components/CustomerNewPayment.vue index 16dd28767..291f28642 100644 --- a/src/pages/Customer/components/CustomerNewPayment.vue +++ b/src/pages/Customer/components/CustomerNewPayment.vue @@ -3,7 +3,7 @@ import { onBeforeMount, reactive, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import axios from 'axios'; - +import { getClientRisk } from '../composables/getClientRisk'; import { useDialogPluginComponent } from 'quasar'; import { usePrintService } from 'composables/usePrintService'; @@ -158,9 +158,7 @@ async function getAmountPaid() { }, }; - const { data } = await axios(`ClientRisks`, { - params: { filter: JSON.stringify(filter) }, - }); + const { data } = await getClientRisk(filter); initialData.amountPaid = (data?.length && data[0].amount) || undefined; } </script> @@ -241,7 +239,7 @@ async function getAmountPaid() { </QItem> </template> </VnSelect> - <VnInput + <VnInputNumber :label="t('Amount')" :required="true" @update:model-value="calculateFromAmount($event)" @@ -254,7 +252,7 @@ async function getAmountPaid() { {{ t('Compensation') }} </div> <VnRow> - <VnInput + <VnInputNumber :label="t('Compensation account')" clearable v-model="data.compensationAccount" diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue index e9bb36be7..745cbf29e 100644 --- a/src/pages/Customer/components/CustomerSummaryTable.vue +++ b/src/pages/Customer/components/CustomerSummaryTable.vue @@ -63,7 +63,7 @@ const columns = computed(() => [ }, { align: 'left', - format: (row) => row.agencyMode.name, + format: (row) => dashIfEmpty(row.agencyMode?.name), columnClass: 'expand', label: t('Agency'), }, @@ -111,7 +111,11 @@ const columns = computed(() => [ { title: t('customer.summary.goToLines'), icon: 'vn:lines', - action: ({ id }) => router.push({ params: { id }, name: 'TicketSale' }), + action: ({ id }) => + window.open( + router.resolve({ params: { id }, name: 'TicketSale' }).href, + '_blank' + ), isPrimary: true, }, { @@ -150,6 +154,8 @@ const setShippedColor = (date) => { if (difference == 0) return 'warning'; if (difference < 0) return 'success'; }; +const rowClick = ({ id }) => + window.open(router.resolve({ params: { id }, name: 'TicketSummary' }).href, '_blank'); const getItemPackagingType = (ticketSales) => { if (!ticketSales?.length) return '-'; @@ -177,13 +183,15 @@ const getItemPackagingType = (ticketSales) => { :column-search="false" url="Tickets" :columns="columns" - search-url="tickets" + append-params="false" :without-header="true" auto-load + :row-click="rowClick" order="shipped DESC, id" :disable-option="{ card: true, table: true }" class="full-width" :disable-infinite-scroll="true" + search-url="tickets" > <template #column-nickname="{ row }"> <span class="link"> diff --git a/src/pages/Customer/composables/getClientRisk.js b/src/pages/Customer/composables/getClientRisk.js new file mode 100644 index 000000000..ebaf545ee --- /dev/null +++ b/src/pages/Customer/composables/getClientRisk.js @@ -0,0 +1,12 @@ +import axios from 'axios'; + +export async function getClientRisk(_filter) { + const filter = { + ..._filter, + include: { relation: 'company', scope: { fields: ['code'] } }, + }; + + return await axios(`ClientRisks`, { + params: { filter: JSON.stringify(filter) }, + }); +} diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index 22bca15ac..93e53b9f6 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -28,7 +28,7 @@ describe('Client list', () => { cy.get('.q-mt-lg > .q-btn--standard').click(); - cy.checkNotification('Data created'); + cy.checkNotification('created'); cy.url().should('include', '/summary'); }); it('Client list search client', () => { @@ -43,4 +43,21 @@ describe('Client list', () => { cy.url().should('include', `/customer/${id}/summary`); }); }); + + it('Client founded create ticket', () => { + const search = 'Jessica Jones'; + cy.searchByLabel('Name', search); + cy.clickButtonsDescriptor(2); + cy.waitForElement('#formModel'); + cy.waitForElement('.q-form'); + cy.checkValueForm(1, search); + }); + it('Client founded create order', () => { + const search = 'Jessica Jones'; + cy.searchByLabel('Name', search); + cy.clickButtonsDescriptor(4); + cy.waitForElement('#formModel'); + cy.waitForElement('.q-form'); + cy.checkValueForm(2, search); + }); }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 76bdefd27..6fea4559a 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -264,6 +264,12 @@ Cypress.Commands.add('openActionsDescriptor', () => { cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click(); }); +Cypress.Commands.add('clickButtonsDescriptor', (id) => { + cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`) + .invoke('removeAttr', 'target') + .click(); +}); + Cypress.Commands.add('openUserPanel', () => { cy.get( '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image' @@ -274,14 +280,25 @@ Cypress.Commands.add('openActions', (row) => { cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); }); -Cypress.Commands.add('checkNotification', (text) => { - cy.get('.q-notification') - .should('be.visible') - .last() - .then(($lastNotification) => { - if (!Cypress.$($lastNotification).text().includes(text)) - throw new Error(`Notification not found: "${text}"`); - }); +Cypress.Commands.add('checkNotification', (type) => { + const values = { + created: 'Data created', + updated: 'Data saved', + deleted: 'Data deleted', + }; + cy.get('.q-notification__message').should('have.text', values[type]); +}); + +Cypress.Commands.add('checkValueForm', (id, search) => { + cy.get( + `.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input` + ).should('have.value', search); +}); + +Cypress.Commands.add('checkValueSelectForm', (id, search) => { + cy.get( + `.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input` + ).should('have.value', search); }); Cypress.Commands.add('searchByLabel', (label, value) => { From 0b2c404ab336d4e0b663b4cdc41b3fb0b772b7a8 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 02:15:21 +0100 Subject: [PATCH 121/150] perf: minor bugs detected --- src/components/VnTable/VnTable.vue | 4 ++++ src/components/common/VnTitle.vue | 2 +- src/components/ui/CardSummary.vue | 20 +++----------------- src/composables/useArrayData.js | 7 ++++--- src/filters/index.js | 2 ++ src/filters/isDialogOpened.js | 3 +++ 6 files changed, 17 insertions(+), 21 deletions(-) create mode 100644 src/filters/isDialogOpened.js diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index c1680bf13..cdf450966 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -73,6 +73,10 @@ const $props = defineProps({ type: Boolean, default: false, }, + appendParams: { + type: Boolean, + default: true, + }, hasSubToolbar: { type: Boolean, default: null, diff --git a/src/components/common/VnTitle.vue b/src/components/common/VnTitle.vue index 1fbd43972..89dd8cd0c 100644 --- a/src/components/common/VnTitle.vue +++ b/src/components/common/VnTitle.vue @@ -8,7 +8,7 @@ defineProps({ <template> <div :class="$q.screen.gt.md ? 'q-pb-lg' : 'q-pb-md'"> <div class="header-link" :style="{ cursor: url ? 'pointer' : 'default' }"> - <a :href="url" :class="url ? 'link' : 'color-vn-text'"> + <a :href="url" :class="url ? 'link' : 'color-vn-text'" v-bind="$attrs"> {{ text }} <QIcon v-if="url" :name="icon" /> </a> diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index 11dcbee3b..f469aa799 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -4,6 +4,7 @@ import { useRoute } from 'vue-router'; import SkeletonSummary from 'components/ui/SkeletonSummary.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import { useArrayData } from 'src/composables/useArrayData'; +import { isDialogOpened } from 'src/filters'; const props = defineProps({ url: { @@ -58,22 +59,6 @@ async function fetch() { emit('onFetch', Array.isArray(data) ? data[0] : data); isLoading.value = false; } - -const showRedirectToSummaryIcon = computed(() => { - const exist = existSummary(route.matched); - return !isSummary.value && route.meta.moduleName && exist; -}); - -function existSummary(routes) { - const hasSummary = routes.some((r) => r.name === `${route.meta.moduleName}Summary`); - if (hasSummary) return hasSummary; - for (const current of routes) { - if (current.path != '/' && current.children) { - const exist = existSummary(current.children); - if (exist) return exist; - } - } -} </script> <template> @@ -84,7 +69,7 @@ function existSummary(routes) { <div class="summaryHeader bg-primary q-pa-sm text-weight-bolder"> <slot name="header-left"> <router-link - v-if="showRedirectToSummaryIcon" + v-if="isDialogOpened()" class="header link" :to="{ name: `${moduleName ?? route.meta.moduleName}Summary`, @@ -118,6 +103,7 @@ function existSummary(routes) { .cardSummary { width: 100%; + max-height: 70vh; .summaryHeader { text-align: center; font-size: 20px; diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index a2eaa649a..0a060e4c6 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -3,6 +3,7 @@ import { useRouter, useRoute } from 'vue-router'; import axios from 'axios'; import { useArrayDataStore } from 'stores/useArrayDataStore'; import { buildFilter } from 'filters/filterPanel'; +import { isDialogOpened } from 'src/filters'; const arrayDataStore = useArrayDataStore(); @@ -114,8 +115,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { for (const row of response.data) store.data.push(row); } else { store.data = response.data; - if (!document.querySelectorAll('[role="dialog"][aria-modal="true"]').length) - updateRouter && updateStateParams(); + if (!isDialogOpened()) updateRouter && updateStateParams(); } store.isLoading = false; @@ -249,7 +249,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { function updateStateParams() { if (!route) return; const newUrl = { path: route.path, query: { ...(route.query ?? {}) } }; - newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter); + if (store.appendParams) + newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter); if (store.navigate) { const { customRouteRedirectName, searchText } = store.navigate; diff --git a/src/filters/index.js b/src/filters/index.js index 5f08f19c7..ce5c44706 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -12,8 +12,10 @@ import dateRange from './dateRange'; import toHour from './toHour'; import dashOrCurrency from './dashOrCurrency'; import getParamWhere from './getParamWhere'; +import isDialogOpened from './isDialogOpened'; export { + isDialogOpened, toLowerCase, toLowerCamel, toDate, diff --git a/src/filters/isDialogOpened.js b/src/filters/isDialogOpened.js new file mode 100644 index 000000000..9d6f3895e --- /dev/null +++ b/src/filters/isDialogOpened.js @@ -0,0 +1,3 @@ +export default function isDialogOpened(query = '[role="dialog"]') { + return document.querySelectorAll(query).length > 0; +} From 4b00ab877c9d1a8455e368087cd74dff85f1dab0 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 02:26:35 +0100 Subject: [PATCH 122/150] perf: use required instead :required="true" --- src/components/common/VnInput.vue | 10 +++++----- src/components/common/VnInputDate.vue | 9 +++++---- src/components/common/VnInputNumber.vue | 7 ++++++- src/components/common/VnInputTime.vue | 7 ++++--- src/components/common/VnLocation.vue | 14 ++++++++++++-- src/components/common/VnSelect.vue | 11 ++++++----- 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 1246eedcd..4534ae4a0 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -2,6 +2,7 @@ import { computed, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useValidator } from 'src/composables/useValidator'; +import { useAttrs } from 'vue'; const emit = defineEmits([ 'update:modelValue', @@ -29,10 +30,11 @@ const $props = defineProps({ }, }); const { validations } = useValidator(); +const $attrs = useAttrs(); const { t } = useI18n(); -const requiredFieldRule = (val) => validations().required($attrs.required, val); - +const isRequired = computed(() => Object.keys($attrs).includes('required')); +const requiredFieldRule = (val) => validations().required(isRequired.value, val); const vnInputRef = ref(null); const value = computed({ get() { @@ -60,8 +62,6 @@ const focus = () => { defineExpose({ focus, }); -import { useAttrs } from 'vue'; -const $attrs = useAttrs(); const mixinRules = [ requiredFieldRule, @@ -85,7 +85,7 @@ const mixinRules = [ v-model="value" v-bind="{ ...$attrs, ...styleAttrs }" :type="$attrs.type" - :class="{ required: $attrs.required }" + :class="{ required: isRequired }" @keyup.enter="emit('keyup.enter')" :clearable="false" :rules="mixinRules" diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue index 1aa797ab7..87833f411 100644 --- a/src/components/common/VnInputDate.vue +++ b/src/components/common/VnInputDate.vue @@ -6,6 +6,8 @@ import { useAttrs } from 'vue'; import VnDate from './VnDate.vue'; const model = defineModel({ type: [String, Date] }); +const $attrs = useAttrs(); +const { t } = useI18n(); const $props = defineProps({ isOutlined: { type: Boolean, @@ -19,15 +21,14 @@ const $props = defineProps({ import { useValidator } from 'src/composables/useValidator'; const { validations } = useValidator(); -const { t } = useI18n(); -const requiredFieldRule = (val) => validations().required($attrs.required, val); +const isRequired = computed(() => Object.keys($attrs).includes('required')); +const requiredFieldRule = (val) => validations().required(isRequired.value, val); const vnInputDateRef = ref(null); const dateFormat = 'DD/MM/YYYY'; const isPopupOpen = ref(); const hover = ref(); const mask = ref(); -const $attrs = useAttrs(); const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; @@ -104,7 +105,7 @@ const manageDate = (date) => { :mask="mask" placeholder="dd/mm/aaaa" v-bind="{ ...$attrs, ...styleAttrs }" - :class="{ required: $attrs.required }" + :class="{ required: isRequired }" :rules="mixinRules" :clearable="false" @click="isPopupOpen = true" diff --git a/src/components/common/VnInputNumber.vue b/src/components/common/VnInputNumber.vue index ef4bb7512..1cad6c245 100644 --- a/src/components/common/VnInputNumber.vue +++ b/src/components/common/VnInputNumber.vue @@ -1,8 +1,13 @@ <script setup> import VnInput from 'src/components/common/VnInput.vue'; +import { ref } from 'vue'; +import { useAttrs } from 'vue'; + const model = defineModel({ type: [Number, String] }); +const $attrs = useAttrs(); +const step = ref($attrs.step || 0.01); </script> <template> - <VnInput v-bind="$attrs" v-model.number="model" type="number" /> + <VnInput v-bind="$attrs" v-model.number="model" type="number" :step="step" /> </template> diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue index 6d69bc4a5..13e90e021 100644 --- a/src/components/common/VnInputTime.vue +++ b/src/components/common/VnInputTime.vue @@ -6,6 +6,7 @@ import { useValidator } from 'src/composables/useValidator'; import VnTime from './VnTime.vue'; const { validations } = useValidator(); +const { t } = useI18n(); const $attrs = useAttrs(); const model = defineModel({ type: String }); const props = defineProps({ @@ -20,8 +21,8 @@ const props = defineProps({ }); const vnInputTimeRef = ref(null); const initialDate = ref(model.value ?? Date.vnNew()); -const { t } = useI18n(); -const requiredFieldRule = (val) => validations().required($attrs.required, val); +const isRequired = computed(() => Object.keys($attrs).includes('required')); +const requiredFieldRule = (val) => validations().required(isRequired.value, val); const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const dateFormat = 'HH:mm'; const isPopupOpen = ref(); @@ -78,7 +79,7 @@ function dateToTime(newDate) { placeholder="--:--" v-model="formattedTime" v-bind="{ ...$attrs, ...styleAttrs }" - :class="{ required: $attrs.required }" + :class="{ required: isRequired }" style="min-width: 100px" :rules="mixinRules" @click="isPopupOpen = false" diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue index 5f94c466a..7c6e46fdd 100644 --- a/src/components/common/VnLocation.vue +++ b/src/components/common/VnLocation.vue @@ -2,10 +2,14 @@ import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue'; import VnSelectDialog from 'components/common/VnSelectDialog.vue'; import { useI18n } from 'vue-i18n'; -import { ref } from 'vue'; +import { computed, ref } from 'vue'; const { t } = useI18n(); const emit = defineEmits(['update:model-value', 'update:options']); +const { validations } = useValidator(); +import { useAttrs } from 'vue'; +import { useValidator } from 'src/composables/useValidator'; +const $attrs = useAttrs(); const props = defineProps({ location: { type: Object, @@ -13,6 +17,10 @@ const props = defineProps({ }, }); +const isRequired = computed(() => Object.keys($attrs).includes('required')); +const requiredFieldRule = (val) => validations().required(isRequired.value, val); + +const mixinRules = [requiredFieldRule]; const locationProperties = [ 'postcode', (obj) => @@ -69,11 +77,13 @@ const handleModelValue = (data) => { :label="t('Location')" :placeholder="t('search_by_postalcode')" :input-debounce="300" - :class="{ required: $attrs.required }" + :class="{ required: isRequired }" v-bind="$attrs" clearable :emit-value="false" :tooltip="t('Create new location')" + :rules="mixinRules" + :lazy-rules="true" > <template #form> <CreateNewPostcode diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index b0aa648c1..4a4730784 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -4,7 +4,8 @@ import { useI18n } from 'vue-i18n'; import FetchData from 'src/components/FetchData.vue'; import { useValidator } from 'src/composables/useValidator'; const emit = defineEmits(['update:modelValue', 'update:options', 'remove']); - +const $attrs = useAttrs(); +const { t } = useI18n(); const $props = defineProps({ modelValue: { type: [String, Number, Object], @@ -88,9 +89,9 @@ const $props = defineProps({ }, }); const { validations } = useValidator(); -const requiredFieldRule = (val) => validations().required($attrs.required, val); -const $attrs = useAttrs(); -const { t } = useI18n(); + +const isRequired = computed(() => Object.keys($attrs).includes('required')); +const requiredFieldRule = (val) => validations().required(isRequired.value, val); const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } = toRefs($props); @@ -257,7 +258,7 @@ defineExpose({ opts: myOptions }); :fill-input="nullishToTrue($attrs['fill-input'])" ref="vnSelectRef" lazy-rules - :class="{ required: $attrs.required }" + :class="{ required: isRequired }" :rules="mixinRules" virtual-scroll-slice-size="options.length" hide-bottom-space From d2680b0a1fcccef8b3b5daa9a2ee6b12ddacad34 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 02:38:44 +0100 Subject: [PATCH 123/150] test: fix arrayData --- src/composables/useArrayData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 0a060e4c6..e33cb8b78 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -249,7 +249,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { function updateStateParams() { if (!route) return; const newUrl = { path: route.path, query: { ...(route.query ?? {}) } }; - if (store.appendParams) + if (store?.appendParams ?? true) newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter); if (store.navigate) { From 96f894cc6c6b9be2dc235323a0a874a86b548eb3 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 29 Oct 2024 08:17:18 +0100 Subject: [PATCH 124/150] fix: refs #7283 filter --- src/pages/Item/ItemList.vue | 2 +- src/pages/Item/ItemRequest.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index a6873d10c..a480cfff6 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -215,7 +215,7 @@ const columns = computed(() => [ columnFilter: { name: 'workerFk', attrs: { - url: 'VnUsers/preview', + url: 'TicketRequests/getItemTypeWorker', optionValue: 'id', optionLabel: 'nickname', }, diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 36da0368b..61156640e 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -104,7 +104,7 @@ const columns = computed(() => [ columnFilter: { name: 'attenderFk', attrs: { - url: 'VnUsers/preview', + url: 'TicketRequests/getItemTypeWorker', optionValue: 'id', optionLabel: 'nickname', }, From 42fbdff5bd2e4d4e00d4514b35b861440e032ac1 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 29 Oct 2024 08:48:06 +0100 Subject: [PATCH 125/150] test: fix e2e --- src/components/ui/VnConfirm.vue | 1 + test/cypress/integration/outLogin/logout.spec.js | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index ec9d0f48b..0abfc84bc 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -34,6 +34,7 @@ const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); +defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() }); const title = props.title || t('Confirm'); const message = diff --git a/test/cypress/integration/outLogin/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js index 8d4e90aac..d6c33a65b 100644 --- a/test/cypress/integration/outLogin/logout.spec.js +++ b/test/cypress/integration/outLogin/logout.spec.js @@ -13,7 +13,7 @@ describe('Logout', () => { }); describe('not user', () => { beforeEach(() => { - cy.intercept('GET', '**DefaultViewConfigs**', { + cy.intercept('GET', '**StarredModules**', { statusCode: 401, body: { error: { @@ -29,10 +29,7 @@ describe('Logout', () => { it('when token not exists', () => { cy.get('.q-list > [href="#/item"]').click(); - cy.get('.q-notification__message').should( - 'have.text', - 'Authorization Required' - ); + cy.checkNotification('Authorization Required'); }); }); }); From 5663064dd8ca211f328e96e0bba902685cec0f9a Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Tue, 29 Oct 2024 09:23:49 +0100 Subject: [PATCH 126/150] refactor: modified composable --- src/pages/Order/Card/OrderLines.vue | 7 +++++-- src/pages/Order/Card/OrderSummary.vue | 9 +++++++-- src/pages/Order/composables/confirmOrder.js | 21 ++------------------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index 90586dc15..58c5c9551 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -6,7 +6,7 @@ import { useQuasar } from 'quasar'; import axios from 'axios'; import { useStateStore } from 'stores/useStateStore'; import { useArrayData } from 'composables/useArrayData'; -import { confirmOrder } from 'composables/confirmOrder'; +import { confirm } from 'src/pages/Order/composables/confirmOrder'; import { toCurrency, toDate } from 'src/filters'; import VnConfirm from 'components/ui/VnConfirm.vue'; @@ -32,7 +32,6 @@ const orderSummary = ref({ }); const getTotalRef = ref(); const getVATRef = ref(); -const { confirm } = confirmOrder(); const lineFilter = ref({ include: [ { @@ -208,6 +207,10 @@ async function remove(item) { async function handleConfirm() { const result = await confirm(route.params.id); if (result) { + quasar.notify({ + message: t('globals.dataSaved'), + type: 'positive', + }); router.push({ name: 'TicketList', query: { diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue index 3669fe8d8..032af1993 100644 --- a/src/pages/Order/Card/OrderSummary.vue +++ b/src/pages/Order/Card/OrderSummary.vue @@ -2,9 +2,10 @@ import { computed, ref } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; +import { useQuasar } from 'quasar'; import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters'; import { useArrayData } from 'composables/useArrayData'; -import { confirmOrder } from 'composables/confirmOrder'; +import { confirm } from 'src/pages/Order/composables/confirmOrder'; import VnLv from 'components/ui/VnLv.vue'; import CardSummary from 'components/ui/CardSummary.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; @@ -24,8 +25,8 @@ const $props = defineProps({ const entityId = computed(() => $props.id || route.params.id); const summary = ref(); +const quasar = useQuasar(); const descriptorData = useArrayData('orderData'); -const { confirm } = confirmOrder(); const detailsColumns = ref([ { name: 'item', @@ -58,6 +59,10 @@ const detailsColumns = ref([ async function handleConfirm() { const result = await confirm(route.params.id); if (result) { + quasar.notify({ + message: t('globals.dataSaved'), + type: 'positive', + }); summary.value.fetch({}); descriptorData.fetch({}); } diff --git a/src/pages/Order/composables/confirmOrder.js b/src/pages/Order/composables/confirmOrder.js index 33e05a1d7..b9eb5d7ac 100644 --- a/src/pages/Order/composables/confirmOrder.js +++ b/src/pages/Order/composables/confirmOrder.js @@ -1,22 +1,5 @@ import axios from 'axios'; -import { useQuasar } from 'quasar'; -import { useI18n } from 'vue-i18n'; -export function confirmOrder() { - const quasar = useQuasar(); - const { t } = useI18n(); - - async function confirm(route) { - const { data } = await axios.post(`Orders/${route}/confirm`); - if (data) { - quasar.notify({ - message: t('globals.confirm'), - type: 'positive', - }); - return data; - } - return null; - } - - return { confirm }; +export async function confirm(routeId) { + return await axios.post(`Orders/${routeId}/confirm`); } From 77cb2d84be6cb11f02effc49371a0ac70e580df2 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 29 Oct 2024 09:38:40 +0100 Subject: [PATCH 127/150] fix: refs #8078 handleSelection --- src/components/VnTable/VnTable.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 74a96e0e3..a15ba2f47 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -335,8 +335,9 @@ function handleScroll() { function handleSelection({ evt, added, rows: selectedRows }, rows) { if (evt?.shiftKey && added) { const rowIndex = selectedRows[0].$index; + selected.value.length = 0; for (const row of rows) { - if (row.$index > rowIndex) break; + if (row.$index == rowIndex) break; selected.value.push(row); } } @@ -440,7 +441,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) { :virtual-scroll="isTableMode" @virtual-scroll="handleScroll" @row-click="(_, row) => rowClickFunction && rowClickFunction(row)" - @update:selected="emit('update:selected', $event)" @selection="(details) => handleSelection(details, rows)" > <template #top-left v-if="!$props.withoutHeader"> From 19a7e526059a6b41cd7fb5902f0502aed294b4bd Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 29 Oct 2024 09:39:56 +0100 Subject: [PATCH 128/150] fix: refs #8078 handleSelection --- src/components/VnTable/VnTable.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index a15ba2f47..1e949120f 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -441,6 +441,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) { :virtual-scroll="isTableMode" @virtual-scroll="handleScroll" @row-click="(_, row) => rowClickFunction && rowClickFunction(row)" + @update:selected="emit('update:selected', $event)" @selection="(details) => handleSelection(details, rows)" > <template #top-left v-if="!$props.withoutHeader"> From e39f85ff4bcd45993a65fba6b01a9ef1aac30df8 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 29 Oct 2024 09:56:06 +0100 Subject: [PATCH 129/150] fix: refs #8078 improve handleSelection --- src/components/VnTable/VnTable.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 1e949120f..dc8671369 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -335,10 +335,13 @@ function handleScroll() { function handleSelection({ evt, added, rows: selectedRows }, rows) { if (evt?.shiftKey && added) { const rowIndex = selectedRows[0].$index; - selected.value.length = 0; + const selectedIndexes = new Set(selected.value.map((row) => row.$index)); for (const row of rows) { if (row.$index == rowIndex) break; - selected.value.push(row); + if (!selectedIndexes.has(row.$index)) { + selected.value.push(row); + selectedIndexes.add(row.$index); + } } } } From 476ef1dedd7907586b27d80433427f2faf6fefe4 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Tue, 29 Oct 2024 13:14:42 +0100 Subject: [PATCH 130/150] feat: refs #7206 added inactive label and corrected minor errors --- src/components/ui/VnConfirm.vue | 1 + .../components/CustomerSummaryTable.vue | 2 +- src/pages/Order/Card/OrderCreateDialog.vue | 1 - src/pages/Order/OrderList.vue | 32 ++++++++++++++++--- src/pages/Ticket/Card/TicketSummary.vue | 5 --- src/pages/Ticket/TicketList.vue | 29 +++++++++++------ 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index ec9d0f48b..d6b1ac0a3 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -31,6 +31,7 @@ const props = defineProps({ }); const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]); +defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() }); const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue index e9bb36be7..e9e30506b 100644 --- a/src/pages/Customer/components/CustomerSummaryTable.vue +++ b/src/pages/Customer/components/CustomerSummaryTable.vue @@ -63,7 +63,7 @@ const columns = computed(() => [ }, { align: 'left', - format: (row) => row.agencyMode.name, + format: (row, dashIfEmpty) => dashIfEmpty(row.agencyMode?.name), columnClass: 'expand', label: t('Agency'), }, diff --git a/src/pages/Order/Card/OrderCreateDialog.vue b/src/pages/Order/Card/OrderCreateDialog.vue index bcc62aa43..1239d195b 100644 --- a/src/pages/Order/Card/OrderCreateDialog.vue +++ b/src/pages/Order/Card/OrderCreateDialog.vue @@ -10,7 +10,6 @@ import VnSelect from 'components/common/VnSelect.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; import { useDialogPluginComponent } from 'quasar'; import { reactive } from 'vue'; -import FetchData from 'components/FetchData.vue'; const { t } = useI18n(); const state = useState(); diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index 6b6b41828..e01790c6b 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -233,7 +233,20 @@ onMounted(() => { v-model="data.clientFk" :label="t('module.customer')" @update:model-value="(id) => fetchClientAddress(id, data)" - /> + > + <template #option="scope"> + <QItem v-bind="scope.itemProps"> + <QItemSection> + <QItemLabel> + {{ scope.opt.name }} + </QItemLabel> + <QItemLabel caption> + {{ `#${scope.opt.id}` }} + </QItemLabel> + </QItemSection> + </QItem> + </template> + </VnSelect> <VnSelect v-model="data.addressId" :options="addressesList" @@ -245,10 +258,21 @@ onMounted(() => { <template #option="scope"> <QItem v-bind="scope.itemProps"> <QItemSection> - <QItemLabel> - {{ scope.opt?.nickname }}: {{ scope.opt?.street }}, - {{ scope.opt?.city }}</QItemLabel + <QItemLabel + :class="{ + 'color-vn-label': !scope.opt?.isActive, + }" > + {{ + `${ + !scope.opt?.isActive + ? t('basicData.inactive') + : '' + } ` + }} + {{ scope.opt?.nickname }}: {{ scope.opt?.street }}, + {{ scope.opt?.city }} + </QItemLabel> </QItemSection> </QItem> </template> diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 5fb99b849..61751357c 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -19,7 +19,6 @@ import VnTitle from 'src/components/common/VnTitle.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; -import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; import VnToSummary from 'src/components/ui/VnToSummary.vue'; const route = useRoute(); @@ -87,10 +86,6 @@ async function changeState(value) { function toTicketUrl(section) { return '#/ticket/' + entityId.value + '/' + section; } -function isOnTicketCard() { - const currentPath = route.path; - return currentPath.startsWith('/ticket'); -} </script> <template> diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index dd1f2d69a..4317efb83 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -268,8 +268,7 @@ const fetchAddresses = async (formData) => { if (!formData.clientId) return; const filter = { - fields: ['nickname', 'street', 'city', 'id'], - where: { isActive: true }, + fields: ['nickname', 'street', 'city', 'id', 'isActive'], order: 'nickname ASC', }; const params = { filter: JSON.stringify(filter) }; @@ -635,24 +634,36 @@ function setReference(data) { </VnRow> <VnRow> <VnSelect - url="Addresses" - :label="t('ticket.create.address')" + :label="t('basicData.address')" v-model="data.addressId" :options="addressesOptions" option-value="id" option-label="nickname" hide-selected + map-options :disable="!data.clientId" + :sort-by="'isActive DESC'" @update:model-value="() => fetchAvailableAgencies(data)" > <template #option="scope"> <QItem v-bind="scope.itemProps"> <QItemSection> - <QItemLabel> - {{ scope.opt.nickname }} - </QItemLabel> - <QItemLabel caption> - {{ `${scope.opt.street}, ${scope.opt.city}` }} + <QItemLabel + :class="{ + 'color-vn-label': !scope.opt?.isActive, + }" + > + {{ + `${ + !scope.opt?.isActive + ? t('basicData.inactive') + : '' + } ` + }} + <span> + {{ scope.opt?.nickname }}: + {{ scope.opt?.street }}, {{ scope.opt?.city }} + </span> </QItemLabel> </QItemSection> </QItem> From 282999c49e52e5e36369d82b28b5a55ba3469c3c Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Tue, 29 Oct 2024 13:23:15 +0100 Subject: [PATCH 131/150] fix: refs #7206 deleted duplicate code --- src/components/ui/VnConfirm.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index 081812093..d6b1ac0a3 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -35,7 +35,6 @@ defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.h const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); -defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() }); const title = props.title || t('Confirm'); const message = From ed8225bf6c0285ef9f89af4ae8d1706b3edbc555 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 14:23:06 +0100 Subject: [PATCH 132/150] feat: #7782 waitUntil domContentLoad --- .../vnComponent/vnLocation.spec.js | 2 +- test/cypress/support/commands.js | 12 +++- test/cypress/support/waitUntil.js | 59 +++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 test/cypress/support/waitUntil.js diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js index c1b0cf929..924b16adc 100644 --- a/test/cypress/integration/vnComponent/vnLocation.spec.js +++ b/test/cypress/integration/vnComponent/vnLocation.spec.js @@ -12,7 +12,7 @@ describe('VnLocation', () => { cy.viewport(1280, 720); cy.login('developer'); cy.visit('/#/supplier/567/fiscal-data', { timeout: 7000 }); - cy.waitForElement('.q-card'); + cy.domContentLoad(); cy.get(createLocationButton).click(); }); it('should filter provinces based on selected country', () => { diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 76bdefd27..5bb89ecf9 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -27,6 +27,10 @@ // DO NOT REMOVE // Imports Quasar Cypress AE predefined commands // import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress'; +Cypress.Commands.add('waitUntil', { prevSubject: 'optional' }, require('./waitUntil')); +Cypress.Commands.add('resetDB', () => { + cy.exec('pnpm run resetDatabase'); +}); Cypress.Commands.add('login', (user) => { //cy.visit('/#/login'); cy.request({ @@ -50,10 +54,12 @@ Cypress.Commands.add('login', (user) => { }); }); -Cypress.Commands.add('waitForElement', (element, timeout = 5000) => { - cy.get(element, { timeout }).should('be.visible'); +Cypress.Commands.add('domContentLoad', (element, timeout = 5000) => { + cy.waitUntil(() => cy.document().then((doc) => doc.readyState === 'complete')); +}); +Cypress.Commands.add('waitForElement', (element, timeout = 5000) => { + cy.waitUntil(() => cy.get(element).then(($el) => $el.is(':visible'))); }); - Cypress.Commands.add('getValue', (selector) => { cy.get(selector).then(($el) => { if ($el.find('.q-checkbox__inner').length > 0) { diff --git a/test/cypress/support/waitUntil.js b/test/cypress/support/waitUntil.js new file mode 100644 index 000000000..5fb47a2d8 --- /dev/null +++ b/test/cypress/support/waitUntil.js @@ -0,0 +1,59 @@ +const waitUntil = (subject, checkFunction, originalOptions = {}) => { + if (!(checkFunction instanceof Function)) { + throw new Error( + '`checkFunction` parameter should be a function. Found: ' + checkFunction + ); + } + + const defaultOptions = { + // base options + interval: 200, + timeout: 5000, + errorMsg: 'Timed out retrying', + + // log options + description: 'waitUntil', + log: true, + customMessage: undefined, + logger: Cypress.log, + verbose: false, + customCheckMessage: undefined, + }; + const options = { ...defaultOptions, ...originalOptions }; + + // filter out a falsy passed "customMessage" value + options.customMessage = [options.customMessage, originalOptions].filter(Boolean); + + const endTime = Date.now() + options.timeout; + + const check = (result) => { + if (result) { + return result; + } + if (Date.now() >= endTime) { + const msg = + options.errorMsg instanceof Function + ? options.errorMsg(result, options) + : options.errorMsg; + throw new Error(msg); + } + cy.wait(options.interval, { log: false }).then(() => { + return resolveValue(); + }); + }; + + const resolveValue = () => { + const result = checkFunction(subject); + + const isAPromise = Boolean(result && result.then); + if (isAPromise) { + return result.then(check); + } else { + return check(result); + } + }; + + return resolveValue(); +}; + +export default waitUntil; From b817aa92c2b4f09dd4c282d2313551acf8f5be0b Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 14:23:22 +0100 Subject: [PATCH 133/150] feat: #7782 npm run resetDatabase --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a61c8f21a..71e95667c 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,11 @@ "private": true, "packageManager": "pnpm@8.15.1", "scripts": { + "resetDatabase": "cd ../salix && gulp docker", "lint": "eslint --ext .js,.vue ./", "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", "test:e2e": "cypress open", - "test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run", + "test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run", "test": "echo \"See package.json => scripts for available tests.\" && exit 0", "test:unit": "vitest", "test:unit:ci": "vitest run", From 2090b78ce6005df77178925f6e04a3b8c39bce86 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 14:25:34 +0100 Subject: [PATCH 134/150] feat: #7782 cypress.config watchForFileChanges --- cypress.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress.config.js b/cypress.config.js index e2046d6c4..1b3e0190f 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -11,6 +11,7 @@ module.exports = defineConfig({ video: false, specPattern: 'test/cypress/integration/**/*.spec.js', experimentalRunAllSpecs: true, + watchForFileChanges: true, component: { componentFolder: 'src', testFiles: '**/*.spec.js', From fbbbc331a951734b09cc7bd084209facdb1143cb Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 14:27:24 +0100 Subject: [PATCH 135/150] feat: #7782 add cypress report --- cypress.config.js | 9 ++ package.json | 1 + pnpm-lock.yaml | 379 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 380 insertions(+), 9 deletions(-) diff --git a/cypress.config.js b/cypress.config.js index 1b3e0190f..f8e771093 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -12,12 +12,21 @@ module.exports = defineConfig({ specPattern: 'test/cypress/integration/**/*.spec.js', experimentalRunAllSpecs: true, watchForFileChanges: true, + reporter: 'cypress-mochawesome-reporter', + reporterOptions: { + charts: true, + reportPageTitle: 'Cypress Inline Reporter', + embeddedScreenshots: true, + reportDir: 'test/cypress/reports', + inlineAssets: true, + }, component: { componentFolder: 'src', testFiles: '**/*.spec.js', supportFile: 'test/cypress/support/unit.js', }, setupNodeEvents(on, config) { + require('cypress-mochawesome-reporter/plugin')(on); // implement node event listeners here }, }, diff --git a/package.json b/package.json index 71e95667c..e2e75f253 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@vue/test-utils": "^2.4.4", "autoprefixer": "^10.4.14", "cypress": "^13.6.6", + "cypress-mochawesome-reporter": "^3.8.2", "eslint": "^8.41.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-cypress": "^2.13.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e336c39bb..83dfa0469 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,6 +70,9 @@ devDependencies: cypress: specifier: ^13.6.6 version: 13.6.6 + cypress-mochawesome-reporter: + specifier: ^3.8.2 + version: 3.8.2(cypress@13.6.6)(mocha@10.7.3) eslint: specifier: ^8.41.0 version: 8.56.0 @@ -829,8 +832,8 @@ packages: vue-i18n: optional: true dependencies: - '@intlify/message-compiler': 10.0.0-beta.5 - '@intlify/shared': 10.0.0-beta.5 + '@intlify/message-compiler': 10.0.0 + '@intlify/shared': 10.0.0 jsonc-eslint-parser: 1.4.1 source-map: 0.6.1 vue-i18n: 9.9.1(vue@3.4.19) @@ -844,11 +847,11 @@ packages: '@intlify/message-compiler': 9.9.1 '@intlify/shared': 9.9.1 - /@intlify/message-compiler@10.0.0-beta.5: - resolution: {integrity: sha512-hLLchnM1dmtSEruerkzvU9vePsLqBXz3RU85SCx/Vd12fFQiymP+/5Rn9MJ8MyfLmIOLDEx4PRh+/GkIQP6oog==} + /@intlify/message-compiler@10.0.0: + resolution: {integrity: sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==} engines: {node: '>= 16'} dependencies: - '@intlify/shared': 10.0.0-beta.5 + '@intlify/shared': 10.0.0 source-map-js: 1.0.2 dev: true @@ -859,8 +862,8 @@ packages: '@intlify/shared': 9.9.1 source-map-js: 1.0.2 - /@intlify/shared@10.0.0-beta.5: - resolution: {integrity: sha512-g9bq5Y1bOcC9qxtNk4UWtF3sXm6Wh0fGISb7vD5aLyF7yQv7ZFjxQjJzBP2GqG/9+PAGYutqjP1GGadNqFtyAQ==} + /@intlify/shared@10.0.0: + resolution: {integrity: sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==} engines: {node: '>= 16'} dev: true @@ -884,7 +887,7 @@ packages: optional: true dependencies: '@intlify/bundle-utils': 4.0.0(vue-i18n@9.9.1) - '@intlify/shared': 10.0.0-beta.5 + '@intlify/shared': 10.0.0 '@rollup/pluginutils': 4.2.1 '@vue/compiler-sfc': 3.4.19 debug: 4.3.4(supports-color@8.1.1) @@ -1999,6 +2002,10 @@ packages: dependencies: fill-range: 7.0.1 + /browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dev: true + /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2106,6 +2113,16 @@ packages: upper-case: 1.1.3 dev: true + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + /camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} @@ -2255,6 +2272,22 @@ packages: engines: {node: '>= 10'} dev: true + /cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2558,6 +2591,23 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /cypress-mochawesome-reporter@3.8.2(cypress@13.6.6)(mocha@10.7.3): + resolution: {integrity: sha512-oJZkNzhNmN9ZD+LmZyFuPb8aWaIijyHyqYh52YOBvR6B6ckfJNCHP3A98a+/nG0H4t46CKTNwo+wNpMa4d2kjA==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + cypress: '>=6.2.0' + dependencies: + commander: 10.0.1 + cypress: 13.6.6 + fs-extra: 10.1.0 + mochawesome: 7.1.3(mocha@10.7.3) + mochawesome-merge: 4.3.0 + mochawesome-report-generator: 6.2.0 + transitivePeerDependencies: + - mocha + dev: true + /cypress@13.6.6: resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -2627,6 +2677,10 @@ packages: time-zone: 1.0.0 dev: true + /dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dev: true + /dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} dev: true @@ -2676,6 +2730,29 @@ packages: ms: 2.1.2 supports-color: 8.1.1 + /debug@4.3.7(supports-color@8.1.1): + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + supports-color: 8.1.1 + dev: true + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + dev: true + /decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -2758,6 +2835,11 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + /diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + dev: true + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -3550,6 +3632,14 @@ packages: transitivePeerDependencies: - supports-color + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3646,6 +3736,15 @@ packages: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: true + /fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + /fs-extra@11.2.0: resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} @@ -3654,6 +3753,15 @@ packages: jsonfile: 6.1.0 universalify: 2.0.1 + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + /fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -3675,6 +3783,10 @@ packages: dev: true optional: true + /fsu@1.1.1: + resolution: {integrity: sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==} + dev: true + /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3775,6 +3887,18 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + /global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} @@ -4189,6 +4313,11 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + /is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: true + /is-plain-obj@3.0.0: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} @@ -4361,6 +4490,12 @@ packages: resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: true + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -4452,6 +4587,13 @@ packages: engines: {node: '>=14'} dev: true + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -4486,10 +4628,26 @@ packages: resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} dev: true + /lodash.isempty@4.4.0: + resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} + dev: true + + /lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + dev: true + + /lodash.isobject@3.0.2: + resolution: {integrity: sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==} + dev: true + /lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} dev: true + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: true + /lodash.kebabcase@4.1.1: resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} dev: true @@ -4552,6 +4710,13 @@ packages: wrap-ansi: 6.2.0 dev: true + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + /loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: @@ -4722,6 +4887,79 @@ packages: ufo: 1.4.0 dev: true + /mocha@10.7.3: + resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==} + engines: {node: '>= 14.0.0'} + hasBin: true + dependencies: + ansi-colors: 4.1.3 + browser-stdout: 1.3.1 + chokidar: 3.6.0 + debug: 4.3.7(supports-color@8.1.1) + diff: 5.2.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 8.1.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.1.6 + ms: 2.1.3 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.5.1 + yargs: 16.2.0 + yargs-parser: 20.2.9 + yargs-unparser: 2.0.0 + dev: true + + /mochawesome-merge@4.3.0: + resolution: {integrity: sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==} + engines: {node: '>=10.0.0'} + hasBin: true + dependencies: + fs-extra: 7.0.1 + glob: 7.2.3 + yargs: 15.4.1 + dev: true + + /mochawesome-report-generator@6.2.0: + resolution: {integrity: sha512-Ghw8JhQFizF0Vjbtp9B0i//+BOkV5OWcQCPpbO0NGOoxV33o+gKDYU0Pr2pGxkIHnqZ+g5mYiXF7GMNgAcDpSg==} + hasBin: true + dependencies: + chalk: 4.1.2 + dateformat: 4.6.3 + escape-html: 1.0.3 + fs-extra: 10.1.0 + fsu: 1.1.1 + lodash.isfunction: 3.0.9 + opener: 1.5.2 + prop-types: 15.8.1 + tcomb: 3.2.29 + tcomb-validation: 3.4.1 + validator: 13.11.0 + yargs: 17.7.2 + dev: true + + /mochawesome@7.1.3(mocha@10.7.3): + resolution: {integrity: sha512-Vkb3jR5GZ1cXohMQQ73H3cZz7RoxGjjUo0G5hu0jLaW+0FdUxUwg3Cj29bqQdh0rFcnyV06pWmqmi5eBPnEuNQ==} + peerDependencies: + mocha: '>=7' + dependencies: + chalk: 4.1.2 + diff: 5.2.0 + json-stringify-safe: 5.0.1 + lodash.isempty: 4.4.0 + lodash.isfunction: 3.0.9 + lodash.isobject: 3.0.2 + lodash.isstring: 4.0.1 + mocha: 10.7.3 + mochawesome-report-generator: 6.2.0 + strip-ansi: 6.0.1 + uuid: 8.3.2 + dev: true + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -4870,6 +5108,11 @@ packages: is-wsl: 2.2.0 dev: false + /opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + dev: true + /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -4915,6 +5158,13 @@ packages: engines: {node: '>=12.20'} dev: false + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4929,6 +5179,13 @@ packages: yocto-queue: 1.0.0 dev: true + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -4950,6 +5207,11 @@ packages: aggregate-error: 3.1.0 dev: true + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + /package-json@8.1.1: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} @@ -5139,6 +5401,14 @@ packages: engines: {node: '>=0.4.0'} dev: false + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -5242,6 +5512,10 @@ packages: strip-json-comments: 2.0.1 dev: false + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true @@ -5328,6 +5602,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + /requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -5573,6 +5851,10 @@ packages: transitivePeerDependencies: - supports-color + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true + /set-function-length@1.2.1: resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} engines: {node: '>= 0.4'} @@ -5829,6 +6111,16 @@ packages: readable-stream: 3.6.2 dev: true + /tcomb-validation@3.4.1: + resolution: {integrity: sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==} + dependencies: + tcomb: 3.2.29 + dev: true + + /tcomb@3.2.29: + resolution: {integrity: sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==} + dev: true + /text-extensions@2.4.0: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} engines: {node: '>=8'} @@ -6048,6 +6340,11 @@ packages: crypto-random-string: 4.0.0 dev: false + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -6137,7 +6434,6 @@ packages: /validator@13.11.0: resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} engines: {node: '>= 0.10'} - dev: false /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} @@ -6484,6 +6780,10 @@ packages: engines: {node: '>=12'} dev: true + /which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + dev: true + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -6511,6 +6811,10 @@ packages: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} dev: true + /workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + dev: true + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -6559,6 +6863,10 @@ packages: engines: {node: '>=12'} dev: true + /y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6584,11 +6892,64 @@ packages: engines: {node: '>= 6'} dev: true + /yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} dev: true + /yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + dev: true + + /yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: true + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: true + /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} From 4de23c31b3f34f1849c4300e93729b244db9e01d Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 29 Oct 2024 15:01:49 +0100 Subject: [PATCH 136/150] feat: use composable to unify logic --- src/components/common/VnInput.vue | 13 +++++-------- src/components/common/VnInputDate.vue | 7 +++---- src/components/common/VnInputTime.vue | 6 ++---- src/components/common/VnLocation.vue | 12 ++++-------- src/components/common/VnSelect.vue | 6 ++---- src/composables/useRequired.js | 13 +++++++++++++ 6 files changed, 29 insertions(+), 28 deletions(-) create mode 100644 src/composables/useRequired.js diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 4534ae4a0..769e7ca44 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -1,9 +1,11 @@ <script setup> -import { computed, ref } from 'vue'; +import { computed, ref, useAttrs } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useValidator } from 'src/composables/useValidator'; -import { useAttrs } from 'vue'; +import { useRequired } from 'src/composables/useRequired'; +const $attrs = useAttrs(); +const { isRequired, requiredFieldRule } = useRequired($attrs); +const { t } = useI18n(); const emit = defineEmits([ 'update:modelValue', 'update:options', @@ -29,12 +31,7 @@ const $props = defineProps({ default: true, }, }); -const { validations } = useValidator(); -const $attrs = useAttrs(); -const { t } = useI18n(); -const isRequired = computed(() => Object.keys($attrs).includes('required')); -const requiredFieldRule = (val) => validations().required(isRequired.value, val); const vnInputRef = ref(null); const value = computed({ get() { diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue index 87833f411..b822092a5 100644 --- a/src/components/common/VnInputDate.vue +++ b/src/components/common/VnInputDate.vue @@ -4,10 +4,13 @@ import { date } from 'quasar'; import { useI18n } from 'vue-i18n'; import { useAttrs } from 'vue'; import VnDate from './VnDate.vue'; +import { useRequired } from 'src/composables/useRequired'; +const { isRequired, requiredFieldRule } = useRequired($attrs); const model = defineModel({ type: [String, Date] }); const $attrs = useAttrs(); const { t } = useI18n(); + const $props = defineProps({ isOutlined: { type: Boolean, @@ -18,11 +21,7 @@ const $props = defineProps({ default: true, }, }); -import { useValidator } from 'src/composables/useValidator'; -const { validations } = useValidator(); -const isRequired = computed(() => Object.keys($attrs).includes('required')); -const requiredFieldRule = (val) => validations().required(isRequired.value, val); const vnInputDateRef = ref(null); const dateFormat = 'DD/MM/YYYY'; diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue index 13e90e021..a0967fdf6 100644 --- a/src/components/common/VnInputTime.vue +++ b/src/components/common/VnInputTime.vue @@ -2,10 +2,10 @@ import { computed, ref, useAttrs } from 'vue'; import { useI18n } from 'vue-i18n'; import { date } from 'quasar'; -import { useValidator } from 'src/composables/useValidator'; import VnTime from './VnTime.vue'; +import { useRequired } from 'src/composables/useRequired'; -const { validations } = useValidator(); +const { isRequired, requiredFieldRule } = useRequired($attrs); const { t } = useI18n(); const $attrs = useAttrs(); const model = defineModel({ type: String }); @@ -21,8 +21,6 @@ const props = defineProps({ }); const vnInputTimeRef = ref(null); const initialDate = ref(model.value ?? Date.vnNew()); -const isRequired = computed(() => Object.keys($attrs).includes('required')); -const requiredFieldRule = (val) => validations().required(isRequired.value, val); const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const dateFormat = 'HH:mm'; const isPopupOpen = ref(); diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue index 7c6e46fdd..16f9fd580 100644 --- a/src/components/common/VnLocation.vue +++ b/src/components/common/VnLocation.vue @@ -2,13 +2,12 @@ import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue'; import VnSelectDialog from 'components/common/VnSelectDialog.vue'; import { useI18n } from 'vue-i18n'; -import { computed, ref } from 'vue'; +import { ref } from 'vue'; +import { useAttrs } from 'vue'; +import { useRequired } from 'src/composables/useRequired'; const { t } = useI18n(); const emit = defineEmits(['update:model-value', 'update:options']); -const { validations } = useValidator(); - -import { useAttrs } from 'vue'; -import { useValidator } from 'src/composables/useValidator'; +const { isRequired, requiredFieldRule } = useRequired($attrs); const $attrs = useAttrs(); const props = defineProps({ location: { @@ -17,9 +16,6 @@ const props = defineProps({ }, }); -const isRequired = computed(() => Object.keys($attrs).includes('required')); -const requiredFieldRule = (val) => validations().required(isRequired.value, val); - const mixinRules = [requiredFieldRule]; const locationProperties = [ 'postcode', diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index 4a4730784..662efcfe9 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -2,10 +2,11 @@ import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'src/components/FetchData.vue'; -import { useValidator } from 'src/composables/useValidator'; +import { useRequired } from 'src/composables/useRequired'; const emit = defineEmits(['update:modelValue', 'update:options', 'remove']); const $attrs = useAttrs(); const { t } = useI18n(); +const { isRequired, requiredFieldRule } = useRequired($attrs); const $props = defineProps({ modelValue: { type: [String, Number, Object], @@ -88,10 +89,7 @@ const $props = defineProps({ default: false, }, }); -const { validations } = useValidator(); -const isRequired = computed(() => Object.keys($attrs).includes('required')); -const requiredFieldRule = (val) => validations().required(isRequired.value, val); const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } = toRefs($props); diff --git a/src/composables/useRequired.js b/src/composables/useRequired.js new file mode 100644 index 000000000..e650c91f5 --- /dev/null +++ b/src/composables/useRequired.js @@ -0,0 +1,13 @@ +import { useValidator } from 'src/composables/useValidator'; + +export function useRequired($attrs) { + const { validations } = useValidator(); + + const isRequired = Object.keys($attrs).includes('required'); + const requiredFieldRule = (val) => validations().required(isRequired, val); + + return { + isRequired, + requiredFieldRule, + }; +} From 1761cc23e0166f3c94c4e7b8e72c8883301bb347 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Wed, 30 Oct 2024 08:29:04 +0100 Subject: [PATCH 137/150] feat: use composable to unify logic --- src/components/common/VnInputDate.vue | 2 +- src/components/common/VnInputTime.vue | 2 +- src/components/common/VnLocation.vue | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue index b822092a5..fcc04ddf7 100644 --- a/src/components/common/VnInputDate.vue +++ b/src/components/common/VnInputDate.vue @@ -6,9 +6,9 @@ import { useAttrs } from 'vue'; import VnDate from './VnDate.vue'; import { useRequired } from 'src/composables/useRequired'; +const $attrs = useAttrs(); const { isRequired, requiredFieldRule } = useRequired($attrs); const model = defineModel({ type: [String, Date] }); -const $attrs = useAttrs(); const { t } = useI18n(); const $props = defineProps({ diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue index a0967fdf6..6724c00b5 100644 --- a/src/components/common/VnInputTime.vue +++ b/src/components/common/VnInputTime.vue @@ -5,9 +5,9 @@ import { date } from 'quasar'; import VnTime from './VnTime.vue'; import { useRequired } from 'src/composables/useRequired'; +const $attrs = useAttrs(); const { isRequired, requiredFieldRule } = useRequired($attrs); const { t } = useI18n(); -const $attrs = useAttrs(); const model = defineModel({ type: String }); const props = defineProps({ timeOnly: { diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue index 16f9fd580..6de402a26 100644 --- a/src/components/common/VnLocation.vue +++ b/src/components/common/VnLocation.vue @@ -7,8 +7,8 @@ import { useAttrs } from 'vue'; import { useRequired } from 'src/composables/useRequired'; const { t } = useI18n(); const emit = defineEmits(['update:model-value', 'update:options']); -const { isRequired, requiredFieldRule } = useRequired($attrs); const $attrs = useAttrs(); +const { isRequired, requiredFieldRule } = useRequired($attrs); const props = defineProps({ location: { type: Object, From 08204aa5f085d9045644a7048d537ab70b9737f0 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 4 Nov 2024 13:02:39 +0100 Subject: [PATCH 138/150] refactor(InvoiceInBasicData): use VnDms --- src/components/FormModel.vue | 1 + src/components/common/VnDms.vue | 9 +- .../InvoiceIn/Card/InvoiceInBasicData.vue | 440 ++++-------------- src/pages/InvoiceIn/Card/InvoiceInCard.vue | 17 + 4 files changed, 114 insertions(+), 353 deletions(-) diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue index 9ac2d38a5..c668769e5 100644 --- a/src/components/FormModel.vue +++ b/src/components/FormModel.vue @@ -272,6 +272,7 @@ defineExpose({ hasChanges, reset, fetch, + formData, }); </script> <template> diff --git a/src/components/common/VnDms.vue b/src/components/common/VnDms.vue index 920b7f137..946cd8264 100644 --- a/src/components/common/VnDms.vue +++ b/src/components/common/VnDms.vue @@ -31,6 +31,10 @@ const $props = defineProps({ type: String, default: null, }, + description: { + type: String, + default: null, + }, }); const warehouses = ref(); @@ -43,7 +47,8 @@ const dms = ref({}); onMounted(() => { defaultData(); if (!$props.formInitialData) - dms.value.description = t($props.model + 'Description', dms.value); + dms.value.description = + $props.description ?? t($props.model + 'Description', dms.value); }); function onFileChange(files) { dms.value.hasFileAttached = !!files; @@ -54,7 +59,6 @@ function mapperDms(data) { const formData = new FormData(); const { files } = data; if (files) formData.append(files?.name, files); - delete data.files; const dms = { hasFile: !!data.hasFile, @@ -78,6 +82,7 @@ async function save() { const body = mapperDms(dms.value); const response = await axios.post(getUrl(), body[0], body[1]); emit('onDataSaved', body[1].params, response); + delete dms.value.files; return response; } diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index c9468557f..f9498f52e 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -3,8 +3,6 @@ import { ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import { useQuasar } from 'quasar'; -import axios from 'axios'; -import { useArrayData } from 'src/composables/useArrayData'; import { downloadFile } from 'src/composables/downloadFile'; import FormModel from 'components/FormModel.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; @@ -12,15 +10,15 @@ import FetchData from 'src/components/FetchData.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInput from 'src/components/common/VnInput.vue'; +import VnDms from 'src/components/common/VnDms.vue'; +import VnConfirm from 'src/components/ui/VnConfirm.vue'; +import axios from 'axios'; -const quasar = useQuasar(); const { t } = useI18n(); -const dms = ref({}); const route = useRoute(); +const quasar = useQuasar(); const editDownloadDisabled = ref(false); -const arrayData = useArrayData(); -const invoiceIn = computed(() => arrayData.store.data); const userConfig = ref(null); const invoiceId = computed(() => +route.params.id); @@ -36,98 +34,25 @@ const warehousesRef = ref(); const allowTypesRef = ref(); const allowedContentTypes = ref([]); const sageWithholdings = ref([]); -const inputFileRef = ref(); -const editDmsRef = ref(); -const createDmsRef = ref(); +const documentDialogRef = ref({}); +const invoiceInRef = ref({}); -async function checkFileExists(dmsId) { - if (!dmsId) return; - try { - await axios.get(`Dms/${dmsId}`, { fields: ['id'] }); - editDownloadDisabled.value = false; - } catch (e) { - editDownloadDisabled.value = true; - } -} - -async function setEditDms(dmsId) { - const { data } = await axios.get(`Dms/${dmsId}`); - dms.value = { - warehouseId: data.warehouseFk, - companyId: data.companyFk, - dmsTypeId: data.dmsTypeFk, - ...data, - }; - - if (!allowedContentTypes.value.length) await allowTypesRef.value.fetch(); - - editDmsRef.value.show(); -} - -async function setCreateDms() { - const { data } = await axios.get('DmsTypes/findOne', { - where: { code: 'invoiceIn' }, - }); - dms.value = { - reference: invoiceIn.value.supplierRef, - warehouseId: userConfig.value.warehouseFk, - companyId: userConfig.value.companyFk, - dmsTypeId: data.id, - description: invoiceIn.value.supplier.name, - hasFile: true, - hasFileAttached: true, - files: null, - }; - - createDmsRef.value.show(); -} - -async function onSubmit() { - try { - const isEdit = !!dms.value.id; - const errors = { - companyId: `The company can't be empty`, - warehouseId: `The warehouse can't be empty`, - dmsTypeId: `The DMS Type can't be empty`, - description: `The description can't be empty`, - }; - - Object.keys(errors).forEach((key) => { - if (!dms.value[key]) throw Error(t(errors[key])); +function deleteFile(dmsFk) { + quasar + .dialog({ + component: VnConfirm, + componentProps: { + title: t('globals.confirmDeletion'), + message: t('globals.confirmDeletionMessage'), + }, + }) + .onOk(async () => { + await axios.post(`dms/${dmsFk}/removeFile`); + invoiceInRef.value.formData.dmsFk = null; + invoiceInRef.value.formData.dms = undefined; + invoiceInRef.value.hasChanges = true; + invoiceInRef.value.save(); }); - - if (!isEdit && !dms.value.files) throw Error(t(`The files can't be empty`)); - - const formData = new FormData(); - - if (dms.value.files) { - for (let i = 0; i < dms.value.files.length; i++) - formData.append(dms.value.files[i].name, dms.value.files[i]); - dms.value.hasFileAttached = true; - } - const url = isEdit ? `dms/${dms.value.id}/updateFile` : 'Dms/uploadFile'; - const { data } = await axios.post(url, formData, { - params: dms.value, - }); - - if (data.length) invoiceIn.value.dmsFk = data[0].id; - - if (!isEdit) { - createDmsRef.value.hide(); - } else { - editDmsRef.value.hide(); - } - - quasar.notify({ - message: t('globals.dataSaved'), - type: 'positive', - }); - } catch (error) { - quasar.notify({ - message: t(`${error.message}`), - type: 'negative', - }); - } } </script> <template> @@ -181,10 +106,12 @@ async function onSubmit() { @on-fetch="(data) => (sageWithholdings = data)" /> <FormModel + ref="invoiceInRef" model="InvoiceIn" :go-to="`/invoice-in/${invoiceId}/vat`" - auto-load :url-update="`InvoiceIns/${invoiceId}/updateInvoiceIn`" + @on-fetch="(data) => (documentDialogRef.supplierName = data.supplier.nickname)" + auto-load > <template #form="{ data }"> <VnRow> @@ -242,16 +169,18 @@ async function onSubmit() { </QItem> </template> </VnSelect> - <VnInput - :label="t('Document')" - v-model="data.dmsFk" - clearable - clear-icon="close" - @update:model-value="checkFileExists(data.dmsFk)" - > - <template #prepend> + + <div class="row no-wrap"> + <VnInput + :label="t('Document')" + v-model="data.dmsFk" + clearable + clear-icon="close" + class="full-width" + :disable="true" + /> + <div v-if="data.dmsFk" class="row no-wrap q-pa-xs q-gutter-x-xs"> <QBtn - v-if="data.dmsFk" :class="{ 'no-pointer-events': editDownloadDisabled, }" @@ -262,33 +191,51 @@ async function onSubmit() { round @click="downloadFile(data.dmsFk)" /> - </template> - <template #append> <QBtn :class="{ 'no-pointer-events': editDownloadDisabled, }" :disable="editDownloadDisabled" - v-if="data.dmsFk" icon="edit" round padding="xs" - @click="setEditDms(data.dmsFk)" + @click=" + () => { + documentDialogRef.show = true; + documentDialogRef.dms = data.dms; + } + " > <QTooltip>{{ t('Edit document') }}</QTooltip> </QBtn> <QBtn - v-else - icon="add_circle" - round - shortcut="+" + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + :disable="editDownloadDisabled" + icon="delete" + :title="t('Delete file')" padding="xs" - @click="setCreateDms()" - > - <QTooltip>{{ t('Create document') }}</QTooltip> - </QBtn> - </template> - </VnInput> + round + @click="deleteFile(data.dmsFk)" + /> + </div> + <QBtn + v-else + icon="add_circle" + round + shortcut="+" + padding="xs" + @click=" + () => { + documentDialogRef.show = true; + delete documentDialogRef.dms; + } + " + > + <QTooltip>{{ t('Create document') }}</QTooltip> + </QBtn> + </div> </VnRow> <VnRow> <VnSelect @@ -319,237 +266,28 @@ async function onSubmit() { </VnRow> </template> </FormModel> - <QDialog ref="editDmsRef"> - <QForm @submit="onSubmit()" class="all-pointer-events"> - <QCard class="q-pa-sm"> - <QCardSection class="row items-center q-pb-none"> - <span class="text-primary text-h6"> - <QIcon name="edit" class="q-mr-xs" /> - {{ t('Edit document') }} - </span> - <QSpace /> - <QBtn icon="close" flat round dense v-close-popup /> - </QCardSection> - <QCardSection class="q-py-none"> - <QItem> - <VnInput - class="full-width q-pa-xs" - :label="t('Reference')" - v-model="dms.reference" - clearable - clear-icon="close" - /> - <VnSelect - class="full-width q-pa-xs" - :label="t('Company')" - v-model="dms.companyId" - :options="companies" - option-value="id" - option-label="code" - :required="true" - /> - </QItem> - <QItem> - <VnSelect - class="full-width q-pa-xs" - :label="t('Warehouse')" - v-model="dms.warehouseId" - :options="warehouses" - option-value="id" - option-label="name" - :required="true" - /> - <VnSelect - class="full-width q-pa-xs" - :label="t('Type')" - v-model="dms.dmsTypeId" - :options="dmsTypes" - option-value="id" - option-label="name" - :required="true" - /> - </QItem> - <QItem> - <VnInput - :label="t('Description')" - v-model="dms.description" - :required="true" - type="textarea" - class="full-width q-pa-xs" - size="lg" - autogrow - clearable - clear-icon="close" - /> - </QItem> - <QItem> - <QFile - ref="inputFileRef" - class="full-width q-pa-xs" - :label="t('File')" - v-model="dms.files" - multiple - :accept="allowedContentTypes.join(',')" - clearable - clear-icon="close" - > - <template #append> - <QBtn - icon="attach_file_add" - flat - round - padding="xs" - @click="inputFileRef.pickFiles()" - > - <QTooltip> - {{ t('globals.selectFile') }} - </QTooltip> - </QBtn> - <QBtn icon="info" flat round padding="xs"> - <QTooltip max-width="30rem"> - {{ - `${t( - 'Allowed content types' - )}: ${allowedContentTypes.join(', ')}` - }} - </QTooltip> - </QBtn> - </template> - </QFile> - </QItem> - <QItem> - <QCheckbox - :label="t('Generate identifier for original file')" - v-model="dms.hasFile" - /> - </QItem> - </QCardSection> - <QCardActions class="justify-end"> - <QBtn - flat - :label="t('globals.close')" - color="primary" - v-close-popup - /> - <QBtn :label="t('globals.save')" color="primary" @click="onSubmit" /> - </QCardActions> - </QCard> - </QForm> - </QDialog> - <QDialog ref="createDmsRef"> - <QForm @submit="onSubmit()" class="all-pointer-events"> - <QCard class="q-pa-sm"> - <QCardSection class="row items-center q-pb-none"> - <span class="text-primary text-h6"> - <QIcon name="edit" class="q-mr-xs" /> - {{ t('Create document') }} - </span> - <QSpace /> - <QBtn icon="close" flat round dense v-close-popup /> - </QCardSection> - <QCardSection class="q-pb-none"> - <QItem> - <VnInput - class="full-width q-pa-xs" - :label="t('Reference')" - v-model="dms.reference" - /> - <VnSelect - class="full-width q-pa-xs" - :label="`${t('Company')}*`" - v-model="dms.companyId" - :options="companies" - option-value="id" - option-label="code" - :required="true" - /> - </QItem> - <QItem> - <VnSelect - class="full-width q-pa-xs" - :label="`${t('Warehouse')}*`" - v-model="dms.warehouseId" - :options="warehouses" - option-value="id" - option-label="name" - :required="true" - /> - <VnSelect - class="full-width q-pa-xs" - :label="`${t('Type')}*`" - v-model="dms.dmsTypeId" - :options="dmsTypes" - option-value="id" - option-label="name" - :required="true" - /> - </QItem> - <QItem> - <VnInput - class="full-width q-pa-xs" - type="textarea" - size="lg" - autogrow - :label="`${t('Description')}*`" - v-model="dms.description" - clearable - clear-icon="close" - :rules="[(val) => val.length || t('Required field')]" - /> - </QItem> - <QItem> - <QFile - ref="inputFileRef" - class="full-width q-pa-xs" - :label="t('File')" - v-model="dms.files" - multiple - :accept="allowedContentTypes.join(',')" - clearable - clear-icon="close" - > - <template #append> - <QBtn - icon="attach_file_add" - flat - round - padding="xs" - @click="inputFileRef.pickFiles()" - > - <QTooltip> - {{ t('globals.selectFile') }} - </QTooltip> - </QBtn> - <QBtn icon="info" flat round padding="xs"> - <QTooltip max-width="30rem"> - {{ - `${t( - 'Allowed content types' - )}: ${allowedContentTypes.join(', ')}` - }} - </QTooltip> - </QBtn> - </template> - </QFile> - </QItem> - <QItem> - <QCheckbox - :label="t('Generate identifier for original file')" - v-model="dms.hasFile" - /> - </QItem> - </QCardSection> - <QCardActions align="right"> - <QBtn - flat - :label="t('globals.close')" - color="primary" - v-close-popup - /> - <QBtn :label="t('globals.save')" color="primary" @click="onSubmit" /> - </QCardActions> - </QCard> - </QForm> + <QDialog v-model="documentDialogRef.show"> + <VnDms + model="dms" + default-dms-code="invoiceIn" + :form-initial-data="documentDialogRef.dms" + :url=" + documentDialogRef.dms + ? `Dms/${documentDialogRef.dms.id}/updateFile` + : 'Dms/uploadFile' + " + :description="documentDialogRef.supplierName" + @on-data-saved=" + (_, { data }) => { + let dmsData = data; + if (Array.isArray(data)) dmsData = data[0]; + invoiceInRef.formData.dmsFk = dmsData.id; + invoiceInRef.formData.dms = dmsData; + invoiceInRef.hasChanges = true; + invoiceInRef.save(); + } + " + /> </QDialog> </template> <style lang="scss" scoped> diff --git a/src/pages/InvoiceIn/Card/InvoiceInCard.vue b/src/pages/InvoiceIn/Card/InvoiceInCard.vue index 0fe2a2368..b16183e52 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInCard.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInCard.vue @@ -20,6 +20,23 @@ const filter = { { relation: 'invoiceInDueDay' }, { relation: 'company' }, { relation: 'currency' }, + { + relation: 'dms', + scope: { + fields: [ + 'dmsTypeFk', + 'reference', + 'hardCopyNumber', + 'workerFk', + 'description', + 'hasFile', + 'file', + 'created', + 'companyFk', + 'warehouseFk', + ], + }, + }, ], }; From e050a077d4fdb77e62598c9466eb282fcb67e189 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 4 Nov 2024 13:41:37 +0100 Subject: [PATCH 139/150] test: refactor e2e --- src/components/common/VnDms.vue | 1 + src/components/ui/VnConfirm.vue | 1 + .../InvoiceIn/Card/InvoiceInBasicData.vue | 7 +++- .../invoiceIn/invoiceInBasicData.spec.js | 33 +++++++++++-------- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/common/VnDms.vue b/src/components/common/VnDms.vue index 946cd8264..3f9e65740 100644 --- a/src/components/common/VnDms.vue +++ b/src/components/common/VnDms.vue @@ -170,6 +170,7 @@ function addDefaultData(data) { @update:model-value="onFileChange(dms.files)" class="required" :display-value="dms.file" + data-cy="VnDms_inputFile" > <template #append> <QIcon diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index d6b1ac0a3..0b1913383 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -103,6 +103,7 @@ function cancel() { @click="confirm()" unelevated autofocus + data-cy="VnConfirm_confirm" /> </QCardActions> </QCard> diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index f9498f52e..209681b7c 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -179,7 +179,11 @@ function deleteFile(dmsFk) { class="full-width" :disable="true" /> - <div v-if="data.dmsFk" class="row no-wrap q-pa-xs q-gutter-x-xs"> + <div + v-if="data.dmsFk" + class="row no-wrap q-pa-xs q-gutter-x-xs" + data-cy="dms-buttons" + > <QBtn :class="{ 'no-pointer-events': editDownloadDisabled, @@ -232,6 +236,7 @@ function deleteFile(dmsFk) { delete documentDialogRef.dms; } " + data-cy="dms-create" > <QTooltip>{{ t('Create document') }}</QTooltip> </QBtn> diff --git a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js index e1939fe5a..2016fca6d 100644 --- a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js +++ b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js @@ -2,9 +2,8 @@ describe('InvoiceInBasicData', () => { const formInputs = '.q-form > .q-card input'; const firstFormSelect = '.q-card > .vn-row:nth-child(1) > .q-select'; - const documentBtns = '.q-form .q-field button'; + const documentBtns = '[data-cy="dms-buttons"] button'; const dialogInputs = '.q-dialog input'; - const dialogActionBtns = '.q-card__actions button'; beforeEach(() => { cy.login('developer'); @@ -21,27 +20,35 @@ describe('InvoiceInBasicData', () => { cy.get(formInputs).eq(1).invoke('val').should('eq', '4739'); }); - it('should edit the dms data', () => { + it('should edit, remove and create the dms data', () => { const firtsInput = 'Ticket:65'; const secondInput = "I don't know what posting here!"; + //edit cy.get(documentBtns).eq(1).click(); cy.get(dialogInputs).eq(0).type(`{selectall}${firtsInput}`); cy.get('textarea').type(`{selectall}${secondInput}`); - cy.get(dialogActionBtns).eq(1).click(); - + cy.get('[data-cy="FormModelPopup_save"]').click(); cy.get(documentBtns).eq(1).click(); cy.get(dialogInputs).eq(0).invoke('val').should('eq', firtsInput); cy.get('textarea').invoke('val').should('eq', secondInput); - }); + cy.get('[data-cy="FormModelPopup_save"]').click(); + cy.checkNotification('Data saved'); - it('should throw an error creating a new dms if a file is not attached', () => { - cy.get(formInputs).eq(7).type('{selectall}{backspace}'); - cy.get(documentBtns).eq(0).click(); - cy.get(dialogActionBtns).eq(1).click(); - cy.get('.q-notification__message').should( - 'have.text', - "The files can't be empty" + //remove + cy.get(documentBtns).eq(2).click(); + cy.get('[data-cy="VnConfirm_confirm"]').click(); + cy.checkNotification('Data saved'); + + //create + cy.get('[data-cy="dms-create"]').eq(0).click(); + cy.get('[data-cy="VnDms_inputFile"').selectFile( + 'test/cypress/fixtures/image.jpg', + { + force: true, + } ); + cy.get('[data-cy="FormModelPopup_save"]').click(); + cy.checkNotification('Data saved'); }); }); From 1f9d850fbeff23c8d44367704f9945c15920a709 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 5 Nov 2024 10:02:29 +0100 Subject: [PATCH 140/150] chore: refs #8078 rollback ref --- src/pages/Ticket/Card/TicketDescriptorMenu.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 8efd0f1e1..bf4a1efb4 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -659,7 +659,7 @@ async function uploadDocuware(force) { </QList> </QMenu> </QItem> - <QItem @click="$refs.weightDialog.dialogRef.show()" v-ripple clickable> + <QItem @click="$refs.weightDialog.show()" v-ripple clickable> <QItemSection avatar> <QIcon name="weight" /> </QItemSection> From 86e6bef90f18d98c391a2046c9223a140e289f59 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 5 Nov 2024 10:37:35 +0100 Subject: [PATCH 141/150] chore: refs #8078 fiz tests --- test/vitest/__tests__/components/VnTable.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/vitest/__tests__/components/VnTable.spec.js b/test/vitest/__tests__/components/VnTable.spec.js index d11c2b6c3..162df727d 100644 --- a/test/vitest/__tests__/components/VnTable.spec.js +++ b/test/vitest/__tests__/components/VnTable.spec.js @@ -20,12 +20,12 @@ describe('VnTable', () => { describe('handleSelection()', () => { const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }]; const selectedRows = [{ $index: 1 }]; - it('should add rows to selected when shift key is pressed and rows are added', () => { + it('should add rows to selected when shift key is pressed and rows are added except last one', () => { vm.handleSelection( { evt: { shiftKey: true }, added: true, rows: selectedRows }, rows ); - expect(vm.selected).toEqual([{ $index: 0 }, { $index: 1 }]); + expect(vm.selected).toEqual([{ $index: 0 }]); }); it('should not add rows to selected when shift key is not pressed', () => { From 4a95a0e39ae71f8ae8210bfe1d4cabc6021d9e0e Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 5 Nov 2024 11:59:38 +0100 Subject: [PATCH 142/150] chore: refs #7273 sticky add btn & refactor --- src/pages/Item/Card/ItemTags.vue | 37 +++++++++++++++++--------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/pages/Item/Card/ItemTags.vue b/src/pages/Item/Card/ItemTags.vue index 6f31d0cf8..e59085402 100644 --- a/src/pages/Item/Card/ItemTags.vue +++ b/src/pages/Item/Card/ItemTags.vue @@ -109,7 +109,11 @@ const insertTag = (rows) => { > <template #body="{ rows, validate }"> <QCard class="q-px-lg q-pt-md q-pb-sm"> - <VnRow v-for="(row, index) in rows" :key="index"> + <VnRow + v-for="(row, index) in rows" + :key="index" + class="items-center" + > <VnSelect :label="t('itemTags.tag')" :options="tagOptions" @@ -153,13 +157,14 @@ const insertTag = (rows) => { :required="true" :rules="validate('itemTag.priority')" /> - <div class="row justify-center items-center" style="flex: 0"> + <div class="row justify-center" style="flex: 0"> <QIcon @click="itemTagsRef.remove([row])" class="fill-icon-on-hover" color="primary" name="delete" size="sm" + dense > <QTooltip> {{ t('itemTags.removeTag') }} @@ -167,22 +172,20 @@ const insertTag = (rows) => { </QIcon> </div> </VnRow> - <VnRow class="justify-center items-center"> - <QBtn - @click="insertTag(rows)" - class="cursor-pointer" - color="primary" - flat - icon="add" - shortcut="+" - style="flex: 0" - > - <QTooltip> - {{ t('itemTags.addTag') }} - </QTooltip> - </QBtn> - </VnRow> </QCard> + <QPageSticky position="bottom-right" :offset="[25, 25]"> + <QBtn + @click="insertTag(rows)" + color="primary" + icon="add" + shortcut="+" + fab + > + <QTooltip> + {{ t('itemTags.addTag') }} + </QTooltip> + </QBtn> + </QPageSticky> </template> </CrudModel> </QPage> From 9959fbf005f9576fda495ac7b8bcd04542c617ef Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 5 Nov 2024 12:23:11 +0100 Subject: [PATCH 143/150] fix: refs #7273 use same filter --- src/pages/Item/ItemList.vue | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index a480cfff6..f789d2cd0 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -12,6 +12,8 @@ import ItemSummary from '../Item/Card/ItemSummary.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue'; import { cloneItem } from 'src/pages/Item/composables/cloneItem'; +import RightMenu from 'src/components/common/RightMenu.vue'; +import ItemListFilter from './ItemListFilter.vue'; const entityId = computed(() => route.params.id); const { openCloneDialog } = cloneItem(); @@ -311,6 +313,11 @@ const columns = computed(() => [ :label="t('item.searchbar.label')" :info="t('You can search by id')" /> + <RightMenu> + <template #right-panel> + <ItemListFilter data-key="ItemList" /> + </template> + </RightMenu> <VnTable ref="tableRef" data-key="ItemList" @@ -329,6 +336,7 @@ const columns = computed(() => [ auto-load redirect="Item" :is-editable="false" + :right-search="false" :filer="itemFilter" > <template #column-id="{ row }"> From abd7b76636c53f69c5d4ff6e0cec87f037170fa8 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 5 Nov 2024 13:50:25 +0100 Subject: [PATCH 144/150] feat(VnInput): empty to null --- src/components/common/VnInput.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 1246eedcd..93cf332a3 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -27,6 +27,10 @@ const $props = defineProps({ type: Boolean, default: true, }, + emptyToNull: { + type: Boolean, + default: true, + }, }); const { validations } = useValidator(); @@ -39,6 +43,7 @@ const value = computed({ return $props.modelValue; }, set(value) { + if ($props.emptyToNull && value === '') value = null; emit('update:modelValue', value); }, }); From a2bbf4474d4a801ece1cb52d57f61f55ecfe2ebb Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Wed, 6 Nov 2024 13:52:59 +0100 Subject: [PATCH 145/150] fix(InvoiceOutGlobal): parallelism --- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 7 ++-- src/stores/invoiceOutGlobal.js | 37 +++++++++---------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue index 5bcb21001..e85f1f44c 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -13,7 +13,7 @@ const { t } = useI18n(); const invoiceOutGlobalStore = useInvoiceOutGlobalStore(); // invoiceOutGlobalStore state and getters -const { initialDataLoading, formInitialData, invoicing, status } = +const { initialDataLoading, formInitialData, status } = storeToRefs(invoiceOutGlobalStore); // invoiceOutGlobalStore actions @@ -151,9 +151,8 @@ onMounted(async () => { rounded /> </div> - <QBtn - v-if="!invoicing" + v-if="!getStatus || getStatus === 'stopping'" :label="t('invoiceOut')" type="submit" color="primary" @@ -163,7 +162,7 @@ onMounted(async () => { dense /> <QBtn - v-if="invoicing" + v-else :label="t('stop')" color="primary" class="q-mt-md full-width" diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js index 42acac013..b17f41564 100644 --- a/src/stores/invoiceOutGlobal.js +++ b/src/stores/invoiceOutGlobal.js @@ -93,7 +93,7 @@ export const useInvoiceOutGlobalStore = defineStore({ async makeInvoice(formData, clientsToInvoice) { this.invoicing = true; - this.status = 'packageInvoicing'; + const promises = []; try { this.printer = formData.printer; const params = { @@ -118,10 +118,11 @@ export const useInvoiceOutGlobalStore = defineStore({ ); throw new Error("There aren't addresses to invoice"); } - - for (const address of this.addresses) { - await this.invoiceClient(address, formData); + this.status = 'invoicing'; + for (let index = 0; index < this.parallelism; index++) { + promises.push(this.invoiceClient(formData, index)); } + await Promise.all(promises); } catch (err) { this.handleError(err); } @@ -171,17 +172,15 @@ export const useInvoiceOutGlobalStore = defineStore({ } }, - async invoiceClient(address, formData) { + async invoiceClient(formData, index) { + const address = this.addresses[index]; + if (!address || !this.status || this.status == 'stopping') { + this.status = 'stopping'; + this.invoicing = false; + return; + } + console.log('address: ', address); try { - if (this.nRequests === this.parallelism || this.isInvoicing) return; - - if (this.status === 'stopping') { - if (this.nRequests) return; - this.invoicing = false; - this.status = 'done'; - return; - } - const params = { clientId: address.clientId, addressId: address.id, @@ -191,13 +190,11 @@ export const useInvoiceOutGlobalStore = defineStore({ serialType: formData.serialType, }; - this.status = 'invoicing'; this.invoicing = true; const { data } = await axios.post('InvoiceOuts/invoiceClient', params); if (data) await this.makePdfAndNotify(data, address); - this.addressIndex++; this.isInvoicing = false; } catch (err) { if (err?.response?.status >= 400 && err?.response?.status < 500) { @@ -205,13 +202,16 @@ export const useInvoiceOutGlobalStore = defineStore({ return; } else { this.invoicing = false; - this.status = 'done'; notify( 'invoiceOut.globalInvoices.errors.criticalInvoiceError', 'negative' ); throw new Error('Critical invoicing error, process stopped'); } + } finally { + this.addressIndex++; + if (this.status != 'stopping') + await this.invoiceClient(formData, this.addressIndex); } }, @@ -234,7 +234,6 @@ export const useInvoiceOutGlobalStore = defineStore({ handleError(err) { this.invoicing = false; - this.status = null; throw err; }, @@ -279,7 +278,7 @@ export const useInvoiceOutGlobalStore = defineStore({ return 0; } let porcentaje = (state.addressIndex / this.getNAddresses) * 100; - return porcentaje; + return porcentaje?.toFixed(2); }, getAddressNumber(state) { return state.addressIndex; From ead241e7daa826608dfe522349f4691a06dbc2c9 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Wed, 6 Nov 2024 13:53:58 +0100 Subject: [PATCH 146/150] chore: remove console.log --- src/stores/invoiceOutGlobal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js index b17f41564..35f834f3d 100644 --- a/src/stores/invoiceOutGlobal.js +++ b/src/stores/invoiceOutGlobal.js @@ -179,7 +179,6 @@ export const useInvoiceOutGlobalStore = defineStore({ this.invoicing = false; return; } - console.log('address: ', address); try { const params = { clientId: address.clientId, From 6c9627c6c51c5dfeb6d61906e0193165a11e5fe3 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Wed, 6 Nov 2024 17:51:21 +0100 Subject: [PATCH 147/150] fix(worker): add searchurls --- src/pages/Worker/Card/WorkerBalance.vue | 19 ++++++++++++++++--- src/pages/Worker/Card/WorkerFormation.vue | 1 + src/pages/Worker/Card/WorkerMedical.vue | 1 + .../Card/WorkerNotificationsManager.vue | 1 + src/pages/Worker/Card/WorkerPda.vue | 1 + 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/pages/Worker/Card/WorkerBalance.vue b/src/pages/Worker/Card/WorkerBalance.vue index c5367a281..25ab92c9b 100644 --- a/src/pages/Worker/Card/WorkerBalance.vue +++ b/src/pages/Worker/Card/WorkerBalance.vue @@ -3,11 +3,12 @@ import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import VnTable from 'components/VnTable/VnTable.vue'; +import FetchData from 'src/components/FetchData.vue'; const tableRef = ref(); +const payrollComponents = ref([]); const { t } = useI18n(); const route = useRoute(); const entityId = computed(() => route.params.id); - const columns = computed(() => [ { align: 'left', @@ -25,8 +26,9 @@ const columns = computed(() => [ create: true, component: 'select', attrs: { - url: 'payrollComponents', - fields: ['id', 'name'], + options: payrollComponents, + optionLabel: 'name', + optionValue: 'id', }, cardVisible: true, }, @@ -73,6 +75,16 @@ const columns = computed(() => [ ]); </script> <template> + <FetchData + url="PayrollComponents" + :filter="{ + fields: ['id', 'name'], + where: { name: { neq: '' } }, + order: 'name ASC', + }" + @on-fetch="(data) => (payrollComponents = data)" + auto-load + /> <VnTable ref="tableRef" data-key="WorkerBalance" @@ -94,6 +106,7 @@ const columns = computed(() => [ :is-editable="true" :use-model="true" :default-remove="false" + search-url="balance" /> </template> <i18n> diff --git a/src/pages/Worker/Card/WorkerFormation.vue b/src/pages/Worker/Card/WorkerFormation.vue index c11dd019e..596691d2e 100644 --- a/src/pages/Worker/Card/WorkerFormation.vue +++ b/src/pages/Worker/Card/WorkerFormation.vue @@ -134,6 +134,7 @@ const columns = computed(() => [ :is-editable="true" :use-model="true" :default-remove="false" + search-url="formation" /> </template> <style lang="scss" scoped> diff --git a/src/pages/Worker/Card/WorkerMedical.vue b/src/pages/Worker/Card/WorkerMedical.vue index fab1416c9..9620c23c2 100644 --- a/src/pages/Worker/Card/WorkerMedical.vue +++ b/src/pages/Worker/Card/WorkerMedical.vue @@ -100,5 +100,6 @@ const columns = [ :is-editable="true" :use-model="true" :default-remove="false" + search-url="medical" /> </template> diff --git a/src/pages/Worker/Card/WorkerNotificationsManager.vue b/src/pages/Worker/Card/WorkerNotificationsManager.vue index 731e073cd..4ebe9b0dd 100644 --- a/src/pages/Worker/Card/WorkerNotificationsManager.vue +++ b/src/pages/Worker/Card/WorkerNotificationsManager.vue @@ -70,6 +70,7 @@ function setNotifications(data) { :default-remove="false" :default-save="false" @on-fetch="setNotifications" + search-url="notifications" > <template #body> <div diff --git a/src/pages/Worker/Card/WorkerPda.vue b/src/pages/Worker/Card/WorkerPda.vue index a53aac270..a5e8ae3fa 100644 --- a/src/pages/Worker/Card/WorkerPda.vue +++ b/src/pages/Worker/Card/WorkerPda.vue @@ -63,6 +63,7 @@ function reloadData() { url="DeviceProductionUsers" :filter="{ where: { userFk: routeId } }" order="id" + search-url="pda" auto-load > <template #body="{ rows }"> From 2bf7fa46ca8a546e4e5fb379b502407e55c92d0f Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 7 Nov 2024 12:53:17 +0100 Subject: [PATCH 148/150] perf: remove appendParams --- src/components/VnTable/VnTable.vue | 6 +----- src/composables/useArrayData.js | 3 +-- src/pages/Customer/components/CustomerSummaryTable.vue | 3 +-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index cdf450966..a4948156a 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -62,7 +62,7 @@ const $props = defineProps({ default: 'flex-one', }, searchUrl: { - type: String, + type: [String, Boolean], default: 'table', }, isEditable: { @@ -73,10 +73,6 @@ const $props = defineProps({ type: Boolean, default: false, }, - appendParams: { - type: Boolean, - default: true, - }, hasSubToolbar: { type: Boolean, default: null, diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index e33cb8b78..747c6ab64 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -247,9 +247,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { } function updateStateParams() { - if (!route) return; const newUrl = { path: route.path, query: { ...(route.query ?? {}) } }; - if (store?.appendParams ?? true) + if (store?.searchUrl) newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter); if (store.navigate) { diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue index 745cbf29e..59e82e252 100644 --- a/src/pages/Customer/components/CustomerSummaryTable.vue +++ b/src/pages/Customer/components/CustomerSummaryTable.vue @@ -183,7 +183,6 @@ const getItemPackagingType = (ticketSales) => { :column-search="false" url="Tickets" :columns="columns" - append-params="false" :without-header="true" auto-load :row-click="rowClick" @@ -191,7 +190,7 @@ const getItemPackagingType = (ticketSales) => { :disable-option="{ card: true, table: true }" class="full-width" :disable-infinite-scroll="true" - search-url="tickets" + :search-url="false" > <template #column-nickname="{ row }"> <span class="link"> From e210ad7de4d0eaff62cdcd176fdef8026003adac Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 7 Nov 2024 13:06:33 +0100 Subject: [PATCH 149/150] Merge branch 'dev' into fix_customer_issues --- src/pages/Customer/components/CustomerSummaryTable.vue | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue index e0ef6a5ef..59e82e252 100644 --- a/src/pages/Customer/components/CustomerSummaryTable.vue +++ b/src/pages/Customer/components/CustomerSummaryTable.vue @@ -63,11 +63,7 @@ const columns = computed(() => [ }, { align: 'left', -<<<<<<< HEAD format: (row) => dashIfEmpty(row.agencyMode?.name), -======= - format: (row, dashIfEmpty) => dashIfEmpty(row.agencyMode?.name), ->>>>>>> dev columnClass: 'expand', label: t('Agency'), }, From 0b5be9e67fef0e164c3d6ca5c429892263d8c1d4 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Thu, 7 Nov 2024 17:19:35 +0100 Subject: [PATCH 150/150] chore: correct checkNotification --- test/cypress/support/commands.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 73c786680..b536121b1 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -280,13 +280,14 @@ Cypress.Commands.add('openActions', (row) => { cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click(); }); -Cypress.Commands.add('checkNotification', (type) => { - const values = { - created: 'Data created', - updated: 'Data saved', - deleted: 'Data deleted', - }; - cy.get('.q-notification__message').should('have.text', values[type]); +Cypress.Commands.add('checkNotification', (text) => { + cy.get('.q-notification') + .should('be.visible') + .last() + .then(($lastNotification) => { + if (!Cypress.$($lastNotification).text().includes(text)) + throw new Error(`Notification not found: "${text}"`); + }); }); Cypress.Commands.add('checkValueForm', (id, search) => {