From 09582ab6a895a7a763dc24812bc30ac7238cb957 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 13 Mar 2024 09:05:23 -0300
Subject: [PATCH 01/15] WIP

---
 src/i18n/en/index.js             |  16 +
 src/i18n/es/index.js             |  16 +
 src/pages/Item/ItemList.vue      | 550 ++++++++++++++++++++++++++++++-
 src/router/modules/item.js       |   4 +-
 src/stores/useNavigationStore.js |   1 +
 5 files changed, 584 insertions(+), 3 deletions(-)

diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 94d552be3..6c4cc34f3 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -1164,6 +1164,22 @@ export default {
             warehouseText: 'Calculated on the warehouse of { warehouseName }',
             itemDiary: 'Item diary',
         },
+        list: {
+            id: 'Identifier',
+            grouping: 'Grouping',
+            description: 'Description',
+            stems: 'Stems',
+            category: 'Category',
+            type: 'Type',
+            intrastat: 'Intrastat',
+            size: 'Size',
+            origin: 'Origin',
+            userName: 'Buyer',
+            weightByPiece: 'Weight/Piece',
+            multiplier: 'Multiplier',
+            producer: 'Producer',
+            landed: 'Landed',
+        },
     },
     components: {
         topbar: {},
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index bcd780f5b..4ad9a1d5c 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -1164,6 +1164,22 @@ export default {
             warehouseText: 'Calculado sobre el almacén de { warehouseName }',
             itemDiary: 'Registro de compra-venta',
         },
+        list: {
+            id: 'Identificador',
+            grouping: 'Grouping',
+            description: 'Descripción',
+            stems: 'Tallos',
+            category: 'Reino',
+            type: 'Tipo',
+            intrastat: 'Intrastat',
+            size: 'Medida',
+            origin: 'Origen',
+            weightByPiece: 'Peso (gramos)/tallo',
+            userName: 'Comprador',
+            multiplier: 'Multiplicador',
+            producer: 'Productor',
+            landed: 'F. entrega',
+        },
     },
     components: {
         topbar: {},
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 49a5dbb64..14549296b 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -1 +1,549 @@
-<template>Item list</template>
+<script setup>
+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 VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
+import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue';
+import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
+
+import { useStateStore } from 'stores/useStateStore';
+import { toDate, toCurrency } from 'src/filters';
+import { useSession } from 'composables/useSession';
+import { dashIfEmpty } from 'src/filters';
+import { useArrayData } from 'composables/useArrayData';
+
+const router = useRouter();
+const session = useSession();
+const token = session.getToken();
+const stateStore = useStateStore();
+const { t } = useI18n();
+
+const itemTypesOptions = ref([]);
+const originsOptions = ref([]);
+const itemFamiliesOptions = ref([]);
+// const intrastatOptions = ref([]);
+const packagingsOptions = 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 params = reactive({});
+const arrayData = useArrayData('ItemList', {
+    url: 'Items/filter',
+    order: ['isActive DESC', 'name', 'id'],
+    exprBuilder: exprBuilder,
+});
+const store = arrayData.store;
+const rows = computed(() => store.data);
+
+// const getInputEvents = (col) => {
+//     return col.columnFilter.type === 'select'
+//         ? { 'update:modelValue': () => applyColumnFilter(col) }
+//         : {
+//               'keyup.enter': () => applyColumnFilter(col),
+//           };
+// };
+
+const columns = computed(() => [
+    {
+        label: '',
+        name: 'picture',
+        align: 'left',
+    },
+    {
+        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,
+        //     },
+        // },
+    },
+    {
+        label: t('item.list.grouping'),
+        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: 'description',
+        name: 'description',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnInput,
+        //     type: 'text',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         dense: true,
+        //     },
+        // },
+        // format: (val) => dashIfEmpty(val),
+    },
+    {
+        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.type'),
+        field: 'typeName',
+        name: 'typeName',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnSelectFilter,
+        //     type: 'select',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         options: itemTypesOptions.value,
+        //         'option-value': 'code',
+        //         'option-label': 'code',
+        //         dense: true,
+        //     },
+        // },
+    },
+
+    {
+        label: t('item.list.category'),
+        field: 'category',
+        name: 'category',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnSelectFilter,
+        //     type: 'select',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         options: intrastatOptions.value,
+        //         'option-value': 'description',
+        //         'option-label': 'description',
+        //         dense: true,
+        //     },
+        // },
+    },
+
+    {
+        label: t('item.list.intrastat'),
+        field: 'intrastat',
+        name: 'intrastat',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnSelectFilter,
+        //     type: 'select',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         options: originsOptions.value,
+        //         'option-value': 'code',
+        //         'option-label': 'code',
+        //         dense: true,
+        //     },
+        // },
+    },
+    {
+        label: t('item.list.origin'),
+        field: 'origin',
+        name: 'origin',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnInput,
+        //     type: 'text',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         dense: true,
+        //     },
+        // },
+    },
+    {
+        label: t('item.list.userName'),
+        field: 'userName',
+        name: 'userName',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnSelectFilter,
+        //     type: 'select',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         options: itemFamiliesOptions.value,
+        //         'option-value': 'code',
+        //         'option-label': 'code',
+        //         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,
+        //     },
+        // },
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('item.list.multiplier'),
+        field: 'stemMultiplier',
+        name: 'stemMultiplier',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnInput,
+        //     type: 'text',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         dense: true,
+        //     },
+        // },
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('entry.latestBuys.isActive'),
+        field: 'isActive',
+        name: 'isActive',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnInput,
+        //     type: 'text',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         dense: true,
+        //     },
+        // },
+    },
+    {
+        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,
+        //     },
+        // },
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('item.list.landed'),
+        field: 'landed',
+        name: 'landed',
+        align: 'left',
+        sortable: true,
+        // columnFilter: {
+        //     component: VnInput,
+        //     type: 'text',
+        //     filterValue: null,
+        //     event: getInputEvents,
+        //     attrs: {
+        //         dense: true,
+        //     },
+        // },
+        format: (val) => dashIfEmpty(toDate(val)),
+    },
+    {
+        label: '',
+        name: 'actions',
+        align: 'left',
+    },
+]);
+
+const redirectToItemCreate = () => {
+    // router.push({ name: 'EntryBuys', params: { id: entryFk } });
+};
+
+const redirectToItemSummary = () => {
+    // router.push({ name: 'EntryBuys', params: { id: entryFk } });
+};
+
+// const applyColumnFilter = async (col) => {
+//     try {
+//         params[col.field] = col.columnFilter.filterValue;
+//         await arrayData.addFilter({ params });
+//     } catch (err) {
+//         console.error('Error applying column filter', err);
+//     }
+// };
+
+onMounted(async () => {
+    stateStore.rightDrawer = true;
+    const filteredColumns = columns.value.filter((col) => col.name !== 'picture');
+    allColumnNames.value = filteredColumns.map((col) => col.name);
+    await arrayData.fetch({ append: false });
+});
+
+onUnmounted(() => (stateStore.rightDrawer = false));
+</script>
+
+<template>
+    <!-- <FetchData
+        url="ItemTypes"
+        :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }"
+        auto-load
+        @on-fetch="(data) => (itemTypesOptions = data)"
+    /> -->
+    <!-- <FetchData
+        url="Origins"
+        :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }"
+        auto-load
+        @on-fetch="(data) => (originsOptions = data)"
+    /> -->
+    <!-- <FetchData
+        url="ItemFamilies"
+        :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }"
+        auto-load
+        @on-fetch="(data) => (itemFamiliesOptions = data)"
+    /> -->
+    <!-- <FetchData
+        url="Packagings"
+        :filter="{ fields: ['id'], order: 'id ASC', limit: 30 }"
+        auto-load
+        @on-fetch="(data) => (packagingsOptions = data)"
+    /> -->
+    <QToolbar class="bg-vn-dark justify-end">
+        <div id="st-data">
+            <TableVisibleColumns
+                :all-columns="allColumnNames"
+                table-code="itemsIndex"
+                labels-traductions-path="item.list"
+                @on-config-saved="visibleColumns = ['picture', ...$event]"
+            />
+        </div>
+        <QSpace />
+        <div id="st-actions"></div>
+    </QToolbar>
+    <!-- <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
+        <QScrollArea class="fit text-grey-8">
+            <EntryLatestBuysFilter data-key="EntryLatestBuys" :tags="tags" />
+        </QScrollArea>
+    </QDrawer> -->
+    <QPage class="column items-center q-pa-md">
+        <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 />
+                    <QTd
+                        v-for="(col, index) in cols"
+                        :key="index"
+                        style="max-width: 100px"
+                    >
+                        <component
+                            :is="col.columnFilter.component"
+                            v-if="col.name !== 'picture'"
+                            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>
+                    <QImg
+                        :src="`/api/Images/catalog/50x50/${row.id}/download?access_token=${token}`"
+                        spinner-color="primary"
+                        :ratio="1"
+                        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>
+                    <fetched-tags :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="{}">
+                <QTd>
+                    <QIcon
+                        @click.stop="viewSummary($props.id, CustomerSummary)"
+                        class="q-ml-sm"
+                        color="primary"
+                        name="vn:clone"
+                        size="sm"
+                    >
+                        <QTooltip>
+                            {{ t('Preview') }}
+                        </QTooltip>
+                    </QIcon>
+                    <QIcon
+                        @click.stop="viewSummary($props.id, CustomerSummary)"
+                        class="q-ml-md"
+                        color="primary"
+                        name="preview"
+                        size="sm"
+                    >
+                        <QTooltip>
+                            {{ t('Preview') }}
+                        </QTooltip>
+                    </QIcon>
+                </QTd>
+            </template>
+        </QTable>
+        <QPageSticky :offset="[20, 20]">
+            <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" />
+            <QTooltip>
+                {{ t('New item') }}
+            </QTooltip>
+        </QPageSticky>
+    </QPage>
+</template>
+
+<i18n>
+es:
+    New item: Nuevo artículo
+</i18n>
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index d3462e15c..700278617 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -10,7 +10,7 @@ export default {
     component: RouterView,
     redirect: { name: 'ItemMain' },
     menus: {
-        main: [],
+        main: ['ItemList'],
         card: [],
     },
     children: [
@@ -18,7 +18,7 @@ export default {
             path: '',
             name: 'ItemMain',
             component: () => import('src/pages/Item/ItemMain.vue'),
-            redirect: { name: 'Itemlist' },
+            redirect: { name: 'ItemList' },
             children: [
                 {
                     path: 'list',
diff --git a/src/stores/useNavigationStore.js b/src/stores/useNavigationStore.js
index afd3af0c0..f075301f6 100644
--- a/src/stores/useNavigationStore.js
+++ b/src/stores/useNavigationStore.js
@@ -7,6 +7,7 @@ import routes from 'src/router/modules';
 
 export const useNavigationStore = defineStore('navigationStore', () => {
     const modules = [
+        'item',
         'shelving',
         'order',
         'customer',

From aa2db28edab94ee3f0da0771c3e71337697399d6 Mon Sep 17 00:00:00 2001
From: carlosfonseca <carlos.fonseca@mindshore.io>
Date: Fri, 22 Mar 2024 08:30:54 -0500
Subject: [PATCH 02/15] Se crea submodulo mis documentos en worker

---
 src/css/app.scss                              |   5 +
 src/i18n/en/index.js                          |   5 +-
 src/i18n/es/index.js                          |   5 +-
 src/pages/Worker/Card/WorkerDms.vue           | 164 +++++++++++
 .../Worker/components/WorkerDmsActions.vue    |  96 +++++++
 .../Worker/components/WorkerDmsCreate.vue     | 262 ++++++++++++++++++
 .../Worker/components/WorkerDmsDelete.vue     |  82 ++++++
 src/pages/Worker/components/WorkerDmsEdit.vue | 246 ++++++++++++++++
 src/router/modules/worker.js                  |  31 ++-
 9 files changed, 891 insertions(+), 5 deletions(-)
 create mode 100644 src/pages/Worker/Card/WorkerDms.vue
 create mode 100644 src/pages/Worker/components/WorkerDmsActions.vue
 create mode 100644 src/pages/Worker/components/WorkerDmsCreate.vue
 create mode 100644 src/pages/Worker/components/WorkerDmsDelete.vue
 create mode 100644 src/pages/Worker/components/WorkerDmsEdit.vue

diff --git a/src/css/app.scss b/src/css/app.scss
index 3baa60c9b..c52cdb5d2 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -85,6 +85,11 @@ select:-webkit-autofill {
     color: $white;
 }
 
+.card-width {
+    max-width: 800px;
+    width: 100%;
+}
+
 .vn-card {
     background-color: var(--vn-section-color);
     color: var(--vn-text-color);
diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 80744b83d..197e94582 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -837,6 +837,7 @@ export default {
             workerCreate: 'New worker',
             department: 'Department',
             pda: 'PDA',
+            dms: 'My documentation',
         },
         list: {
             name: 'Name',
@@ -955,7 +956,7 @@ export default {
             roadmap: 'Roadmap',
             summary: 'Summary',
             basicData: 'Basic Data',
-            stops: 'Stops'
+            stops: 'Stops',
         },
     },
     roadmap: {
@@ -963,7 +964,7 @@ export default {
             roadmap: 'Roadmap',
             summary: 'Summary',
             basicData: 'Basic Data',
-            stops: 'Stops'
+            stops: 'Stops',
         },
     },
     route: {
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 37ba8da85..61896a283 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -837,6 +837,7 @@ export default {
             workerCreate: 'Nuevo trabajador',
             department: 'Departamentos',
             pda: 'PDA',
+            dms: 'Mi documentación',
         },
         list: {
             name: 'Nombre',
@@ -955,7 +956,7 @@ export default {
             roadmap: 'Troncales',
             summary: 'Resumen',
             basicData: 'Datos básicos',
-            stops: 'Paradas'
+            stops: 'Paradas',
         },
     },
     roadmap: {
@@ -963,7 +964,7 @@ export default {
             roadmap: 'Troncales',
             summary: 'Resumen',
             basicData: 'Datos básicos',
-            stops: 'Paradas'
+            stops: 'Paradas',
         },
     },
     route: {
diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
new file mode 100644
index 000000000..144e3edee
--- /dev/null
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -0,0 +1,164 @@
+<script setup>
+import { computed, ref, watch } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRoute, useRouter } from 'vue-router';
+
+import { downloadFile } from 'src/composables/downloadFile';
+import { toDateHour } from 'src/filters';
+
+import FetchData from 'components/FetchData.vue';
+import WorkerDmsActions from '../components/WorkerDmsActions.vue';
+
+const { t } = useI18n();
+const route = useRoute();
+const router = useRouter();
+
+const workerDmsRef = ref(null);
+const rows = ref([]);
+
+const filter = { where: { worker: route.params.id }, order: ['dmsFk DESC'], limit: 20 };
+
+const columns = computed(() => [
+    {
+        align: 'left',
+        field: 'dmsFk',
+        label: t('Id'),
+        name: 'id',
+    },
+    {
+        align: 'left',
+        field: 'hardCopyNumber',
+        label: t('Order'),
+        name: 'order',
+    },
+    {
+        align: 'left',
+        field: 'reference',
+        label: t('Reference'),
+        name: 'reference',
+    },
+    {
+        align: 'left',
+        field: 'description',
+        label: t('Description'),
+        name: 'description',
+    },
+    {
+        align: 'left',
+        field: 'hasFile',
+        label: t('Original'),
+        name: 'original',
+    },
+    {
+        align: 'left',
+        field: 'file',
+        label: t('File'),
+        name: 'file',
+    },
+    {
+        align: 'left',
+        field: 'created',
+        label: t('Created'),
+        name: 'created',
+        format: (value) => toDateHour(value),
+    },
+    {
+        align: 'right',
+        field: 'actions',
+        label: '',
+        name: 'actions',
+    },
+]);
+
+watch(
+    () => route.params.id,
+    (newValue) => {
+        filter.where.worker = newValue;
+        setData();
+    }
+);
+
+const setData = () => {
+    workerDmsRef.value.fetch();
+};
+
+const toWorkerDmsCreate = () => {
+    router.push({ name: 'WorkerDmsCreate' });
+};
+</script>
+
+<template>
+    <FetchData
+        ref="workerDmsRef"
+        :filter="filter"
+        @on-fetch="(data) => (rows = data)"
+        auto-load
+        :url="`WorkerDms/${route.params.id}/filter`"
+    />
+
+    <QPage class="column items-center q-pa-md">
+        <QTable
+            :columns="columns"
+            :pagination="{ rowsPerPage: 12 }"
+            :rows="rows"
+            class="full-width q-mt-md"
+            row-key="id"
+            v-if="rows?.length"
+        >
+            <template #body-cell-order="{ row }">
+                <QTd>
+                    <QChip class="chip-color" square>{{ row.hardCopyNumber }}</QChip>
+                </QTd>
+            </template>
+
+            <template #body-cell-original="{ row }">
+                <QTd>
+                    <QCheckbox :model-value="row.hasFile === 1" disable />
+                </QTd>
+            </template>
+
+            <template #body-cell-file="{ row }">
+                <QTd>
+                    <QBtn @click.stop="downloadFile(row.dmsFk)" color="blue" flat no-caps>
+                        {{ row.file }}
+                    </QBtn>
+                </QTd>
+            </template>
+
+            <template #body-cell-actions="{ row }">
+                <QTd>
+                    <WorkerDmsActions :id="row.dmsFk" :promise="setData" />
+                </QTd>
+            </template>
+        </QTable>
+
+        <h5 class="flex justify-center color-vn-label" v-else>
+            {{ t('globals.noResults') }}
+        </h5>
+    </QPage>
+
+    <QPageSticky :offset="[18, 18]">
+        <QBtn @click.stop="toWorkerDmsCreate()" color="primary" fab icon="add" />
+        <QTooltip>
+            {{ t('Upload file') }}
+        </QTooltip>
+    </QPageSticky>
+</template>
+
+<style scoped>
+.chip-color {
+    background-color: var(--vn-label);
+}
+</style>
+
+<i18n>
+es:
+    Id: Id
+    Order: Orden
+    Reference: Referencia
+    Description: Descripción
+    File: Archivo
+    Original: Original
+    Created: Fecha creación
+    Upload file: Subir fichero
+</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsActions.vue b/src/pages/Worker/components/WorkerDmsActions.vue
new file mode 100644
index 000000000..9a8e4816e
--- /dev/null
+++ b/src/pages/Worker/components/WorkerDmsActions.vue
@@ -0,0 +1,96 @@
+<script setup>
+import { useI18n } from 'vue-i18n';
+import { useRoute, useRouter } from 'vue-router';
+
+import { useQuasar } from 'quasar';
+
+import { downloadFile } from 'src/composables/downloadFile';
+
+import WorkerDmsDelete from 'src/pages/Worker/components/WorkerDmsDelete.vue';
+
+const { t } = useI18n();
+const quasar = useQuasar();
+const route = useRoute();
+const router = useRouter();
+
+const $props = defineProps({
+    id: {
+        type: Number,
+        required: true,
+    },
+    promise: {
+        type: Function,
+        required: true,
+    },
+});
+
+const setDownloadFile = () => downloadFile($props.id);
+
+const toWorkerDmsEdit = () => {
+    router.push({
+        name: 'WorkerDmsEdit',
+        params: {
+            id: route.params.id,
+            dmsId: $props.id,
+        },
+    });
+};
+
+const showWorkerDmsDeleteDialog = () => {
+    quasar.dialog({
+        component: WorkerDmsDelete,
+        componentProps: {
+            id: $props.id,
+            promise: setData,
+        },
+    });
+};
+
+const setData = () => {
+    $props.promise();
+};
+</script>
+
+<template>
+    <div>
+        <QIcon
+            @click.stop="setDownloadFile"
+            color="primary"
+            name="cloud_download"
+            size="sm"
+        >
+            <QTooltip>
+                {{ t('actionFile', { action: t('globals.download') }) }}
+            </QTooltip>
+        </QIcon>
+        <QIcon
+            @click.stop="toWorkerDmsEdit"
+            class="q-ml-md"
+            color="primary"
+            name="edit"
+            size="sm"
+        >
+            <QTooltip>
+                {{ t('actionFile', { action: t('globals.edit') }) }}
+            </QTooltip>
+        </QIcon>
+        <QIcon
+            @click.stop="showWorkerDmsDeleteDialog"
+            class="q-ml-md"
+            color="primary"
+            name="delete"
+            size="sm"
+        >
+            <QTooltip>
+                {{ t('actionFile', { action: t('globals.remove') }) }}
+            </QTooltip>
+        </QIcon>
+    </div>
+</template>
+
+<i18n>
+en:
+    actionFile: '{action} file'
+es:
+    actionFile: '{action} fichero'
+</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsCreate.vue b/src/pages/Worker/components/WorkerDmsCreate.vue
new file mode 100644
index 000000000..d38e044ca
--- /dev/null
+++ b/src/pages/Worker/components/WorkerDmsCreate.vue
@@ -0,0 +1,262 @@
+<script setup>
+import { onBeforeMount, ref, watch } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRoute, useRouter } from 'vue-router';
+
+import axios from 'axios';
+
+import { useState } from 'src/composables/useState';
+import { useValidator } from 'src/composables/useValidator';
+import useNotify from 'src/composables/useNotify';
+
+import FetchData from 'components/FetchData.vue';
+import VnRow from 'components/ui/VnRow.vue';
+import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+
+const { notify } = useNotify();
+const { t } = useI18n();
+const { validate } = useValidator();
+const route = useRoute();
+const router = useRouter();
+const state = useState();
+const user = state.getUser();
+
+const filterFindOne = { where: { code: 'paymentsLaw' } };
+const filterCompanies = { order: ['code'] };
+const filterWarehouses = { order: ['name'] };
+
+const inputFileRef = ref();
+const client = ref({});
+const findOne = ref([]);
+const allowedContentTypes = ref([]);
+const optionsCompanies = ref([]);
+const optionsWarehouses = ref([]);
+const optionsDmsTypes = ref([]);
+const isLoading = ref(false);
+const dms = ref({
+    hasFile: false,
+});
+
+onBeforeMount(() => {
+    const { companyFk, warehouseFk } = user.value;
+    dms.value.reference = route.params.id;
+    dms.value.companyId = companyFk;
+    dms.value.warehouseId = warehouseFk;
+});
+
+watch([client, findOne], ([newClient, newFindOne]) => {
+    dms.value.description = t('clientFileDescription', {
+        dmsTypeName: newFindOne.name?.toUpperCase(),
+        clientName: newClient.name?.toUpperCase(),
+        clientId: newClient.id,
+    });
+    dms.value.dmsTypeId = newFindOne.id;
+});
+
+const saveData = async () => {
+    try {
+        const formData = new FormData();
+        const files = dms.value.files;
+
+        if (files && files.length > 0) {
+            for (let file of files) {
+                formData.append(file.name, file);
+            }
+            dms.value.hasFileAttached = true;
+
+            const url = `Workers/${route.params.id}/uploadFile`;
+            await axios.post(url, formData, {
+                params: dms.value,
+            });
+            notify('globals.dataSaved', 'positive');
+            toWorkerDms();
+        }
+    } catch (error) {
+        notify(error.message, 'negative');
+    }
+};
+
+const toWorkerDms = () => {
+    router.push({ name: 'WorkerDms' });
+};
+</script>
+
+<template>
+    <fetch-data
+        @on-fetch="(data) => (client = data)"
+        auto-load
+        :url="`Clients/${route.params.id}/getCard`"
+    />
+    <fetch-data
+        :filter="filterFindOne"
+        @on-fetch="(data) => (findOne = data)"
+        auto-load
+        url="DmsTypes/findOne"
+    />
+    <fetch-data
+        @on-fetch="(data) => (allowedContentTypes = data)"
+        auto-load
+        url="DmsContainers/allowedContentTypes"
+    />
+    <fetch-data
+        :filter="filterCompanies"
+        @on-fetch="(data) => (optionsCompanies = data)"
+        auto-load
+        url="Companies"
+    />
+    <fetch-data
+        :filter="filterWarehouses"
+        @on-fetch="(data) => (optionsWarehouses = data)"
+        auto-load
+        url="Warehouses"
+    />
+    <fetch-data
+        :filter="filterWarehouses"
+        @on-fetch="(data) => (optionsDmsTypes = data)"
+        auto-load
+        url="DmsTypes"
+    />
+
+    <Teleport to="#st-actions">
+        <QBtnGroup push class="q-gutter-x-sm">
+            <QBtn
+                :disabled="isLoading"
+                :label="t('globals.cancel')"
+                :loading="isLoading"
+                @click="toWorkerDms"
+                color="primary"
+                flat
+                icon="close"
+            />
+            <QBtn
+                :disabled="isLoading"
+                :label="t('globals.save')"
+                :loading="isLoading"
+                @click.stop="saveData"
+                color="primary"
+                icon="save"
+            />
+        </QBtnGroup>
+    </Teleport>
+
+    <div class="full-width flex justify-center">
+        <QCard class="card-width q-pa-lg">
+            <QCardSection>
+                <QForm>
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <VnInput
+                                :label="t('Reference')"
+                                clearable
+                                v-model="dms.reference"
+                            />
+                        </div>
+                        <div class="col">
+                            <VnSelectFilter
+                                :label="t('Company')"
+                                :options="optionsCompanies"
+                                :rules="validate('entry.companyFk')"
+                                option-label="code"
+                                option-value="id"
+                                v-model="dms.companyId"
+                            />
+                        </div>
+                    </VnRow>
+
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <VnSelectFilter
+                                :label="t('Warehouse')"
+                                :options="optionsWarehouses"
+                                option-label="name"
+                                option-value="id"
+                                v-model="dms.warehouseId"
+                            />
+                        </div>
+                        <div class="col">
+                            <VnSelectFilter
+                                :label="t('Type')"
+                                :options="optionsDmsTypes"
+                                option-label="name"
+                                option-value="id"
+                                v-model="dms.dmsTypeId"
+                            />
+                        </div>
+                    </VnRow>
+
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <VnInput
+                                :label="t('Description')"
+                                :rules="validate('route.description')"
+                                clearable
+                                type="textarea"
+                                v-model="dms.description"
+                            />
+                        </div>
+                    </VnRow>
+
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <QFile
+                                ref="inputFileRef"
+                                class="required"
+                                :label="t('File')"
+                                v-model="dms.files"
+                                multiple
+                                :accept="allowedContentTypes.join(',')"
+                                clearable
+                                clear-icon="close"
+                            >
+                                <template #append>
+                                    <QBtn
+                                        icon="vn:attach"
+                                        flat
+                                        round
+                                        padding="xs"
+                                        @click="inputFileRef.pickFiles()"
+                                    >
+                                        <QTooltip>
+                                            {{ t('Select a file') }}
+                                        </QTooltip>
+                                    </QBtn>
+                                    <QBtn icon="info" flat round padding="xs">
+                                        <QTooltip max-width="30rem">
+                                            {{
+                                                `${t(
+                                                    'Allowed content types'
+                                                )}: ${allowedContentTypes.join(', ')}`
+                                            }}
+                                        </QTooltip>
+                                    </QBtn>
+                                </template>
+                            </QFile>
+                        </div>
+                    </VnRow>
+
+                    <QCheckbox
+                        :label="t('Generate identifier for original file')"
+                        v-model="dms.hasFile"
+                    />
+                </QForm>
+            </QCardSection>
+        </QCard>
+    </div>
+</template>
+
+<i18n>
+en:
+    clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}'
+es:
+    Reference: Referencia
+    Company: Empresa
+    Warehouse: Almacén
+    Type: Tipo
+    Description: Descripción
+    clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}'
+    File: Fichero
+    Select a file: Selecciona un fichero
+    Allowed content types: Tipos de archivo permitidos
+    Generate identifier for original file: Generar identificador para archivo original
+</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsDelete.vue b/src/pages/Worker/components/WorkerDmsDelete.vue
new file mode 100644
index 000000000..5e1b98b24
--- /dev/null
+++ b/src/pages/Worker/components/WorkerDmsDelete.vue
@@ -0,0 +1,82 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+import { useDialogPluginComponent } from 'quasar';
+import axios from 'axios';
+
+import useNotify from 'src/composables/useNotify';
+
+const $props = defineProps({
+    id: {
+        type: Number,
+        required: true,
+    },
+    promise: {
+        type: Function,
+        required: true,
+    },
+});
+
+const { dialogRef } = useDialogPluginComponent();
+const { notify } = useNotify();
+const { t } = useI18n();
+
+const closeButton = ref(null);
+
+const isLoading = ref(false);
+
+const deleteDms = async () => {
+    isLoading.value = true;
+    try {
+        await axios.post(`WorkerDms/${$props.id}/removeFile`);
+        if ($props.promise) await $props.promise();
+        notify('globals.dataDeleted', 'positive');
+    } catch (error) {
+        notify(error.message, 'negative');
+    } finally {
+        closeButton.value.click();
+        isLoading.value = false;
+    }
+};
+</script>
+
+<template>
+    <QDialog ref="dialogRef">
+        <QCard class="q-pa-md q-mb-md">
+            <span ref="closeButton" class="row justify-end close-icon" v-close-popup>
+                <QIcon name="close" size="sm" />
+            </span>
+
+            <QCardSection>
+                <div class="mt-1 text-h6">{{ t('This file will be deleted') }}</div>
+                <div>{{ t('Are you sure you want to continue?') }}</div>
+            </QCardSection>
+
+            <QCardActions class="flex justify-end">
+                <QBtn
+                    :disabled="isLoading"
+                    :label="t('globals.cancel')"
+                    :loading="isLoading"
+                    class="q-mr-xl"
+                    color="primary"
+                    flat
+                    v-close-popup
+                />
+                <QBtn
+                    :disabled="isLoading"
+                    :label="t('globals.save')"
+                    :loading="isLoading"
+                    @click.stop="deleteDms"
+                    color="primary"
+                />
+            </QCardActions>
+        </QCard>
+    </QDialog>
+</template>
+
+<i18n>
+es:
+    This file will be deleted: Este fichero va a ser borrado
+    Are you sure you want to continue?: ¿Seguro que quieres continuar?
+</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsEdit.vue b/src/pages/Worker/components/WorkerDmsEdit.vue
new file mode 100644
index 000000000..8e84c6326
--- /dev/null
+++ b/src/pages/Worker/components/WorkerDmsEdit.vue
@@ -0,0 +1,246 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRoute, useRouter } from 'vue-router';
+
+import axios from 'axios';
+
+import { useValidator } from 'src/composables/useValidator';
+import useNotify from 'src/composables/useNotify';
+
+import FetchData from 'components/FetchData.vue';
+import VnRow from 'components/ui/VnRow.vue';
+import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+
+const { notify } = useNotify();
+const { t } = useI18n();
+const { validate } = useValidator();
+const route = useRoute();
+const router = useRouter();
+
+const filterCompanies = { order: ['code'] };
+const filterWarehouses = { order: ['name'] };
+
+const inputFileRef = ref();
+const allowedContentTypes = ref([]);
+const optionsCompanies = ref([]);
+const optionsWarehouses = ref([]);
+const optionsDmsTypes = ref([]);
+const isLoading = ref(false);
+const dms = ref({
+    hasFile: true,
+});
+
+const setCurrentDms = (data) => {
+    dms.value.reference = data.reference;
+    dms.value.companyId = data.companyFk;
+    dms.value.warehouseId = data.warehouseFk;
+    dms.value.dmsTypeId = data.dmsTypeFk;
+    dms.value.description = data.description;
+    dms.value.hasFile = data.hasFile;
+};
+
+const saveData = async () => {
+    try {
+        const formData = new FormData();
+        console.log(formData);
+        const files = dms.value.files;
+        console.log(files);
+
+        dms.value.hasFileAttached = files ? true : false;
+        const url = `dms/${route.params.dmsId}/updateFile`;
+
+        if (files && files.length > 0) {
+            for (let file of files) {
+                formData.append(file.name, file);
+            }
+
+            await axios.post(url, formData, {
+                params: dms.value,
+            });
+            notify('globals.dataSaved', 'positive');
+        } else {
+            await axios.post(url, dms.value);
+        }
+
+        toWorkerDms();
+    } catch (error) {
+        notify(error.message, 'negative');
+    }
+};
+
+const toWorkerDms = () => {
+    router.push({ name: 'WorkerDms' });
+};
+</script>
+
+<template>
+    <fetch-data :url="`Dms/${route.params.dmsId}`" @on-fetch="setCurrentDms" auto-load />
+    <fetch-data
+        @on-fetch="(data) => (allowedContentTypes = data)"
+        auto-load
+        url="DmsContainers/allowedContentTypes"
+    />
+    <fetch-data
+        :filter="filterCompanies"
+        @on-fetch="(data) => (optionsCompanies = data)"
+        auto-load
+        url="Companies"
+    />
+    <fetch-data
+        :filter="filterWarehouses"
+        @on-fetch="(data) => (optionsWarehouses = data)"
+        auto-load
+        url="Warehouses"
+    />
+    <fetch-data
+        :filter="filterWarehouses"
+        @on-fetch="(data) => (optionsDmsTypes = data)"
+        auto-load
+        url="DmsTypes"
+    />
+
+    <Teleport to="#st-actions">
+        <QBtnGroup push class="q-gutter-x-sm">
+            <QBtn
+                :disabled="isLoading"
+                :label="t('globals.cancel')"
+                :loading="isLoading"
+                @click="toWorkerDms"
+                color="primary"
+                flat
+                icon="close"
+            />
+            <QBtn
+                :disabled="isLoading"
+                :label="t('globals.save')"
+                :loading="isLoading"
+                @click.stop="saveData"
+                color="primary"
+                icon="save"
+            />
+        </QBtnGroup>
+    </Teleport>
+
+    <div class="full-width flex justify-center">
+        <QCard class="card-width q-pa-lg">
+            <QCardSection>
+                <QForm>
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <VnInput
+                                :label="t('Reference')"
+                                clearable
+                                v-model="dms.reference"
+                            />
+                        </div>
+                        <div class="col">
+                            <VnSelectFilter
+                                :label="t('Company')"
+                                :options="optionsCompanies"
+                                :rules="validate('entry.companyFk')"
+                                option-label="code"
+                                option-value="id"
+                                v-model="dms.companyId"
+                            />
+                        </div>
+                    </VnRow>
+
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <VnSelectFilter
+                                :label="t('Warehouse')"
+                                :options="optionsWarehouses"
+                                option-label="name"
+                                option-value="id"
+                                v-model="dms.warehouseId"
+                            />
+                        </div>
+                        <div class="col">
+                            <VnSelectFilter
+                                :label="t('Type')"
+                                :options="optionsDmsTypes"
+                                option-label="name"
+                                option-value="id"
+                                v-model="dms.dmsTypeId"
+                            />
+                        </div>
+                    </VnRow>
+
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <VnInput
+                                :label="t('Description')"
+                                :rules="validate('route.description')"
+                                clearable
+                                type="textarea"
+                                v-model="dms.description"
+                            />
+                        </div>
+                    </VnRow>
+
+                    <VnRow class="row q-gutter-md q-mb-md">
+                        <div class="col">
+                            <QFile
+                                ref="inputFileRef"
+                                class="required"
+                                :label="t('File')"
+                                v-model="dms.files"
+                                multiple
+                                :accept="allowedContentTypes.join(',')"
+                                clearable
+                                clear-icon="close"
+                            >
+                                <template #append>
+                                    <QBtn
+                                        icon="vn:attach"
+                                        flat
+                                        round
+                                        padding="xs"
+                                        @click="inputFileRef.pickFiles()"
+                                    >
+                                        <QTooltip>
+                                            {{ t('Select a file') }}
+                                        </QTooltip>
+                                    </QBtn>
+                                    <QBtn icon="info" flat round padding="xs">
+                                        <QTooltip max-width="30rem">
+                                            {{
+                                                `${t(
+                                                    'Allowed content types'
+                                                )}: ${allowedContentTypes.join(', ')}`
+                                            }}
+                                        </QTooltip>
+                                    </QBtn>
+                                </template>
+                            </QFile>
+                        </div>
+                    </VnRow>
+
+                    <QCheckbox
+                        :label="t('Generate identifier for original file')"
+                        v-model="dms.hasFile"
+                        disable
+                    />
+                </QForm>
+            </QCardSection>
+        </QCard>
+    </div>
+</template>
+
+<i18n>
+en:
+    clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}'
+es:
+    Reference: Referencia
+    Company: Empresa
+    Warehouse: Almacén
+    Type: Tipo
+    Description: Descripción
+    clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}'
+    File: Fichero
+    Select a file: Selecciona un fichero
+    Allowed content types: Tipos de archivo permitidos
+    Generate identifier for original file: Generar identificador para archivo original
+</i18n>
diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js
index 1c722afe8..a3b2d52dc 100644
--- a/src/router/modules/worker.js
+++ b/src/router/modules/worker.js
@@ -12,7 +12,7 @@ export default {
     redirect: { name: 'WorkerMain' },
     menus: {
         main: ['WorkerList', 'WorkerDepartment'],
-        card: ['WorkerNotificationsManager', 'WorkerPda'],
+        card: ['WorkerNotificationsManager', 'WorkerPda', 'WorkerDms'],
         departmentCard: ['BasicData'],
     },
     children: [
@@ -85,6 +85,35 @@ export default {
                     },
                     component: () => import('src/pages/Worker/Card/WorkerPda.vue'),
                 },
+                {
+                    path: 'dms',
+                    name: 'DmsCard',
+                    redirect: { name: 'WorkerDms' },
+                    children: [
+                        {
+                            path: '',
+                            name: 'WorkerDms',
+                            meta: {
+                                title: 'dms',
+                                icon: 'cloud_upload',
+                            },
+                            component: () =>
+                                import('src/pages/Worker/Card/WorkerDms.vue'),
+                        },
+                        {
+                            path: 'create',
+                            name: 'WorkerDmsCreate',
+                            component: () =>
+                                import('src/pages/Worker/components/WorkerDmsCreate.vue'),
+                        },
+                        {
+                            path: ':dmsId/edit',
+                            name: 'WorkerDmsEdit',
+                            component: () =>
+                                import('src/pages/Worker/components/WorkerDmsEdit.vue'),
+                        },
+                    ],
+                },
             ],
         },
     ],

From 627d3bbe85a38232de80a5d92bb67aee98a38291 Mon Sep 17 00:00:00 2001
From: carlosfonseca <carlos.fonseca@mindshore.io>
Date: Mon, 25 Mar 2024 16:00:47 -0500
Subject: [PATCH 03/15] Se implementan las correcciones a los comentarios

---
 src/pages/Worker/Card/WorkerDms.vue           |  36 ++-
 .../Worker/components/WorkerDmsActions.vue    |  16 +-
 .../Worker/components/WorkerDmsCreate.vue     | 277 ++----------------
 src/pages/Worker/components/WorkerDmsEdit.vue | 262 +++--------------
 src/router/modules/worker.js                  |  32 +-
 5 files changed, 103 insertions(+), 520 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index 144e3edee..f794f6b17 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -1,22 +1,27 @@
 <script setup>
 import { computed, ref, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
+
+import { useQuasar } from 'quasar';
 
 import { downloadFile } from 'src/composables/downloadFile';
 import { toDateHour } from 'src/filters';
 
 import FetchData from 'components/FetchData.vue';
+import WorkerDmsCreate from '../components/WorkerDmsCreate.vue';
 import WorkerDmsActions from '../components/WorkerDmsActions.vue';
 
 const { t } = useI18n();
+const quasar = useQuasar();
 const route = useRoute();
-const router = useRouter();
 
-const workerDmsRef = ref(null);
+const findOne = ref([]);
 const rows = ref([]);
+const workerDmsRef = ref(null);
 
 const filter = { where: { worker: route.params.id }, order: ['dmsFk DESC'], limit: 20 };
+const filterFindOne = { where: { code: 'hhrrData' } };
 
 const columns = computed(() => [
     {
@@ -82,8 +87,15 @@ const setData = () => {
     workerDmsRef.value.fetch();
 };
 
-const toWorkerDmsCreate = () => {
-    router.push({ name: 'WorkerDmsCreate' });
+const showWorkerDmsCreate = () => {
+    quasar.dialog({
+        component: WorkerDmsCreate,
+        componentProps: {
+            id: Number(route.params.id),
+            defaultDmsCode: findOne.value.code,
+            promise: setData,
+        },
+    });
 };
 </script>
 
@@ -95,6 +107,12 @@ const toWorkerDmsCreate = () => {
         auto-load
         :url="`WorkerDms/${route.params.id}/filter`"
     />
+    <fetch-data
+        :filter="filterFindOne"
+        @on-fetch="(data) => (findOne = data)"
+        auto-load
+        url="DmsTypes/findOne"
+    />
 
     <QPage class="column items-center q-pa-md">
         <QTable
@@ -107,7 +125,9 @@ const toWorkerDmsCreate = () => {
         >
             <template #body-cell-order="{ row }">
                 <QTd>
-                    <QChip class="chip-color" square>{{ row.hardCopyNumber }}</QChip>
+                    <QChip class="chip-color" square v-if="row.hardCopyNumber">{{
+                        row.hardCopyNumber
+                    }}</QChip>
                 </QTd>
             </template>
 
@@ -138,7 +158,7 @@ const toWorkerDmsCreate = () => {
     </QPage>
 
     <QPageSticky :offset="[18, 18]">
-        <QBtn @click.stop="toWorkerDmsCreate()" color="primary" fab icon="add" />
+        <QBtn @click.stop="showWorkerDmsCreate" color="primary" fab icon="add" />
         <QTooltip>
             {{ t('Upload file') }}
         </QTooltip>
@@ -147,7 +167,7 @@ const toWorkerDmsCreate = () => {
 
 <style scoped>
 .chip-color {
-    background-color: var(--vn-label);
+    background-color: var(--vn-label-color);
 }
 </style>
 
diff --git a/src/pages/Worker/components/WorkerDmsActions.vue b/src/pages/Worker/components/WorkerDmsActions.vue
index 9a8e4816e..2a6ab1ce3 100644
--- a/src/pages/Worker/components/WorkerDmsActions.vue
+++ b/src/pages/Worker/components/WorkerDmsActions.vue
@@ -1,17 +1,15 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { useRoute, useRouter } from 'vue-router';
 
 import { useQuasar } from 'quasar';
 
 import { downloadFile } from 'src/composables/downloadFile';
 
 import WorkerDmsDelete from 'src/pages/Worker/components/WorkerDmsDelete.vue';
+import WorkerDmsEdit from './WorkerDmsEdit.vue';
 
 const { t } = useI18n();
 const quasar = useQuasar();
-const route = useRoute();
-const router = useRouter();
 
 const $props = defineProps({
     id: {
@@ -26,12 +24,12 @@ const $props = defineProps({
 
 const setDownloadFile = () => downloadFile($props.id);
 
-const toWorkerDmsEdit = () => {
-    router.push({
-        name: 'WorkerDmsEdit',
-        params: {
-            id: route.params.id,
+const showWorkerDmsEdit = () => {
+    quasar.dialog({
+        component: WorkerDmsEdit,
+        componentProps: {
             dmsId: $props.id,
+            promise: setData,
         },
     });
 };
@@ -64,7 +62,7 @@ const setData = () => {
             </QTooltip>
         </QIcon>
         <QIcon
-            @click.stop="toWorkerDmsEdit"
+            @click.stop="showWorkerDmsEdit"
             class="q-ml-md"
             color="primary"
             name="edit"
diff --git a/src/pages/Worker/components/WorkerDmsCreate.vue b/src/pages/Worker/components/WorkerDmsCreate.vue
index d38e044ca..f9afdddeb 100644
--- a/src/pages/Worker/components/WorkerDmsCreate.vue
+++ b/src/pages/Worker/components/WorkerDmsCreate.vue
@@ -1,262 +1,37 @@
 <script setup>
-import { onBeforeMount, ref, watch } from 'vue';
-import { useI18n } from 'vue-i18n';
-import { useRoute, useRouter } from 'vue-router';
+import { useDialogPluginComponent } from 'quasar';
 
-import axios from 'axios';
+import VnDms from 'src/components/common/VnDms.vue';
 
-import { useState } from 'src/composables/useState';
-import { useValidator } from 'src/composables/useValidator';
-import useNotify from 'src/composables/useNotify';
+const { dialogRef } = useDialogPluginComponent();
 
-import FetchData from 'components/FetchData.vue';
-import VnRow from 'components/ui/VnRow.vue';
-import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-
-const { notify } = useNotify();
-const { t } = useI18n();
-const { validate } = useValidator();
-const route = useRoute();
-const router = useRouter();
-const state = useState();
-const user = state.getUser();
-
-const filterFindOne = { where: { code: 'paymentsLaw' } };
-const filterCompanies = { order: ['code'] };
-const filterWarehouses = { order: ['name'] };
-
-const inputFileRef = ref();
-const client = ref({});
-const findOne = ref([]);
-const allowedContentTypes = ref([]);
-const optionsCompanies = ref([]);
-const optionsWarehouses = ref([]);
-const optionsDmsTypes = ref([]);
-const isLoading = ref(false);
-const dms = ref({
-    hasFile: false,
+const $props = defineProps({
+    id: {
+        type: Number,
+        required: true,
+    },
+    defaultDmsCode: {
+        type: String,
+        required: true,
+    },
+    promise: {
+        type: Function,
+        required: true,
+    },
 });
 
-onBeforeMount(() => {
-    const { companyFk, warehouseFk } = user.value;
-    dms.value.reference = route.params.id;
-    dms.value.companyId = companyFk;
-    dms.value.warehouseId = warehouseFk;
-});
-
-watch([client, findOne], ([newClient, newFindOne]) => {
-    dms.value.description = t('clientFileDescription', {
-        dmsTypeName: newFindOne.name?.toUpperCase(),
-        clientName: newClient.name?.toUpperCase(),
-        clientId: newClient.id,
-    });
-    dms.value.dmsTypeId = newFindOne.id;
-});
-
-const saveData = async () => {
-    try {
-        const formData = new FormData();
-        const files = dms.value.files;
-
-        if (files && files.length > 0) {
-            for (let file of files) {
-                formData.append(file.name, file);
-            }
-            dms.value.hasFileAttached = true;
-
-            const url = `Workers/${route.params.id}/uploadFile`;
-            await axios.post(url, formData, {
-                params: dms.value,
-            });
-            notify('globals.dataSaved', 'positive');
-            toWorkerDms();
-        }
-    } catch (error) {
-        notify(error.message, 'negative');
-    }
-};
-
-const toWorkerDms = () => {
-    router.push({ name: 'WorkerDms' });
+const onDataSaved = async () => {
+    if ($props.promise) await $props.promise();
 };
 </script>
 
 <template>
-    <fetch-data
-        @on-fetch="(data) => (client = data)"
-        auto-load
-        :url="`Clients/${route.params.id}/getCard`"
-    />
-    <fetch-data
-        :filter="filterFindOne"
-        @on-fetch="(data) => (findOne = data)"
-        auto-load
-        url="DmsTypes/findOne"
-    />
-    <fetch-data
-        @on-fetch="(data) => (allowedContentTypes = data)"
-        auto-load
-        url="DmsContainers/allowedContentTypes"
-    />
-    <fetch-data
-        :filter="filterCompanies"
-        @on-fetch="(data) => (optionsCompanies = data)"
-        auto-load
-        url="Companies"
-    />
-    <fetch-data
-        :filter="filterWarehouses"
-        @on-fetch="(data) => (optionsWarehouses = data)"
-        auto-load
-        url="Warehouses"
-    />
-    <fetch-data
-        :filter="filterWarehouses"
-        @on-fetch="(data) => (optionsDmsTypes = data)"
-        auto-load
-        url="DmsTypes"
-    />
-
-    <Teleport to="#st-actions">
-        <QBtnGroup push class="q-gutter-x-sm">
-            <QBtn
-                :disabled="isLoading"
-                :label="t('globals.cancel')"
-                :loading="isLoading"
-                @click="toWorkerDms"
-                color="primary"
-                flat
-                icon="close"
-            />
-            <QBtn
-                :disabled="isLoading"
-                :label="t('globals.save')"
-                :loading="isLoading"
-                @click.stop="saveData"
-                color="primary"
-                icon="save"
-            />
-        </QBtnGroup>
-    </Teleport>
-
-    <div class="full-width flex justify-center">
-        <QCard class="card-width q-pa-lg">
-            <QCardSection>
-                <QForm>
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <VnInput
-                                :label="t('Reference')"
-                                clearable
-                                v-model="dms.reference"
-                            />
-                        </div>
-                        <div class="col">
-                            <VnSelectFilter
-                                :label="t('Company')"
-                                :options="optionsCompanies"
-                                :rules="validate('entry.companyFk')"
-                                option-label="code"
-                                option-value="id"
-                                v-model="dms.companyId"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <VnSelectFilter
-                                :label="t('Warehouse')"
-                                :options="optionsWarehouses"
-                                option-label="name"
-                                option-value="id"
-                                v-model="dms.warehouseId"
-                            />
-                        </div>
-                        <div class="col">
-                            <VnSelectFilter
-                                :label="t('Type')"
-                                :options="optionsDmsTypes"
-                                option-label="name"
-                                option-value="id"
-                                v-model="dms.dmsTypeId"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <VnInput
-                                :label="t('Description')"
-                                :rules="validate('route.description')"
-                                clearable
-                                type="textarea"
-                                v-model="dms.description"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <QFile
-                                ref="inputFileRef"
-                                class="required"
-                                :label="t('File')"
-                                v-model="dms.files"
-                                multiple
-                                :accept="allowedContentTypes.join(',')"
-                                clearable
-                                clear-icon="close"
-                            >
-                                <template #append>
-                                    <QBtn
-                                        icon="vn:attach"
-                                        flat
-                                        round
-                                        padding="xs"
-                                        @click="inputFileRef.pickFiles()"
-                                    >
-                                        <QTooltip>
-                                            {{ t('Select a file') }}
-                                        </QTooltip>
-                                    </QBtn>
-                                    <QBtn icon="info" flat round padding="xs">
-                                        <QTooltip max-width="30rem">
-                                            {{
-                                                `${t(
-                                                    'Allowed content types'
-                                                )}: ${allowedContentTypes.join(', ')}`
-                                            }}
-                                        </QTooltip>
-                                    </QBtn>
-                                </template>
-                            </QFile>
-                        </div>
-                    </VnRow>
-
-                    <QCheckbox
-                        :label="t('Generate identifier for original file')"
-                        v-model="dms.hasFile"
-                    />
-                </QForm>
-            </QCardSection>
-        </QCard>
-    </div>
+    <QDialog ref="dialogRef">
+        <VnDms
+            :default-dms-code="$props.defaultDmsCode"
+            :url="`Workers/${$props.id}/uploadFile`"
+            @on-data-saved="onDataSaved"
+            model="WorkerDmsCreate"
+        />
+    </QDialog>
 </template>
-
-<i18n>
-en:
-    clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}'
-es:
-    Reference: Referencia
-    Company: Empresa
-    Warehouse: Almacén
-    Type: Tipo
-    Description: Descripción
-    clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}'
-    File: Fichero
-    Select a file: Selecciona un fichero
-    Allowed content types: Tipos de archivo permitidos
-    Generate identifier for original file: Generar identificador para archivo original
-</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsEdit.vue b/src/pages/Worker/components/WorkerDmsEdit.vue
index 8e84c6326..7656fe09f 100644
--- a/src/pages/Worker/components/WorkerDmsEdit.vue
+++ b/src/pages/Worker/components/WorkerDmsEdit.vue
@@ -1,246 +1,56 @@
 <script setup>
 import { ref } from 'vue';
-import { useI18n } from 'vue-i18n';
-import { useRoute, useRouter } from 'vue-router';
 
-import axios from 'axios';
-
-import { useValidator } from 'src/composables/useValidator';
-import useNotify from 'src/composables/useNotify';
+import { useDialogPluginComponent } from 'quasar';
 
 import FetchData from 'components/FetchData.vue';
-import VnRow from 'components/ui/VnRow.vue';
-import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
-import VnInput from 'src/components/common/VnInput.vue';
+import VnDms from 'src/components/common/VnDms.vue';
 
-const { notify } = useNotify();
-const { t } = useI18n();
-const { validate } = useValidator();
-const route = useRoute();
-const router = useRouter();
+const { dialogRef } = useDialogPluginComponent();
 
-const filterCompanies = { order: ['code'] };
-const filterWarehouses = { order: ['name'] };
+const $props = defineProps({
+    dmsId: {
+        type: Number,
+        required: true,
+    },
+    defaultDmsCode: {
+        type: String,
+        required: false,
+    },
+    promise: {
+        type: Function,
+        required: true,
+    },
+});
 
-const inputFileRef = ref();
-const allowedContentTypes = ref([]);
-const optionsCompanies = ref([]);
-const optionsWarehouses = ref([]);
-const optionsDmsTypes = ref([]);
-const isLoading = ref(false);
-const dms = ref({
+const initialData = ref({
     hasFile: true,
+    id: $props.dmsId,
 });
 
 const setCurrentDms = (data) => {
-    dms.value.reference = data.reference;
-    dms.value.companyId = data.companyFk;
-    dms.value.warehouseId = data.warehouseFk;
-    dms.value.dmsTypeId = data.dmsTypeFk;
-    dms.value.description = data.description;
-    dms.value.hasFile = data.hasFile;
+    initialData.value.reference = data.reference;
+    initialData.value.companyFk = data.companyFk;
+    initialData.value.warehouseFk = data.warehouseFk;
+    initialData.value.dmsTypeFk = data.dmsTypeFk;
+    initialData.value.description = data.description;
+    initialData.value.hasFile = data.hasFile;
 };
 
-const saveData = async () => {
-    try {
-        const formData = new FormData();
-        console.log(formData);
-        const files = dms.value.files;
-        console.log(files);
-
-        dms.value.hasFileAttached = files ? true : false;
-        const url = `dms/${route.params.dmsId}/updateFile`;
-
-        if (files && files.length > 0) {
-            for (let file of files) {
-                formData.append(file.name, file);
-            }
-
-            await axios.post(url, formData, {
-                params: dms.value,
-            });
-            notify('globals.dataSaved', 'positive');
-        } else {
-            await axios.post(url, dms.value);
-        }
-
-        toWorkerDms();
-    } catch (error) {
-        notify(error.message, 'negative');
-    }
-};
-
-const toWorkerDms = () => {
-    router.push({ name: 'WorkerDms' });
+const onDataSaved = async () => {
+    if ($props.promise) await $props.promise();
 };
 </script>
 
 <template>
-    <fetch-data :url="`Dms/${route.params.dmsId}`" @on-fetch="setCurrentDms" auto-load />
-    <fetch-data
-        @on-fetch="(data) => (allowedContentTypes = data)"
-        auto-load
-        url="DmsContainers/allowedContentTypes"
-    />
-    <fetch-data
-        :filter="filterCompanies"
-        @on-fetch="(data) => (optionsCompanies = data)"
-        auto-load
-        url="Companies"
-    />
-    <fetch-data
-        :filter="filterWarehouses"
-        @on-fetch="(data) => (optionsWarehouses = data)"
-        auto-load
-        url="Warehouses"
-    />
-    <fetch-data
-        :filter="filterWarehouses"
-        @on-fetch="(data) => (optionsDmsTypes = data)"
-        auto-load
-        url="DmsTypes"
-    />
+    <fetch-data :url="`Dms/${$props.dmsId}`" @on-fetch="setCurrentDms" auto-load />
 
-    <Teleport to="#st-actions">
-        <QBtnGroup push class="q-gutter-x-sm">
-            <QBtn
-                :disabled="isLoading"
-                :label="t('globals.cancel')"
-                :loading="isLoading"
-                @click="toWorkerDms"
-                color="primary"
-                flat
-                icon="close"
-            />
-            <QBtn
-                :disabled="isLoading"
-                :label="t('globals.save')"
-                :loading="isLoading"
-                @click.stop="saveData"
-                color="primary"
-                icon="save"
-            />
-        </QBtnGroup>
-    </Teleport>
-
-    <div class="full-width flex justify-center">
-        <QCard class="card-width q-pa-lg">
-            <QCardSection>
-                <QForm>
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <VnInput
-                                :label="t('Reference')"
-                                clearable
-                                v-model="dms.reference"
-                            />
-                        </div>
-                        <div class="col">
-                            <VnSelectFilter
-                                :label="t('Company')"
-                                :options="optionsCompanies"
-                                :rules="validate('entry.companyFk')"
-                                option-label="code"
-                                option-value="id"
-                                v-model="dms.companyId"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <VnSelectFilter
-                                :label="t('Warehouse')"
-                                :options="optionsWarehouses"
-                                option-label="name"
-                                option-value="id"
-                                v-model="dms.warehouseId"
-                            />
-                        </div>
-                        <div class="col">
-                            <VnSelectFilter
-                                :label="t('Type')"
-                                :options="optionsDmsTypes"
-                                option-label="name"
-                                option-value="id"
-                                v-model="dms.dmsTypeId"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <VnInput
-                                :label="t('Description')"
-                                :rules="validate('route.description')"
-                                clearable
-                                type="textarea"
-                                v-model="dms.description"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <VnRow class="row q-gutter-md q-mb-md">
-                        <div class="col">
-                            <QFile
-                                ref="inputFileRef"
-                                class="required"
-                                :label="t('File')"
-                                v-model="dms.files"
-                                multiple
-                                :accept="allowedContentTypes.join(',')"
-                                clearable
-                                clear-icon="close"
-                            >
-                                <template #append>
-                                    <QBtn
-                                        icon="vn:attach"
-                                        flat
-                                        round
-                                        padding="xs"
-                                        @click="inputFileRef.pickFiles()"
-                                    >
-                                        <QTooltip>
-                                            {{ t('Select a file') }}
-                                        </QTooltip>
-                                    </QBtn>
-                                    <QBtn icon="info" flat round padding="xs">
-                                        <QTooltip max-width="30rem">
-                                            {{
-                                                `${t(
-                                                    'Allowed content types'
-                                                )}: ${allowedContentTypes.join(', ')}`
-                                            }}
-                                        </QTooltip>
-                                    </QBtn>
-                                </template>
-                            </QFile>
-                        </div>
-                    </VnRow>
-
-                    <QCheckbox
-                        :label="t('Generate identifier for original file')"
-                        v-model="dms.hasFile"
-                        disable
-                    />
-                </QForm>
-            </QCardSection>
-        </QCard>
-    </div>
+    <QDialog ref="dialogRef">
+        <VnDms
+            :default-initialData-code="$props.defaultDmsCode"
+            @on-data-saved="onDataSaved"
+            model="WorkerDmsEdit"
+            :form-initial-data="initialData"
+        />
+    </QDialog>
 </template>
-
-<i18n>
-en:
-    clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}'
-es:
-    Reference: Referencia
-    Company: Empresa
-    Warehouse: Almacén
-    Type: Tipo
-    Description: Descripción
-    clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}'
-    File: Fichero
-    Select a file: Selecciona un fichero
-    Allowed content types: Tipos de archivo permitidos
-    Generate identifier for original file: Generar identificador para archivo original
-</i18n>
diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js
index a3b2d52dc..c0fe6e1fa 100644
--- a/src/router/modules/worker.js
+++ b/src/router/modules/worker.js
@@ -86,33 +86,13 @@ export default {
                     component: () => import('src/pages/Worker/Card/WorkerPda.vue'),
                 },
                 {
+                    name: 'WorkerDms',
                     path: 'dms',
-                    name: 'DmsCard',
-                    redirect: { name: 'WorkerDms' },
-                    children: [
-                        {
-                            path: '',
-                            name: 'WorkerDms',
-                            meta: {
-                                title: 'dms',
-                                icon: 'cloud_upload',
-                            },
-                            component: () =>
-                                import('src/pages/Worker/Card/WorkerDms.vue'),
-                        },
-                        {
-                            path: 'create',
-                            name: 'WorkerDmsCreate',
-                            component: () =>
-                                import('src/pages/Worker/components/WorkerDmsCreate.vue'),
-                        },
-                        {
-                            path: ':dmsId/edit',
-                            name: 'WorkerDmsEdit',
-                            component: () =>
-                                import('src/pages/Worker/components/WorkerDmsEdit.vue'),
-                        },
-                    ],
+                    meta: {
+                        title: 'dms',
+                        icon: 'cloud_upload',
+                    },
+                    component: () => import('src/pages/Worker/Card/WorkerDms.vue'),
                 },
             ],
         },

From 0d6d18b8c8ddd9046f84a5ad71d54ccba0b7a379 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 27 Mar 2024 20:33:35 -0300
Subject: [PATCH 04/15] Apply VnDmsList component and add docuware action

---
 src/components/common/VnDmsList.vue           |  33 +++-
 src/composables/downloadFile.js               |   9 +-
 src/composables/getUrl.js                     |  24 +++
 src/pages/Worker/Card/WorkerDms.vue           | 185 +-----------------
 .../Worker/components/WorkerDmsActions.vue    |  94 ---------
 .../Worker/components/WorkerDmsCreate.vue     |  37 ----
 .../Worker/components/WorkerDmsDelete.vue     |  82 --------
 src/pages/Worker/components/WorkerDmsEdit.vue |  56 ------
 8 files changed, 67 insertions(+), 453 deletions(-)
 delete mode 100644 src/pages/Worker/components/WorkerDmsActions.vue
 delete mode 100644 src/pages/Worker/components/WorkerDmsCreate.vue
 delete mode 100644 src/pages/Worker/components/WorkerDmsDelete.vue
 delete mode 100644 src/pages/Worker/components/WorkerDmsEdit.vue

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index 23e00f5d9..9bf90230d 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -9,6 +9,7 @@ import FetchData from 'components/FetchData.vue';
 import VnDms from 'src/components/common/VnDms.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
 import { downloadFile } from 'src/composables/downloadFile';
+import { getUrlFindOne } from 'composables/getUrl';
 
 const route = useRoute();
 const quasar = useQuasar();
@@ -142,15 +143,17 @@ const columns = computed(() => [
         components: [
             {
                 component: QBtn,
+                name: 'download',
                 props: () => ({
                     icon: 'cloud_download',
                     flat: true,
                     color: 'primary',
                 }),
-                click: (prop) => downloadFile(prop.row.id),
+                click: (prop) => downloadFile(prop.row.id, prop.row.isDocuware),
             },
             {
                 component: QBtn,
+                name: 'edit',
                 props: () => ({
                     icon: 'edit',
                     flat: true,
@@ -160,6 +163,7 @@ const columns = computed(() => [
             },
             {
                 component: QBtn,
+                name: 'delete',
                 props: () => ({
                     icon: 'delete',
                     flat: true,
@@ -167,6 +171,16 @@ const columns = computed(() => [
                 }),
                 click: (prop) => deleteDms(prop.row.id),
             },
+            {
+                component: QBtn,
+                name: 'openDocuware',
+                props: () => ({
+                    icon: 'open_in_new',
+                    flat: true,
+                    color: 'primary',
+                }),
+                click: () => openDocuware(),
+            },
         ],
     },
 ]);
@@ -206,6 +220,16 @@ function parseDms(data) {
     }
     return data;
 }
+
+async function openDocuware() {
+    const url = await getUrlFindOne('WebClient', 'docuware');
+    if (url) window.open(url).focus();
+}
+
+function shouldRenderButton(buttonName, isDocuware = false) {
+    // Renderizar el botón si no se llama 'openDocuware' o (se llama 'openDocuware' y props.row.isDocuware = true)
+    return buttonName !== 'openDocuware' || (buttonName === 'openDocuware' && isDocuware);
+}
 </script>
 <template>
     <FetchData
@@ -243,6 +267,7 @@ function parseDms(data) {
                 <div class="flex justify-center" v-if="props.col.name == 'options'">
                     <div v-for="button of props.col.components" :key="button.id">
                         <component
+                            v-if="shouldRenderButton(button.name, props.row.isDocuware)"
                             :is="button.component"
                             v-bind="button.props(props)"
                             @click="button.click(props)"
@@ -272,6 +297,12 @@ function parseDms(data) {
                                     class="row"
                                 >
                                     <component
+                                        v-if="
+                                            shouldRenderButton(
+                                                button.name,
+                                                props.row.isDocuware
+                                            )
+                                        "
                                         :is="button.component"
                                         v-bind="button.props(col)"
                                         @click="button.click(col)"
diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js
index b26dec731..45162f81b 100644
--- a/src/composables/downloadFile.js
+++ b/src/composables/downloadFile.js
@@ -1,11 +1,12 @@
 import { useSession } from 'src/composables/useSession';
 import { getUrl } from './getUrl';
 
-const {getTokenMultimedia} = useSession();
-const token =  getTokenMultimedia();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 
-export async function downloadFile(dmsId) {
+export async function downloadFile(dmsId, isDocuware = false) {
     let appUrl = await getUrl('', 'lilium');
     appUrl = appUrl.replace('/#/', '');
-    window.open(`${appUrl}/api/dms/${dmsId}/downloadFile?access_token=${token}`);
+    const urlPath = isDocuware ? '/docuwareDownload' : '/downloadFile';
+    window.open(`${appUrl}/api/dms/${dmsId}${urlPath}?access_token=${token}`);
 }
diff --git a/src/composables/getUrl.js b/src/composables/getUrl.js
index 1e6aec4c6..7b7103562 100644
--- a/src/composables/getUrl.js
+++ b/src/composables/getUrl.js
@@ -1,4 +1,7 @@
 import axios from 'axios';
+import useNotify from 'src/composables/useNotify.js';
+
+const { notify } = useNotify();
 
 export async function getUrl(route, app = 'salix') {
     let url;
@@ -8,3 +11,24 @@ export async function getUrl(route, app = 'salix') {
     });
     return url;
 }
+
+export async function getUrlFindOne(route, app = 'salix') {
+    try {
+        const env = process.env.NODE_ENV;
+
+        const filter = {
+            where: { and: [{ appName: app }, { environment: env }] },
+        };
+
+        const { data } = await axios.get('Urls/findOne', {
+            params: { filter: JSON.stringify(filter) },
+        });
+
+        if (!data) return null;
+
+        return data.url + route;
+    } catch (err) {
+        notify('Direction not found', 'negative');
+        console.error('Error finding url: ', err);
+    }
+}
diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index f794f6b17..d0c5cf5e5 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -1,184 +1,11 @@
 <script setup>
-import { computed, ref, watch } from 'vue';
-import { useI18n } from 'vue-i18n';
-import { useRoute } from 'vue-router';
-
-import { useQuasar } from 'quasar';
-
-import { downloadFile } from 'src/composables/downloadFile';
-import { toDateHour } from 'src/filters';
-
-import FetchData from 'components/FetchData.vue';
-import WorkerDmsCreate from '../components/WorkerDmsCreate.vue';
-import WorkerDmsActions from '../components/WorkerDmsActions.vue';
-
-const { t } = useI18n();
-const quasar = useQuasar();
-const route = useRoute();
-
-const findOne = ref([]);
-const rows = ref([]);
-const workerDmsRef = ref(null);
-
-const filter = { where: { worker: route.params.id }, order: ['dmsFk DESC'], limit: 20 };
-const filterFindOne = { where: { code: 'hhrrData' } };
-
-const columns = computed(() => [
-    {
-        align: 'left',
-        field: 'dmsFk',
-        label: t('Id'),
-        name: 'id',
-    },
-    {
-        align: 'left',
-        field: 'hardCopyNumber',
-        label: t('Order'),
-        name: 'order',
-    },
-    {
-        align: 'left',
-        field: 'reference',
-        label: t('Reference'),
-        name: 'reference',
-    },
-    {
-        align: 'left',
-        field: 'description',
-        label: t('Description'),
-        name: 'description',
-    },
-    {
-        align: 'left',
-        field: 'hasFile',
-        label: t('Original'),
-        name: 'original',
-    },
-    {
-        align: 'left',
-        field: 'file',
-        label: t('File'),
-        name: 'file',
-    },
-    {
-        align: 'left',
-        field: 'created',
-        label: t('Created'),
-        name: 'created',
-        format: (value) => toDateHour(value),
-    },
-    {
-        align: 'right',
-        field: 'actions',
-        label: '',
-        name: 'actions',
-    },
-]);
-
-watch(
-    () => route.params.id,
-    (newValue) => {
-        filter.where.worker = newValue;
-        setData();
-    }
-);
-
-const setData = () => {
-    workerDmsRef.value.fetch();
-};
-
-const showWorkerDmsCreate = () => {
-    quasar.dialog({
-        component: WorkerDmsCreate,
-        componentProps: {
-            id: Number(route.params.id),
-            defaultDmsCode: findOne.value.code,
-            promise: setData,
-        },
-    });
-};
+import VnDmsList from 'src/components/common/VnDmsList.vue';
 </script>
-
 <template>
-    <FetchData
-        ref="workerDmsRef"
-        :filter="filter"
-        @on-fetch="(data) => (rows = data)"
-        auto-load
-        :url="`WorkerDms/${route.params.id}/filter`"
+    <VnDmsList
+        model="WorkerDms"
+        update-model="Workers"
+        default-dms-code="hhrrData"
+        filter="workerFk"
     />
-    <fetch-data
-        :filter="filterFindOne"
-        @on-fetch="(data) => (findOne = data)"
-        auto-load
-        url="DmsTypes/findOne"
-    />
-
-    <QPage class="column items-center q-pa-md">
-        <QTable
-            :columns="columns"
-            :pagination="{ rowsPerPage: 12 }"
-            :rows="rows"
-            class="full-width q-mt-md"
-            row-key="id"
-            v-if="rows?.length"
-        >
-            <template #body-cell-order="{ row }">
-                <QTd>
-                    <QChip class="chip-color" square v-if="row.hardCopyNumber">{{
-                        row.hardCopyNumber
-                    }}</QChip>
-                </QTd>
-            </template>
-
-            <template #body-cell-original="{ row }">
-                <QTd>
-                    <QCheckbox :model-value="row.hasFile === 1" disable />
-                </QTd>
-            </template>
-
-            <template #body-cell-file="{ row }">
-                <QTd>
-                    <QBtn @click.stop="downloadFile(row.dmsFk)" color="blue" flat no-caps>
-                        {{ row.file }}
-                    </QBtn>
-                </QTd>
-            </template>
-
-            <template #body-cell-actions="{ row }">
-                <QTd>
-                    <WorkerDmsActions :id="row.dmsFk" :promise="setData" />
-                </QTd>
-            </template>
-        </QTable>
-
-        <h5 class="flex justify-center color-vn-label" v-else>
-            {{ t('globals.noResults') }}
-        </h5>
-    </QPage>
-
-    <QPageSticky :offset="[18, 18]">
-        <QBtn @click.stop="showWorkerDmsCreate" color="primary" fab icon="add" />
-        <QTooltip>
-            {{ t('Upload file') }}
-        </QTooltip>
-    </QPageSticky>
 </template>
-
-<style scoped>
-.chip-color {
-    background-color: var(--vn-label-color);
-}
-</style>
-
-<i18n>
-es:
-    Id: Id
-    Order: Orden
-    Reference: Referencia
-    Description: Descripción
-    File: Archivo
-    Original: Original
-    Created: Fecha creación
-    Upload file: Subir fichero
-</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsActions.vue b/src/pages/Worker/components/WorkerDmsActions.vue
deleted file mode 100644
index 2a6ab1ce3..000000000
--- a/src/pages/Worker/components/WorkerDmsActions.vue
+++ /dev/null
@@ -1,94 +0,0 @@
-<script setup>
-import { useI18n } from 'vue-i18n';
-
-import { useQuasar } from 'quasar';
-
-import { downloadFile } from 'src/composables/downloadFile';
-
-import WorkerDmsDelete from 'src/pages/Worker/components/WorkerDmsDelete.vue';
-import WorkerDmsEdit from './WorkerDmsEdit.vue';
-
-const { t } = useI18n();
-const quasar = useQuasar();
-
-const $props = defineProps({
-    id: {
-        type: Number,
-        required: true,
-    },
-    promise: {
-        type: Function,
-        required: true,
-    },
-});
-
-const setDownloadFile = () => downloadFile($props.id);
-
-const showWorkerDmsEdit = () => {
-    quasar.dialog({
-        component: WorkerDmsEdit,
-        componentProps: {
-            dmsId: $props.id,
-            promise: setData,
-        },
-    });
-};
-
-const showWorkerDmsDeleteDialog = () => {
-    quasar.dialog({
-        component: WorkerDmsDelete,
-        componentProps: {
-            id: $props.id,
-            promise: setData,
-        },
-    });
-};
-
-const setData = () => {
-    $props.promise();
-};
-</script>
-
-<template>
-    <div>
-        <QIcon
-            @click.stop="setDownloadFile"
-            color="primary"
-            name="cloud_download"
-            size="sm"
-        >
-            <QTooltip>
-                {{ t('actionFile', { action: t('globals.download') }) }}
-            </QTooltip>
-        </QIcon>
-        <QIcon
-            @click.stop="showWorkerDmsEdit"
-            class="q-ml-md"
-            color="primary"
-            name="edit"
-            size="sm"
-        >
-            <QTooltip>
-                {{ t('actionFile', { action: t('globals.edit') }) }}
-            </QTooltip>
-        </QIcon>
-        <QIcon
-            @click.stop="showWorkerDmsDeleteDialog"
-            class="q-ml-md"
-            color="primary"
-            name="delete"
-            size="sm"
-        >
-            <QTooltip>
-                {{ t('actionFile', { action: t('globals.remove') }) }}
-            </QTooltip>
-        </QIcon>
-    </div>
-</template>
-
-<i18n>
-en:
-    actionFile: '{action} file'
-es:
-    actionFile: '{action} fichero'
-</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsCreate.vue b/src/pages/Worker/components/WorkerDmsCreate.vue
deleted file mode 100644
index f9afdddeb..000000000
--- a/src/pages/Worker/components/WorkerDmsCreate.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-<script setup>
-import { useDialogPluginComponent } from 'quasar';
-
-import VnDms from 'src/components/common/VnDms.vue';
-
-const { dialogRef } = useDialogPluginComponent();
-
-const $props = defineProps({
-    id: {
-        type: Number,
-        required: true,
-    },
-    defaultDmsCode: {
-        type: String,
-        required: true,
-    },
-    promise: {
-        type: Function,
-        required: true,
-    },
-});
-
-const onDataSaved = async () => {
-    if ($props.promise) await $props.promise();
-};
-</script>
-
-<template>
-    <QDialog ref="dialogRef">
-        <VnDms
-            :default-dms-code="$props.defaultDmsCode"
-            :url="`Workers/${$props.id}/uploadFile`"
-            @on-data-saved="onDataSaved"
-            model="WorkerDmsCreate"
-        />
-    </QDialog>
-</template>
diff --git a/src/pages/Worker/components/WorkerDmsDelete.vue b/src/pages/Worker/components/WorkerDmsDelete.vue
deleted file mode 100644
index 5e1b98b24..000000000
--- a/src/pages/Worker/components/WorkerDmsDelete.vue
+++ /dev/null
@@ -1,82 +0,0 @@
-<script setup>
-import { ref } from 'vue';
-import { useI18n } from 'vue-i18n';
-
-import { useDialogPluginComponent } from 'quasar';
-import axios from 'axios';
-
-import useNotify from 'src/composables/useNotify';
-
-const $props = defineProps({
-    id: {
-        type: Number,
-        required: true,
-    },
-    promise: {
-        type: Function,
-        required: true,
-    },
-});
-
-const { dialogRef } = useDialogPluginComponent();
-const { notify } = useNotify();
-const { t } = useI18n();
-
-const closeButton = ref(null);
-
-const isLoading = ref(false);
-
-const deleteDms = async () => {
-    isLoading.value = true;
-    try {
-        await axios.post(`WorkerDms/${$props.id}/removeFile`);
-        if ($props.promise) await $props.promise();
-        notify('globals.dataDeleted', 'positive');
-    } catch (error) {
-        notify(error.message, 'negative');
-    } finally {
-        closeButton.value.click();
-        isLoading.value = false;
-    }
-};
-</script>
-
-<template>
-    <QDialog ref="dialogRef">
-        <QCard class="q-pa-md q-mb-md">
-            <span ref="closeButton" class="row justify-end close-icon" v-close-popup>
-                <QIcon name="close" size="sm" />
-            </span>
-
-            <QCardSection>
-                <div class="mt-1 text-h6">{{ t('This file will be deleted') }}</div>
-                <div>{{ t('Are you sure you want to continue?') }}</div>
-            </QCardSection>
-
-            <QCardActions class="flex justify-end">
-                <QBtn
-                    :disabled="isLoading"
-                    :label="t('globals.cancel')"
-                    :loading="isLoading"
-                    class="q-mr-xl"
-                    color="primary"
-                    flat
-                    v-close-popup
-                />
-                <QBtn
-                    :disabled="isLoading"
-                    :label="t('globals.save')"
-                    :loading="isLoading"
-                    @click.stop="deleteDms"
-                    color="primary"
-                />
-            </QCardActions>
-        </QCard>
-    </QDialog>
-</template>
-
-<i18n>
-es:
-    This file will be deleted: Este fichero va a ser borrado
-    Are you sure you want to continue?: ¿Seguro que quieres continuar?
-</i18n>
diff --git a/src/pages/Worker/components/WorkerDmsEdit.vue b/src/pages/Worker/components/WorkerDmsEdit.vue
deleted file mode 100644
index 7656fe09f..000000000
--- a/src/pages/Worker/components/WorkerDmsEdit.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<script setup>
-import { ref } from 'vue';
-
-import { useDialogPluginComponent } from 'quasar';
-
-import FetchData from 'components/FetchData.vue';
-import VnDms from 'src/components/common/VnDms.vue';
-
-const { dialogRef } = useDialogPluginComponent();
-
-const $props = defineProps({
-    dmsId: {
-        type: Number,
-        required: true,
-    },
-    defaultDmsCode: {
-        type: String,
-        required: false,
-    },
-    promise: {
-        type: Function,
-        required: true,
-    },
-});
-
-const initialData = ref({
-    hasFile: true,
-    id: $props.dmsId,
-});
-
-const setCurrentDms = (data) => {
-    initialData.value.reference = data.reference;
-    initialData.value.companyFk = data.companyFk;
-    initialData.value.warehouseFk = data.warehouseFk;
-    initialData.value.dmsTypeFk = data.dmsTypeFk;
-    initialData.value.description = data.description;
-    initialData.value.hasFile = data.hasFile;
-};
-
-const onDataSaved = async () => {
-    if ($props.promise) await $props.promise();
-};
-</script>
-
-<template>
-    <fetch-data :url="`Dms/${$props.dmsId}`" @on-fetch="setCurrentDms" auto-load />
-
-    <QDialog ref="dialogRef">
-        <VnDms
-            :default-initialData-code="$props.defaultDmsCode"
-            @on-data-saved="onDataSaved"
-            model="WorkerDmsEdit"
-            :form-initial-data="initialData"
-        />
-    </QDialog>
-</template>

From f12ec3c0c9a6cf7e245fda5113fb2b0c92904f36 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Mon, 8 Apr 2024 08:47:29 -0300
Subject: [PATCH 05/15] Add redirection to summary and open summary action

---
 src/i18n/en/index.js        |  1 +
 src/i18n/es/index.js        |  1 +
 src/pages/Item/ItemList.vue | 17 ++++++++++++-----
 3 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 6c4cc34f3..0a17ee45f 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -1167,6 +1167,7 @@ export default {
         list: {
             id: 'Identifier',
             grouping: 'Grouping',
+            packing: 'Packing',
             description: 'Description',
             stems: 'Stems',
             category: 'Category',
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 4ad9a1d5c..f484c6cc6 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -1167,6 +1167,7 @@ export default {
         list: {
             id: 'Identificador',
             grouping: 'Grouping',
+            packing: 'Packing',
             description: 'Descripción',
             stems: 'Tallos',
             category: 'Reino',
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 14549296b..9baf3061a 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -10,18 +10,21 @@ import VnInput from 'src/components/common/VnInput.vue';
 import VnSelectFilter from 'src/components/common/VnSelectFilter.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 { useStateStore } from 'stores/useStateStore';
 import { toDate, toCurrency } from 'src/filters';
 import { useSession } from 'composables/useSession';
 import { dashIfEmpty } from 'src/filters';
 import { useArrayData } from 'composables/useArrayData';
+import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 
 const router = useRouter();
 const session = useSession();
 const token = session.getToken();
 const stateStore = useStateStore();
 const { t } = useI18n();
+const { viewSummary } = useSummaryDialog();
 
 const itemTypesOptions = ref([]);
 const originsOptions = ref([]);
@@ -373,8 +376,8 @@ const redirectToItemCreate = () => {
     // router.push({ name: 'EntryBuys', params: { id: entryFk } });
 };
 
-const redirectToItemSummary = () => {
-    // router.push({ name: 'EntryBuys', params: { id: entryFk } });
+const redirectToItemSummary = (id) => {
+    router.push({ name: 'ItemSummary', params: { id } });
 };
 
 // const applyColumnFilter = async (col) => {
@@ -386,6 +389,10 @@ const redirectToItemSummary = () => {
 //     }
 // };
 
+const cloneRow = () => {
+    console.log('cloneRow');
+};
+
 onMounted(async () => {
     stateStore.rightDrawer = true;
     const filteredColumns = columns.value.filter((col) => col.name !== 'picture');
@@ -507,10 +514,10 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                     <QCheckbox :model-value="!!row.isActive" disable />
                 </QTd>
             </template>
-            <template #body-cell-actions="{}">
+            <template #body-cell-actions="{ row }">
                 <QTd>
                     <QIcon
-                        @click.stop="viewSummary($props.id, CustomerSummary)"
+                        @click.stop="cloneRow()"
                         class="q-ml-sm"
                         color="primary"
                         name="vn:clone"
@@ -521,7 +528,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                         </QTooltip>
                     </QIcon>
                     <QIcon
-                        @click.stop="viewSummary($props.id, CustomerSummary)"
+                        @click.stop="viewSummary(row.id, ItemSummary)"
                         class="q-ml-md"
                         color="primary"
                         name="preview"

From 64ff5e44460be30d3566d8779aa6b76f0fc14f07 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Mon, 8 Apr 2024 11:40:44 -0300
Subject: [PATCH 06/15] Add pagination and table actions

---
 src/pages/Item/ItemList.vue | 212 ++++++++++++++++++++----------------
 1 file changed, 119 insertions(+), 93 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 9baf3061a..773cb0771 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -11,6 +11,7 @@ import VnSelectFilter from 'src/components/common/VnSelectFilter.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 { useStateStore } from 'stores/useStateStore';
 import { toDate, toCurrency } from 'src/filters';
@@ -18,13 +19,16 @@ import { useSession } from 'composables/useSession';
 import { dashIfEmpty } from 'src/filters';
 import { useArrayData } from 'composables/useArrayData';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
+import { useVnConfirm } from 'composables/useVnConfirm';
+import axios from 'axios';
 
 const router = useRouter();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 const stateStore = useStateStore();
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
+const { openConfirmationModal } = useVnConfirm();
 
 const itemTypesOptions = ref([]);
 const originsOptions = ref([]);
@@ -66,13 +70,6 @@ const exprBuilder = (param, value) => {
 };
 
 const params = reactive({});
-const arrayData = useArrayData('ItemList', {
-    url: 'Items/filter',
-    order: ['isActive DESC', 'name', 'id'],
-    exprBuilder: exprBuilder,
-});
-const store = arrayData.store;
-const rows = computed(() => store.data);
 
 // const getInputEvents = (col) => {
 //     return col.columnFilter.type === 'select'
@@ -389,15 +386,25 @@ const redirectToItemSummary = (id) => {
 //     }
 // };
 
-const cloneRow = () => {
-    console.log('cloneRow');
+const cloneItem = async (itemFk) => {
+    try {
+        console.log('cloneRow: ', itemFk);
+        const { data } = await axios.post(`Items/${itemFk}/clone`);
+        if (!data) return;
+        router.push({ name: 'ItemTags', params: { id: data.id } });
+    } catch (err) {
+        console.error('Error cloning row', err);
+    }
+};
+
+const test = (ev) => {
+    console.log('test: ', ev);
 };
 
 onMounted(async () => {
     stateStore.rightDrawer = true;
     const filteredColumns = columns.value.filter((col) => col.name !== 'picture');
     allColumnNames.value = filteredColumns.map((col) => col.name);
-    await arrayData.fetch({ append: false });
 });
 
 onUnmounted(() => (stateStore.rightDrawer = false));
@@ -440,23 +447,29 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         <QSpace />
         <div id="st-actions"></div>
     </QToolbar>
-    <!-- <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
-        <QScrollArea class="fit text-grey-8">
-            <EntryLatestBuysFilter data-key="EntryLatestBuys" :tags="tags" />
-        </QScrollArea>
-    </QDrawer> -->
     <QPage class="column items-center q-pa-md">
-        <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)"
+        <VnPaginate
+            data-key="ItemList"
+            url="Items/filter"
+            :order="['isActive DESC', 'name', 'id']"
+            :limit="12"
+            :offset="50"
+            auto-load
         >
-            <!-- <template #top-row="{ cols }">
+            <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')"
+                    virtual-scroll
+                    @virtual-scroll="test"
+                    @row-click="(_, row) => redirectToItemSummary(row.id)"
+                >
+                    <!-- <template #top-row="{ cols }">
                 <QTr>
                     <QTd />
                     <QTd
@@ -475,75 +488,84 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                     </QTd>
                 </QTr>
             </template> -->
-            <template #body-cell-picture="{ row }">
-                <QTd>
-                    <QImg
-                        :src="`/api/Images/catalog/50x50/${row.id}/download?access_token=${token}`"
-                        spinner-color="primary"
-                        :ratio="1"
-                        height="50px"
-                        width="50px"
-                        class="image"
-                    />
-                </QTd>
+                    <template #body-cell-picture="{ row }">
+                        <QTd>
+                            <QImg
+                                :src="`/api/Images/catalog/50x50/${row.id}/download?access_token=${token}`"
+                                spinner-color="primary"
+                                :ratio="1"
+                                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>
+                            <fetched-tags :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('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>
-            <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>
-                    <fetched-tags :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="cloneRow()"
-                        class="q-ml-sm"
-                        color="primary"
-                        name="vn:clone"
-                        size="sm"
-                    >
-                        <QTooltip>
-                            {{ t('Preview') }}
-                        </QTooltip>
-                    </QIcon>
-                    <QIcon
-                        @click.stop="viewSummary(row.id, ItemSummary)"
-                        class="q-ml-md"
-                        color="primary"
-                        name="preview"
-                        size="sm"
-                    >
-                        <QTooltip>
-                            {{ t('Preview') }}
-                        </QTooltip>
-                    </QIcon>
-                </QTd>
-            </template>
-        </QTable>
+        </VnPaginate>
+
         <QPageSticky :offset="[20, 20]">
             <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" />
-            <QTooltip>
+            <QTooltip class="text-no-wrap">
                 {{ t('New item') }}
             </QTooltip>
         </QPageSticky>
@@ -553,4 +575,8 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 <i18n>
 es:
     New item: Nuevo artículo
+    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?
+    Clone: Clonar
+    Preview: Vista previa
 </i18n>

From e51ea9996b5bb8fb797ec1085313cd24b69bbea3 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 9 Apr 2024 08:18:18 -0300
Subject: [PATCH 07/15] Item list view

---
 src/components/ui/VnPaginate.vue |   7 +-
 src/composables/useArrayData.js  |   2 +
 src/i18n/en/index.js             |   2 +
 src/i18n/es/index.js             |   2 +
 src/pages/Item/ItemCreate.vue    |   1 +
 src/pages/Item/ItemList.vue      | 443 +++++++++++++++----------------
 src/router/modules/item.js       |   8 +
 7 files changed, 239 insertions(+), 226 deletions(-)
 create mode 100644 src/pages/Item/ItemCreate.vue

diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index d1b2f5ccb..fc81c82b1 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -61,7 +61,6 @@ const props = defineProps({
 });
 
 const emit = defineEmits(['onFetch', 'onPaginate']);
-defineExpose({ fetch });
 const isLoading = ref(false);
 const pagination = ref({
     sortBy: props.order,
@@ -91,6 +90,10 @@ watch(
     }
 );
 
+const addFilter = async (filter, params) => {
+    await arrayData.addFilter({ filter, params });
+};
+
 async function fetch() {
     await arrayData.fetch({ append: false });
     if (!arrayData.hasMoreData.value) {
@@ -140,6 +143,8 @@ async function onLoad(index, done) {
     if (store.userParamsChanged) isDone = !arrayData.hasMoreData.value;
     done(isDone);
 }
+
+defineExpose({ fetch, addFilter });
 </script>
 
 <template>
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index b6b81f2d5..74228d8dc 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -145,6 +145,8 @@ export function useArrayData(key, userOptions) {
         store.userParams = userParams;
         store.skip = 0;
         store.filter.skip = 0;
+        page.value = 1;
+
         await fetch({ append: false });
         return { filter, params };
     }
diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 710b1d5bf..59082422a 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -1208,6 +1208,7 @@ export default {
             list: 'List',
             diary: 'Diary',
             tags: 'Tags',
+            create: 'Create',
         },
         descriptor: {
             item: 'Item',
@@ -1229,6 +1230,7 @@ export default {
             category: 'Category',
             type: 'Type',
             intrastat: 'Intrastat',
+            isActive: 'Active',
             size: 'Size',
             origin: 'Origin',
             userName: 'Buyer',
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 3a5da882d..16b2e9394 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -1207,6 +1207,7 @@ export default {
             list: 'Listado',
             diary: 'Histórico',
             tags: 'Etiquetas',
+            create: 'Crear',
         },
         descriptor: {
             item: 'Artículo',
@@ -1228,6 +1229,7 @@ export default {
             category: 'Reino',
             type: 'Tipo',
             intrastat: 'Intrastat',
+            isActive: 'Activo',
             size: 'Medida',
             origin: 'Origen',
             weightByPiece: 'Peso (gramos)/tallo',
diff --git a/src/pages/Item/ItemCreate.vue b/src/pages/Item/ItemCreate.vue
new file mode 100644
index 000000000..e20f67ac3
--- /dev/null
+++ b/src/pages/Item/ItemCreate.vue
@@ -0,0 +1 @@
+<template>Item create view</template>
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 773cb0771..304145e23 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -14,10 +14,9 @@ import ItemSummary from '../Item/Card/ItemSummary.vue';
 import VnPaginate from 'components/ui/VnPaginate.vue';
 
 import { useStateStore } from 'stores/useStateStore';
-import { toDate, toCurrency } from 'src/filters';
+import { toDateFormat } from 'src/filters/date.js';
 import { useSession } from 'composables/useSession';
 import { dashIfEmpty } from 'src/filters';
-import { useArrayData } from 'composables/useArrayData';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import { useVnConfirm } from 'composables/useVnConfirm';
 import axios from 'axios';
@@ -30,11 +29,12 @@ const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
 const { openConfirmationModal } = useVnConfirm();
 
+const paginateRef = ref(null);
 const itemTypesOptions = ref([]);
 const originsOptions = ref([]);
-const itemFamiliesOptions = ref([]);
-// const intrastatOptions = ref([]);
-const packagingsOptions = ref([]);
+const buyersOptions = ref([]);
+const intrastatOptions = ref([]);
+const itemCategoriesOptions = ref([]);
 const visibleColumns = ref([]);
 const allColumnNames = ref([]);
 
@@ -71,19 +71,30 @@ const exprBuilder = (param, value) => {
 
 const params = reactive({});
 
-// const getInputEvents = (col) => {
-//     return col.columnFilter.type === 'select'
-//         ? { 'update:modelValue': () => applyColumnFilter(col) }
-//         : {
-//               'keyup.enter': () => applyColumnFilter(col),
-//           };
-// };
+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'),
@@ -91,15 +102,15 @@ const columns = computed(() => [
         field: 'id',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
     },
     {
         label: t('item.list.grouping'),
@@ -107,15 +118,15 @@ const columns = computed(() => [
         name: 'grouping',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
         format: (val) => dashIfEmpty(val),
     },
     {
@@ -124,33 +135,32 @@ const columns = computed(() => [
         name: 'packing',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
         format: (val) => dashIfEmpty(val),
     },
     {
         label: t('globals.description'),
-        field: 'description',
+        field: 'name',
         name: 'description',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
-        // format: (val) => dashIfEmpty(val),
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
     },
     {
         label: t('item.list.stems'),
@@ -158,15 +168,15 @@ const columns = computed(() => [
         name: 'stems',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
     },
     {
         label: t('item.list.size'),
@@ -174,15 +184,15 @@ const columns = computed(() => [
         name: 'size',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
     },
     {
         label: t('item.list.type'),
@@ -190,18 +200,19 @@ const columns = computed(() => [
         name: 'typeName',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnSelectFilter,
-        //     type: 'select',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         options: itemTypesOptions.value,
-        //         'option-value': 'code',
-        //         'option-label': 'code',
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnSelectFilter,
+            filterParamKey: 'typeFk',
+            type: 'select',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                options: itemTypesOptions.value,
+                'option-value': 'id',
+                'option-label': 'name',
+                dense: true,
+            },
+        },
     },
 
     {
@@ -210,18 +221,18 @@ const columns = computed(() => [
         name: 'category',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnSelectFilter,
-        //     type: 'select',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         options: intrastatOptions.value,
-        //         'option-value': 'description',
-        //         'option-label': 'description',
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnSelectFilter,
+            type: 'select',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                options: itemCategoriesOptions.value,
+                'option-value': 'name',
+                'option-label': 'name',
+                dense: true,
+            },
+        },
     },
 
     {
@@ -230,18 +241,18 @@ const columns = computed(() => [
         name: 'intrastat',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnSelectFilter,
-        //     type: 'select',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         options: originsOptions.value,
-        //         'option-value': 'code',
-        //         'option-label': 'code',
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnSelectFilter,
+            type: 'select',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                options: intrastatOptions.value,
+                'option-value': 'description',
+                'option-label': 'description',
+                dense: true,
+            },
+        },
     },
     {
         label: t('item.list.origin'),
@@ -249,15 +260,18 @@ const columns = computed(() => [
         name: 'origin',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnSelectFilter,
+            type: 'select',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                options: originsOptions.value,
+                'option-value': 'code',
+                'option-label': 'code',
+                dense: true,
+            },
+        },
     },
     {
         label: t('item.list.userName'),
@@ -265,18 +279,19 @@ const columns = computed(() => [
         name: 'userName',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnSelectFilter,
-        //     type: 'select',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         options: itemFamiliesOptions.value,
-        //         'option-value': 'code',
-        //         'option-label': 'code',
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnSelectFilter,
+            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'),
@@ -284,15 +299,15 @@ const columns = computed(() => [
         name: 'weightByPiece',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
         format: (val) => dashIfEmpty(val),
     },
     {
@@ -301,32 +316,24 @@ const columns = computed(() => [
         name: 'stemMultiplier',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
         format: (val) => dashIfEmpty(val),
     },
     {
-        label: t('entry.latestBuys.isActive'),
+        label: t('item.list.isActive'),
         field: 'isActive',
         name: 'isActive',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: null,
     },
     {
         label: t('item.list.producer'),
@@ -334,15 +341,15 @@ const columns = computed(() => [
         name: 'producer',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
+        columnFilter: {
+            component: VnInput,
+            type: 'text',
+            filterValue: null,
+            event: getInputEvents,
+            attrs: {
+                dense: true,
+            },
+        },
         format: (val) => dashIfEmpty(val),
     },
     {
@@ -351,44 +358,27 @@ const columns = computed(() => [
         name: 'landed',
         align: 'left',
         sortable: true,
-        // columnFilter: {
-        //     component: VnInput,
-        //     type: 'text',
-        //     filterValue: null,
-        //     event: getInputEvents,
-        //     attrs: {
-        //         dense: true,
-        //     },
-        // },
-        format: (val) => dashIfEmpty(toDate(val)),
+        format: (val) => dashIfEmpty(toDateFormat(val)),
+        columnFilter: null,
     },
     {
         label: '',
         name: 'actions',
         align: 'left',
+        columnFilter: null,
     },
 ]);
 
 const redirectToItemCreate = () => {
-    // router.push({ name: 'EntryBuys', params: { id: entryFk } });
+    router.push({ name: 'ItemCreate' });
 };
 
 const redirectToItemSummary = (id) => {
     router.push({ name: 'ItemSummary', params: { id } });
 };
 
-// const applyColumnFilter = async (col) => {
-//     try {
-//         params[col.field] = col.columnFilter.filterValue;
-//         await arrayData.addFilter({ params });
-//     } catch (err) {
-//         console.error('Error applying column filter', err);
-//     }
-// };
-
 const cloneItem = async (itemFk) => {
     try {
-        console.log('cloneRow: ', itemFk);
         const { data } = await axios.post(`Items/${itemFk}/clone`);
         if (!data) return;
         router.push({ name: 'ItemTags', params: { id: data.id } });
@@ -397,10 +387,6 @@ const cloneItem = async (itemFk) => {
     }
 };
 
-const test = (ev) => {
-    console.log('test: ', ev);
-};
-
 onMounted(async () => {
     stateStore.rightDrawer = true;
     const filteredColumns = columns.value.filter((col) => col.name !== 'picture');
@@ -411,30 +397,37 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
-    <!-- <FetchData
+    <FetchData
         url="ItemTypes"
-        :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }"
+        :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
         auto-load
         @on-fetch="(data) => (itemTypesOptions = data)"
-    /> -->
-    <!-- <FetchData
+    />
+    <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', limit: 30 }"
+        :filter="{ fields: ['code'], order: 'code ASC' }"
         auto-load
         @on-fetch="(data) => (originsOptions = data)"
-    /> -->
-    <!-- <FetchData
-        url="ItemFamilies"
-        :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }"
+    />
+    <FetchData
+        url="TicketRequests/getItemTypeWorker"
+        :filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }"
         auto-load
-        @on-fetch="(data) => (itemFamiliesOptions = data)"
-    /> -->
-    <!-- <FetchData
-        url="Packagings"
-        :filter="{ fields: ['id'], order: 'id ASC', limit: 30 }"
-        auto-load
-        @on-fetch="(data) => (packagingsOptions = data)"
-    /> -->
+        @on-fetch="(data) => (buyersOptions = data)"
+    />
+
     <QToolbar class="bg-vn-dark justify-end">
         <div id="st-data">
             <TableVisibleColumns
@@ -449,10 +442,13 @@ onUnmounted(() => (stateStore.rightDrawer = false));
     </QToolbar>
     <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"
             :offset="50"
             auto-load
         >
@@ -465,29 +461,26 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                     class="full-width q-mt-md"
                     :visible-columns="visibleColumns"
                     :no-data-label="t('globals.noResults')"
-                    virtual-scroll
-                    @virtual-scroll="test"
                     @row-click="(_, row) => redirectToItemSummary(row.id)"
                 >
-                    <!-- <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.name !== 'picture'"
-                            v-model="col.columnFilter.filterValue"
-                            v-bind="col.columnFilter.attrs"
-                            v-on="col.columnFilter.event(col)"
-                            dense
-                        />
-                    </QTd>
-                </QTr>
-            </template> -->
+                    <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>
                             <QImg
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index 700278617..d16022f79 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -29,6 +29,14 @@ export default {
                     },
                     component: () => import('src/pages/Item/ItemList.vue'),
                 },
+                {
+                    path: 'create',
+                    name: 'ItemCreate',
+                    meta: {
+                        title: 'create',
+                    },
+                    component: () => import('src/pages/Item/ItemCreate.vue'),
+                },
             ],
         },
         {

From f6e140e369f54bf41c77fb3fd05e584e740eeaa7 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 9 Apr 2024 08:28:00 -0300
Subject: [PATCH 08/15] Apply workerDms changes

---
 src/components/common/VnDms.vue               |   2 +
 src/components/common/VnDmsList.vue           | 225 +++++++++++-------
 src/components/ui/VnPaginate.vue              |  15 +-
 src/components/ui/VnTree.vue                  |   2 +-
 src/composables/downloadFile.js               |   5 +-
 src/composables/getUrl.js                     |  24 --
 src/composables/useArrayData.js               |   2 +-
 src/i18n/en/index.js                          |   2 +
 src/i18n/es/index.js                          |   2 +
 .../Department/Card/DepartmentDescriptor.vue  |   2 +-
 src/pages/Worker/Card/WorkerDms.vue           |   7 +-
 11 files changed, 163 insertions(+), 125 deletions(-)

diff --git a/src/components/common/VnDms.vue b/src/components/common/VnDms.vue
index 08255f8a5..daef5940b 100644
--- a/src/components/common/VnDms.vue
+++ b/src/components/common/VnDms.vue
@@ -198,9 +198,11 @@ function addDefaultData(data) {
 en:
     contentTypesInfo: Allowed file types {allowedContentTypes}
     EntryDmsDescription: Reference {reference}
+    WorkersDescription: Working of employee id {reference}
 es:
     Generate identifier for original file: Generar identificador para archivo original
     contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
     EntryDmsDescription: Referencia {reference}
+    WorkersDescription: Laboral del empleado {reference}
 
 </i18n>
diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index 9bf90230d..8c3ffc807 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -5,11 +5,12 @@ import { useRoute } from 'vue-router';
 import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
 import axios from 'axios';
 
-import FetchData from 'components/FetchData.vue';
+import VnPaginate from 'components/ui/VnPaginate.vue';
 import VnDms from 'src/components/common/VnDms.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
+import VnInputDate from 'components/common/VnInputDate.vue';
+import VnUserLink from '../ui/VnUserLink.vue';
 import { downloadFile } from 'src/composables/downloadFile';
-import { getUrlFindOne } from 'composables/getUrl';
 
 const route = useRoute();
 const quasar = useQuasar();
@@ -27,6 +28,11 @@ const $props = defineProps({
         type: String,
         default: null,
     },
+    downloadModel: {
+        type: String,
+        required: false,
+        default: null,
+    },
     defaultDmsCode: {
         type: String,
         required: true,
@@ -75,7 +81,7 @@ const dmsFilter = {
             ],
         },
     },
-    order: ['dmsFk DESC'],
+    where: { [$props.filter]: route.params.id },
 };
 
 const columns = computed(() => [
@@ -95,12 +101,12 @@ const columns = computed(() => [
         props: (prop) => ({
             readonly: true,
             borderless: true,
-            'model-value': prop.row.dmsType.name,
+            'model-value': prop.row.dmsType?.name,
         }),
     },
     {
         align: 'left',
-        field: 'order',
+        field: 'hardCopyNumber',
         label: t('globals.order'),
         name: 'order',
         component: 'span',
@@ -118,6 +124,7 @@ const columns = computed(() => [
         label: t('globals.description'),
         name: 'description',
         component: 'span',
+        props: (prop) => ({ value: prop.value?.toUpperCase() }),
     },
     {
         align: 'left',
@@ -137,6 +144,28 @@ const columns = computed(() => [
         name: 'file',
         component: 'span',
     },
+    {
+        align: 'left',
+        field: 'worker',
+        label: t('globals.worker'),
+        name: 'worker',
+        component: VnUserLink,
+        props: (prop) => ({
+            name: prop.row.worker?.user?.name.toLowerCase(),
+            workerId: prop.row.worker?.id,
+        }),
+    },
+    {
+        align: 'left',
+        field: 'created',
+        label: t('globals.created'),
+        name: 'created',
+        component: VnInputDate,
+        props: (prop) => ({
+            disable: true,
+            'model-value': prop.row.created,
+        }),
+    },
     {
         field: 'options',
         name: 'options',
@@ -144,16 +173,24 @@ const columns = computed(() => [
             {
                 component: QBtn,
                 name: 'download',
+                isDocuware: true,
                 props: () => ({
                     icon: 'cloud_download',
                     flat: true,
                     color: 'primary',
                 }),
-                click: (prop) => downloadFile(prop.row.id, prop.row.isDocuware),
+                click: (prop) =>
+                    downloadFile(
+                        prop.row.id,
+                        $props.downloadModel,
+                        null,
+                        prop.row.download
+                    ),
             },
             {
                 component: QBtn,
                 name: 'edit',
+                external: false,
                 props: () => ({
                     icon: 'edit',
                     flat: true,
@@ -164,6 +201,7 @@ const columns = computed(() => [
             {
                 component: QBtn,
                 name: 'delete',
+                external: false,
                 props: () => ({
                     icon: 'delete',
                     flat: true,
@@ -173,20 +211,22 @@ const columns = computed(() => [
             },
             {
                 component: QBtn,
-                name: 'openDocuware',
+                name: 'open',
+                external: true,
                 props: () => ({
                     icon: 'open_in_new',
                     flat: true,
                     color: 'primary',
                 }),
-                click: () => openDocuware(),
+                click: (prop) => open(prop.row.url),
             },
         ],
     },
 ]);
 
 function setData(data) {
-    const newData = data.map((value) => value.dms);
+    const newData = data.map((value) => value.dms || value);
+    newData.sort((a, b) => new Date(b.created) - new Date(a.created));
     rows.value = newData;
 }
 
@@ -221,100 +261,105 @@ function parseDms(data) {
     return data;
 }
 
-async function openDocuware() {
-    const url = await getUrlFindOne('WebClient', 'docuware');
-    if (url) window.open(url).focus();
+async function open(url) {
+    window.open(url).focus();
 }
 
-function shouldRenderButton(buttonName, isDocuware = false) {
-    // Renderizar el botón si no se llama 'openDocuware' o (se llama 'openDocuware' y props.row.isDocuware = true)
-    return buttonName !== 'openDocuware' || (buttonName === 'openDocuware' && isDocuware);
+function shouldRenderButton(button, isExternal = false) {
+    if (button.name == 'download') return true;
+    return button.external === isExternal;
 }
 </script>
 <template>
-    <FetchData
+    <VnPaginate
         ref="dmsRef"
+        :data-key="$props.model"
         :url="$props.model"
         :filter="dmsFilter"
-        :where="{ [$props.filter]: route.params.id }"
+        :order="['dmsFk DESC']"
+        :auto-load="true"
         @on-fetch="setData"
-        auto-load
-    />
-    <QTable
-        :columns="columns"
-        :rows="rows"
-        class="full-width q-mt-md"
-        hide-bottom
-        row-key="clientFk"
-        :grid="$q.screen.lt.sm"
     >
-        <template #body-cell="props">
-            <QTd :props="props">
-                <QTr :props="props">
-                    <component
-                        v-if="props.col.component"
-                        :is="props.col.component"
-                        v-bind="props.col.props && props.col.props(props)"
-                    >
-                        <span
-                            v-if="props.col.component == 'span'"
-                            style="white-space: wrap"
-                            >{{ props.value }}</span
-                        >
-                    </component>
-                </QTr>
-
-                <div class="flex justify-center" v-if="props.col.name == 'options'">
-                    <div v-for="button of props.col.components" :key="button.id">
-                        <component
-                            v-if="shouldRenderButton(button.name, props.row.isDocuware)"
-                            :is="button.component"
-                            v-bind="button.props(props)"
-                            @click="button.click(props)"
-                        />
-                    </div>
-                </div>
-            </QTd>
-        </template>
-        <template #item="props">
-            <div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
-                <QCard
-                    bordered
-                    flat
-                    @keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()"
-                >
-                    <QSeparator />
-                    <QList dense>
-                        <QItem v-for="col in props.cols" :key="col.name">
-                            <div v-if="col.name != 'options'" class="row">
-                                <span class="labelColor">{{ col.label }}:</span>
-                                <span>{{ col.value }}</span>
-                            </div>
-                            <div v-if="col.name == 'options'" class="row">
-                                <div
-                                    v-for="button of col.components"
-                                    :key="button.id"
-                                    class="row"
+        <template #body>
+            <QTable
+                :columns="columns"
+                :rows="rows"
+                class="full-width q-mt-md"
+                hide-bottom
+                row-key="clientFk"
+                :grid="$q.screen.lt.sm"
+            >
+                <template #body-cell="props">
+                    <QTd :props="props">
+                        <QTr :props="props">
+                            <component
+                                v-if="props.col.component"
+                                :is="props.col.component"
+                                v-bind="props.col.props && props.col.props(props)"
+                            >
+                                <span
+                                    v-if="props.col.component == 'span'"
+                                    style="white-space: wrap"
+                                    >{{ props.value }}</span
                                 >
-                                    <component
-                                        v-if="
-                                            shouldRenderButton(
-                                                button.name,
-                                                props.row.isDocuware
-                                            )
-                                        "
-                                        :is="button.component"
-                                        v-bind="button.props(col)"
-                                        @click="button.click(col)"
-                                    />
-                                </div>
+                            </component>
+                        </QTr>
+
+                        <div class="row no-wrap" v-if="props.col.name == 'options'">
+                            <div v-for="button of props.col.components" :key="button.id">
+                                <component
+                                    v-if="
+                                        shouldRenderButton(button, props.row.isDocuware)
+                                    "
+                                    :is="button.component"
+                                    v-bind="button.props(props)"
+                                    @click="button.click(props)"
+                                />
                             </div>
-                        </QItem>
-                    </QList>
-                </QCard>
-            </div>
+                        </div>
+                    </QTd>
+                </template>
+                <template #item="props">
+                    <div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
+                        <QCard
+                            bordered
+                            flat
+                            @keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()"
+                        >
+                            <QSeparator />
+                            <QList dense>
+                                <QItem v-for="col in props.cols" :key="col.name">
+                                    <div v-if="col.name != 'options'" class="row">
+                                        <span class="labelColor">{{ col.label }}:</span>
+                                        <span>{{ col.value }}</span>
+                                    </div>
+                                    <div v-if="col.name == 'options'" class="row">
+                                        <div
+                                            v-for="button of col.components"
+                                            :key="button.id"
+                                            class="row"
+                                        >
+                                            <component
+                                                v-if="
+                                                    shouldRenderButton(
+                                                        button.name,
+                                                        props.row.isDocuware
+                                                    )
+                                                "
+                                                :is="button.component"
+                                                v-bind="button.props(col)"
+                                                @click="button.click(col)"
+                                            />
+                                        </div>
+                                    </div>
+                                </QItem>
+                            </QList>
+                        </QCard>
+                    </div>
+                </template>
+            </QTable>
         </template>
-    </QTable>
+    </VnPaginate>
     <QDialog v-model="formDialog.show">
         <VnDms
             :model="updateModel ?? model"
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index d1b2f5ccb..4cb2e6d6e 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -110,7 +110,7 @@ async function paginate() {
     if (!arrayData.hasMoreData.value) {
         if (store.userParamsChanged) arrayData.hasMoreData.value = true;
         store.userParamsChanged = false;
-        isLoading.value = false;
+        endPagination();
         return;
     }
 
@@ -120,12 +120,14 @@ async function paginate() {
     pagination.value.sortBy = sortBy;
     pagination.value.descending = descending;
 
-    isLoading.value = false;
+    endPagination();
+}
 
+function endPagination() {
+    isLoading.value = false;
     emit('onFetch', store.data);
     emit('onPaginate');
 }
-
 async function onLoad(index, done) {
     if (!store.data) {
         return done();
@@ -188,6 +190,12 @@ async function onLoad(index, done) {
             <QSpinner color="orange" size="md" />
         </div>
     </QInfiniteScroll>
+    <div
+        v-if="!isLoading && arrayData.hasMoreData"
+        class="w-full flex justify-center q-mt-md"
+    >
+        <QBtn color="primary" :label="t('Load more data')" @click="paginate()" />
+    </div>
 </template>
 
 <style lang="scss" scoped>
@@ -204,4 +212,5 @@ async function onLoad(index, done) {
 es:
     No data to display: Sin datos que mostrar
     No results found: No se han encontrado resultados
+    Load more data: Cargar más resultados
 </i18n>
diff --git a/src/components/ui/VnTree.vue b/src/components/ui/VnTree.vue
index 9a99124c6..13aa05635 100644
--- a/src/components/ui/VnTree.vue
+++ b/src/components/ui/VnTree.vue
@@ -76,7 +76,7 @@ const removeNode = (node) => {
                 notify(t('department.departmentRemoved'), 'positive');
                 await fetchNodeLeaves(parentFk);
             } catch (err) {
-                console.log('Error removing department');
+                console.error('Error removing department');
             }
         });
 };
diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js
index 45162f81b..12639dcd6 100644
--- a/src/composables/downloadFile.js
+++ b/src/composables/downloadFile.js
@@ -4,9 +4,8 @@ import { getUrl } from './getUrl';
 const { getTokenMultimedia } = useSession();
 const token = getTokenMultimedia();
 
-export async function downloadFile(dmsId, isDocuware = false) {
+export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) {
     let appUrl = await getUrl('', 'lilium');
     appUrl = appUrl.replace('/#/', '');
-    const urlPath = isDocuware ? '/docuwareDownload' : '/downloadFile';
-    window.open(`${appUrl}/api/dms/${dmsId}${urlPath}?access_token=${token}`);
+    window.open(url ?? `${appUrl}/api/${model}/${id}${urlPath}?access_token=${token}`);
 }
diff --git a/src/composables/getUrl.js b/src/composables/getUrl.js
index 7b7103562..1e6aec4c6 100644
--- a/src/composables/getUrl.js
+++ b/src/composables/getUrl.js
@@ -1,7 +1,4 @@
 import axios from 'axios';
-import useNotify from 'src/composables/useNotify.js';
-
-const { notify } = useNotify();
 
 export async function getUrl(route, app = 'salix') {
     let url;
@@ -11,24 +8,3 @@ export async function getUrl(route, app = 'salix') {
     });
     return url;
 }
-
-export async function getUrlFindOne(route, app = 'salix') {
-    try {
-        const env = process.env.NODE_ENV;
-
-        const filter = {
-            where: { and: [{ appName: app }, { environment: env }] },
-        };
-
-        const { data } = await axios.get('Urls/findOne', {
-            params: { filter: JSON.stringify(filter) },
-        });
-
-        if (!data) return null;
-
-        return data.url + route;
-    } catch (err) {
-        notify('Direction not found', 'negative');
-        console.error('Error finding url: ', err);
-    }
-}
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index b6b81f2d5..d32eeb32e 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -98,7 +98,7 @@ export function useArrayData(key, userOptions) {
 
         const { limit } = filter;
 
-        hasMoreData.value = response.data.length === limit;
+        hasMoreData.value = response.data.length >= limit;
 
         if (append) {
             if (!store.data) store.data = [];
diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index ff86aec4c..793fc42fd 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -92,6 +92,8 @@ export default {
             log: 'Logs',
             parkingList: 'Parkings list',
         },
+        created: 'Created',
+        worker: 'Worker',
     },
     errors: {
         statusUnauthorized: 'Access denied',
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index fbf8bc230..74e289039 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -92,6 +92,8 @@ export default {
             log: 'Historial',
             parkingList: 'Listado de parkings',
         },
+        created: 'Fecha creación',
+        worker: 'Trabajador',
     },
     errors: {
         statusUnauthorized: 'Acceso denegado',
diff --git a/src/pages/Department/Card/DepartmentDescriptor.vue b/src/pages/Department/Card/DepartmentDescriptor.vue
index 7049d4145..e60a8c91b 100644
--- a/src/pages/Department/Card/DepartmentDescriptor.vue
+++ b/src/pages/Department/Card/DepartmentDescriptor.vue
@@ -63,7 +63,7 @@ const removeDepartment = () => {
                 router.push({ name: 'WorkerDepartment' });
                 notify('department.departmentRemoved', 'positive');
             } catch (err) {
-                console.log('Error removing department');
+                console.error('Error removing department');
             }
         });
 };
diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index d0c5cf5e5..aef18d138 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -1,11 +1,14 @@
 <script setup>
 import VnDmsList from 'src/components/common/VnDmsList.vue';
+import { useRoute } from 'vue-router';
+const route = useRoute();
 </script>
 <template>
     <VnDmsList
-        model="WorkerDms"
+        :model="`WorkerDms/${route.params.id}/filter`"
         update-model="Workers"
+        download-model="WorkerDms"
         default-dms-code="hhrrData"
-        filter="workerFk"
+        filter="worker"
     />
 </template>

From 0144aab7a5f613d0775f14d79d56eccd2ad60ac0 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 9 Apr 2024 08:51:00 -0300
Subject: [PATCH 09/15] Mini fix

---
 src/i18n/en/index.js        | 4 ++--
 src/i18n/es/index.js        | 4 ++--
 src/pages/Item/ItemList.vue | 8 +++++---
 3 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 59082422a..fdeec2744 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -1228,14 +1228,14 @@ export default {
             description: 'Description',
             stems: 'Stems',
             category: 'Category',
-            type: 'Type',
+            typeName: 'Type',
             intrastat: 'Intrastat',
             isActive: 'Active',
             size: 'Size',
             origin: 'Origin',
             userName: 'Buyer',
             weightByPiece: 'Weight/Piece',
-            multiplier: 'Multiplier',
+            stemMultiplier: 'Multiplier',
             producer: 'Producer',
             landed: 'Landed',
         },
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 16b2e9394..bc9cb0d01 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -1227,14 +1227,14 @@ export default {
             description: 'Descripción',
             stems: 'Tallos',
             category: 'Reino',
-            type: 'Tipo',
+            typeName: 'Tipo',
             intrastat: 'Intrastat',
             isActive: 'Activo',
             size: 'Medida',
             origin: 'Origen',
             weightByPiece: 'Peso (gramos)/tallo',
             userName: 'Comprador',
-            multiplier: 'Multiplicador',
+            stemMultiplier: 'Multiplicador',
             producer: 'Productor',
             landed: 'F. entrega',
         },
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 304145e23..407b01fab 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -311,7 +311,7 @@ const columns = computed(() => [
         format: (val) => dashIfEmpty(val),
     },
     {
-        label: t('item.list.multiplier'),
+        label: t('item.list.stemMultiplier'),
         field: 'stemMultiplier',
         name: 'stemMultiplier',
         align: 'left',
@@ -383,13 +383,15 @@ const cloneItem = async (itemFk) => {
         if (!data) return;
         router.push({ name: 'ItemTags', params: { id: data.id } });
     } catch (err) {
-        console.error('Error cloning row', err);
+        console.error('Error cloning item', err);
     }
 };
 
 onMounted(async () => {
     stateStore.rightDrawer = true;
-    const filteredColumns = columns.value.filter((col) => col.name !== 'picture');
+    const filteredColumns = columns.value.filter(
+        (col) => col.name !== 'picture' && col.name !== 'actions'
+    );
     allColumnNames.value = filteredColumns.map((col) => col.name);
 });
 

From 9c3407846b0ab23a432e26e33523f7fb98bf341f Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 9 Apr 2024 10:30:40 -0300
Subject: [PATCH 10/15] Fix translation

---
 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 407b01fab..ec54d1c05 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -195,7 +195,7 @@ const columns = computed(() => [
         },
     },
     {
-        label: t('item.list.type'),
+        label: t('item.list.typeName'),
         field: 'typeName',
         name: 'typeName',
         align: 'left',

From e88194eb88235bca0a8b2a328e3b1a03a2d7393d Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 9 Apr 2024 16:00:12 -0300
Subject: [PATCH 11/15] Update worker dms model prop

---
 src/pages/Worker/Card/WorkerDms.vue | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index aef18d138..eee42857c 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -1,11 +1,10 @@
 <script setup>
 import VnDmsList from 'src/components/common/VnDmsList.vue';
-import { useRoute } from 'vue-router';
-const route = useRoute();
 </script>
+
 <template>
     <VnDmsList
-        :model="`WorkerDms/${route.params.id}/filter`"
+        model="WorkerDms"
         update-model="Workers"
         download-model="WorkerDms"
         default-dms-code="hhrrData"

From 39af4278a688309d5543f56a84da523a986f040f Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 10 Apr 2024 08:27:30 -0300
Subject: [PATCH 12/15] Add delete model to worker dms

---
 src/components/common/VnDmsList.vue | 6 +++++-
 src/pages/Worker/Card/WorkerDms.vue | 6 ++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index 8c3ffc807..cc184703d 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -28,6 +28,10 @@ const $props = defineProps({
         type: String,
         default: null,
     },
+    deleteModel: {
+        type: String,
+        default: null,
+    },
     downloadModel: {
         type: String,
         required: false,
@@ -240,7 +244,7 @@ function deleteDms(dmsFk) {
             },
         })
         .onOk(async () => {
-            await axios.post(`${$props.model}/${dmsFk}/removeFile`);
+            await axios.post($props.deleteModel ?? `${$props.model}/${dmsFk}/removeFile`);
             const index = rows.value.findIndex((row) => row.id == dmsFk);
             rows.value.splice(index, 1);
         });
diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index eee42857c..faf89b245 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -1,11 +1,13 @@
 <script setup>
 import VnDmsList from 'src/components/common/VnDmsList.vue';
+import { useRoute } from 'vue-router';
+const route = useRoute();
 </script>
-
 <template>
     <VnDmsList
-        model="WorkerDms"
+        :model="`WorkerDms/${route.params.id}/filter`"
         update-model="Workers"
+        :delete-model="`WorkerDms/${route.params.id}/removeFile`"
         download-model="WorkerDms"
         default-dms-code="hhrrData"
         filter="worker"

From 9ae79a5631639f023f175eb7d8996107a3394f82 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 10 Apr 2024 08:43:57 -0300
Subject: [PATCH 13/15] Add translation again

---
 src/i18n/es/index.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 6d54e52ec..c19eb0764 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -865,6 +865,7 @@ export default {
             basicData: 'Datos básicos',
             notes: 'Notas',
             pda: 'PDA',
+            dms: 'Mi documentación',
             notifications: 'Notificaciones',
             pbx: 'Centralita',
             log: 'Historial',

From e676933942d9987894360c93e6dbbe021897c932 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 10 Apr 2024 08:59:05 -0300
Subject: [PATCH 14/15] Update worker dms remove

---
 src/components/common/VnDmsList.vue | 2 +-
 src/pages/Worker/Card/WorkerDms.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index cc184703d..f70146c58 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -244,7 +244,7 @@ function deleteDms(dmsFk) {
             },
         })
         .onOk(async () => {
-            await axios.post($props.deleteModel ?? `${$props.model}/${dmsFk}/removeFile`);
+            await axios.post(`${$props.deleteModel ?? $props.model}/${dmsFk}/removeFile`);
             const index = rows.value.findIndex((row) => row.id == dmsFk);
             rows.value.splice(index, 1);
         });
diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index faf89b245..a4f7ef5a9 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -7,7 +7,7 @@ const route = useRoute();
     <VnDmsList
         :model="`WorkerDms/${route.params.id}/filter`"
         update-model="Workers"
-        :delete-model="`WorkerDms/${route.params.id}/removeFile`"
+        delete-model="WorkerDms"
         download-model="WorkerDms"
         default-dms-code="hhrrData"
         filter="worker"

From 99c576b16eb146fb56671d6323c2712bec52fc05 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 10 Apr 2024 09:32:15 -0300
Subject: [PATCH 15/15] Add actions again

---
 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 ec54d1c05..3e399e29f 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -436,7 +436,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                 :all-columns="allColumnNames"
                 table-code="itemsIndex"
                 labels-traductions-path="item.list"
-                @on-config-saved="visibleColumns = ['picture', ...$event]"
+                @on-config-saved="visibleColumns = ['picture', ...$event, 'actions']"
             />
         </div>
         <QSpace />