diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index 480d36e4f..05c74f00a 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -169,6 +169,7 @@ watch(modelValue, (newValue) => {
         ref="vnSelectRef"
         :class="{ required: $attrs.required }"
         :rules="$attrs.required ? [requiredFieldRule] : null"
+        virtual-scroll-slice-size="options.length"
     >
         <template v-if="isClearable" #append>
             <QIcon
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index 96d097191..eb0bbbe66 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -79,6 +79,7 @@ watch(
 
 const isLoading = ref(false);
 async function search() {
+    store.filter.where = {};
     isLoading.value = true;
     const params = { ...userParams.value };
     store.userParamsChanged = true;
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index fc8475ace..344267ef7 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -81,8 +81,10 @@ async function search() {
     const staticParams = Object.entries(store.userParams).filter(
         ([key, value]) => value && (props.staticParams || []).includes(key)
     );
+    // const filter =props?.where? { where: JSON.parse(props.where) }: {}
     await arrayData.applyFilter({
         params: {
+            // filter ,
             ...Object.fromEntries(staticParams),
             search: searchText.value,
         },
@@ -106,6 +108,7 @@ async function search() {
     let targetUrl;
 
     if (path.endsWith('/list')) targetUrl = path.replace('/list', `/${targetId}/summary`);
+    if (path.endsWith('-list')) targetUrl = path.replace('-list', `/${targetId}/summary`);
     else if (path.includes(':id')) targetUrl = path.replace(':id', targetId);
 
     await router.push({ path: targetUrl });
diff --git a/src/components/ui/VnUserLink.vue b/src/components/ui/VnUserLink.vue
index 47287c12b..33836550a 100644
--- a/src/components/ui/VnUserLink.vue
+++ b/src/components/ui/VnUserLink.vue
@@ -1,6 +1,5 @@
 <script setup>
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
 import { useI18n } from 'vue-i18n';
 
 const $props = defineProps({
diff --git a/src/composables/useSession.js b/src/composables/useSession.js
index 8583e10d4..56bce0279 100644
--- a/src/composables/useSession.js
+++ b/src/composables/useSession.js
@@ -3,11 +3,14 @@ import { useRole } from './useRole';
 import { useUserConfig } from './useUserConfig';
 import axios from 'axios';
 import useNotify from './useNotify';
+import { useTokenConfig } from './useTokenConfig';
 const TOKEN_MULTIMEDIA = 'tokenMultimedia';
 const TOKEN = 'token';
 
 export function useSession() {
     const { notify } = useNotify();
+    let isCheckingToken = false;
+    let intervalId = null;
 
     function getToken() {
         const localToken = localStorage.getItem(TOKEN);
@@ -22,10 +25,24 @@ export function useSession() {
         return localTokenMultimedia || sessionTokenMultimedia || '';
     }
 
-    function setToken(data) {
-        const storage = data.keepLogin ? localStorage : sessionStorage;
+    function setSession(data) {
+        let keepLogin = data.keepLogin;
+        const storage = keepLogin ? localStorage : sessionStorage;
         storage.setItem(TOKEN, data.token);
         storage.setItem(TOKEN_MULTIMEDIA, data.tokenMultimedia);
+        storage.setItem('created', data.created);
+        storage.setItem('ttl', data.ttl);
+        sessionStorage.setItem('keepLogin', keepLogin);
+    }
+
+    function keepLogin() {
+        return sessionStorage.getItem('keepLogin');
+    }
+
+    function setToken({ token, tokenMultimedia }) {
+        const storage = keepLogin() ? localStorage : sessionStorage;
+        storage.setItem(TOKEN, token);
+        storage.setItem(TOKEN_MULTIMEDIA, tokenMultimedia);
     }
     async function destroyToken(url, storage, key) {
         if (storage.getItem(key)) {
@@ -45,11 +62,15 @@ export function useSession() {
             tokenMultimedia: 'Accounts/logout',
             token: 'VnUsers/logout',
         };
+        const storage = keepLogin() ? localStorage : sessionStorage;
+
         for (const [key, url] of Object.entries(tokens)) {
-            await destroyToken(url, localStorage, key);
-            await destroyToken(url, sessionStorage, key);
+            await destroyToken(url, storage, key);
         }
 
+        localStorage.clear();
+        sessionStorage.clear();
+
         const { setUser } = useState();
 
         setUser({
@@ -59,22 +80,75 @@ export function useSession() {
             lang: '',
             darkMode: null,
         });
+
+        stopRenewer();
     }
 
-    async function login(token, tokenMultimedia, keepLogin) {
-        setToken({ token, tokenMultimedia, keepLogin });
+    async function login(data) {
+        setSession(data);
 
         await useRole().fetch();
         await useUserConfig().fetch();
+        await useTokenConfig().fetch();
+
+        startInterval();
     }
 
     function isLoggedIn() {
         const localToken = localStorage.getItem(TOKEN);
         const sessionToken = sessionStorage.getItem(TOKEN);
-
+        startInterval();
         return !!(localToken || sessionToken);
     }
 
+    function startInterval() {
+        stopRenewer();
+        const renewPeriod = +sessionStorage.getItem('renewPeriod');
+        if (!renewPeriod) return;
+        intervalId = setInterval(() => checkValidity(), renewPeriod * 1000);
+    }
+
+    function stopRenewer() {
+        clearInterval(intervalId);
+    }
+
+    async function renewToken() {
+        const _token = getToken();
+        const token = await axios.post('VnUsers/renewToken', {
+            headers: { Authorization: _token },
+        });
+        const _tokenMultimedia = getTokenMultimedia();
+        const tokenMultimedia = await axios.post('VnUsers/renewToken', {
+            headers: { Authorization: _tokenMultimedia },
+        });
+        setToken({ token: token.data.id, tokenMultimedia: tokenMultimedia.data.id });
+    }
+
+    async function checkValidity() {
+        const { getTokenConfig } = useState();
+
+        const tokenConfig = getTokenConfig() ?? sessionStorage.getItem('tokenConfig');
+        const storage = keepLogin() ? localStorage : sessionStorage;
+
+        const created = +storage.getItem('created');
+        const ttl = +storage.getItem('ttl');
+
+        if (isCheckingToken || !created) return;
+        isCheckingToken = true;
+
+        const renewPeriodInSeconds = Math.min(ttl, tokenConfig.value.renewPeriod) * 1000;
+
+        const maxDate = created + renewPeriodInSeconds;
+        const now = new Date().getTime();
+
+        if (isNaN(renewPeriodInSeconds) || now <= maxDate) {
+            return (isCheckingToken = false);
+        }
+
+        await renewToken();
+        isCheckingToken = false;
+    }
+
     return {
         getToken,
         getTokenMultimedia,
@@ -82,5 +156,8 @@ export function useSession() {
         destroy,
         login,
         isLoggedIn,
+        checkValidity,
+        setSession,
+        renewToken,
     };
 }
diff --git a/src/composables/useState.js b/src/composables/useState.js
index e0b742a73..e671d41bd 100644
--- a/src/composables/useState.js
+++ b/src/composables/useState.js
@@ -13,6 +13,7 @@ const user = ref({
 });
 
 const roles = ref([]);
+const tokenConfig = ref({});
 const drawer = ref(true);
 const headerMounted = ref(false);
 
@@ -52,6 +53,15 @@ export function useState() {
     function setRoles(data) {
         roles.value = data;
     }
+    function getTokenConfig() {
+        return computed(() => {
+            return tokenConfig.value;
+        });
+    }
+
+    function setTokenConfig(data) {
+        tokenConfig.value = data;
+    }
 
     function set(name, data) {
         state.value[name] = ref(data);
@@ -70,6 +80,8 @@ export function useState() {
         setUser,
         getRoles,
         setRoles,
+        getTokenConfig,
+        setTokenConfig,
         set,
         get,
         unset,
diff --git a/src/composables/useTokenConfig.js b/src/composables/useTokenConfig.js
new file mode 100644
index 000000000..5cf1b34ee
--- /dev/null
+++ b/src/composables/useTokenConfig.js
@@ -0,0 +1,28 @@
+import axios from 'axios';
+import { useState } from './useState';
+import useNotify from './useNotify';
+
+export function useTokenConfig() {
+    const state = useState();
+    const { notify } = useNotify();
+
+    async function fetch() {
+        try {
+            const { data } = await axios.get('AccessTokenConfigs/findOne', {
+                filter: { fields: ['renewInterval', 'renewPeriod'] },
+            });
+            if (!data) return;
+            state.setTokenConfig(data);
+            sessionStorage.setItem('renewPeriod', data.renewPeriod);
+            return data;
+        } catch (error) {
+            notify('errors.tokenConfig', 'negative');
+            console.error('Error fetching token config:', error);
+        }
+    }
+
+    return {
+        fetch,
+        state,
+    };
+}
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index abe59fe18..96adf0e23 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -106,6 +106,7 @@ errors:
     statusBadGateway: It seems that the server has fall down
     statusGatewayTimeout: Could not contact the server
     userConfig: Error fetching user config
+    tokenConfig: Error fetching token config
     writeRequest: The requested operation could not be completed
 login:
     title: Login
@@ -1135,6 +1136,8 @@ item:
         tax: Tax
         barcode: Barcode
         botanical: Botanical
+        itemTypeCreate: New item type
+        family: Item Type
     descriptor:
         item: Item
         buyer: Buyer
@@ -1219,6 +1222,11 @@ item:
         minSalesQuantity: 'Cantidad mínima de venta'
         genus: 'Genus'
         specie: 'Specie'
+item/itemType:
+    pageTitles:
+        itemType: Item type
+        basicData: Basic data
+        summary: Summary
 components:
     topbar: {}
     itemsFilterPanel:
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 06aa057e3..cb2be6dc9 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -106,6 +106,7 @@ errors:
     statusBadGateway: Parece ser que el servidor ha caído
     statusGatewayTimeout: No se ha podido contactar con el servidor
     userConfig: Error al obtener configuración de usuario
+    tokenConfig: Error al obtener configuración de token
     writeRequest: No se pudo completar la operación solicitada
 login:
     title: Inicio de sesión
@@ -1134,6 +1135,8 @@ item:
         botanical: 'Botánico'
         barcode: 'Código de barras'
         log: Historial
+        itemTypeCreate: Nueva familia
+        family: Familia
     descriptor:
         item: Artículo
         buyer: Comprador
@@ -1218,6 +1221,11 @@ item:
         achieved: 'Conseguido'
         concept: 'Concepto'
         state: 'Estado'
+item/itemType:
+    pageTitles:
+        itemType: Familia
+        basicData: Datos básicos
+        summary: Resumen
 components:
     topbar: {}
     itemsFilterPanel:
diff --git a/src/pages/Claim/Card/ClaimBasicData.vue b/src/pages/Claim/Card/ClaimBasicData.vue
index 47d1ac1c6..be2efa31a 100644
--- a/src/pages/Claim/Card/ClaimBasicData.vue
+++ b/src/pages/Claim/Card/ClaimBasicData.vue
@@ -2,7 +2,7 @@
 import { ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-
+import VnSelect from 'src/components/common/VnSelect.vue';
 import FetchData from 'components/FetchData.vue';
 import FormModel from 'components/FormModel.vue';
 import VnRow from 'components/ui/VnRow.vue';
@@ -37,8 +37,6 @@ const claimFilter = {
     ],
 };
 
-const workers = ref([]);
-const workersCopy = ref([]);
 const claimStates = ref([]);
 const claimStatesCopy = ref([]);
 const optionsList = ref([]);
@@ -47,6 +45,7 @@ function setWorkers(data) {
     workers.value = data;
     workersCopy.value = data;
 }
+const workersOptions = ref([]);
 
 function setClaimStates(data) {
     claimStates.value = data;
@@ -68,25 +67,6 @@ async function getEnumValues() {
 
 getEnumValues();
 
-const workerFilter = {
-    options: workers,
-    filterFn: (options, value) => {
-        const search = value.toLowerCase();
-
-        if (value === '') return workersCopy.value;
-
-        return options.value.filter((row) => {
-            const id = row.id;
-            const name = row.name.toLowerCase();
-
-            const idMatches = id == search;
-            const nameMatches = name.indexOf(search) > -1;
-
-            return idMatches || nameMatches;
-        });
-    },
-};
-
 const statesFilter = {
     options: claimStates,
     filterFn: (options, value) => {
@@ -106,7 +86,7 @@ const statesFilter = {
     <FetchData
         url="Workers/activeWithInheritedRole"
         :filter="{ where: { role: 'salesPerson' } }"
-        @on-fetch="setWorkers"
+        @on-fetch="(data) => (workersOptions = data)"
         auto-load
     />
     <FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
@@ -135,18 +115,15 @@ const statesFilter = {
             </VnRow>
             <VnRow class="row q-gutter-md q-mb-md">
                 <div class="col">
-                    <QSelect
+                    <VnSelect
+                        :label="t('claim.basicData.assignedTo')"
                         v-model="data.workerFk"
-                        :options="workers"
+                        :options="workersOptions"
                         option-value="id"
                         option-label="name"
                         emit-value
-                        :label="t('claim.basicData.assignedTo')"
-                        map-options
-                        use-input
-                        @filter="(value, update) => filter(value, update, workerFilter)"
+                        auto-load
                         :rules="validate('claim.claimStateFk')"
-                        :input-debounce="0"
                     >
                         <template #before>
                             <QAvatar color="orange">
@@ -157,7 +134,7 @@ const statesFilter = {
                                 />
                             </QAvatar>
                         </template>
-                    </QSelect>
+                    </VnSelect>
                 </div>
                 <div class="col">
                     <QSelect
diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue
index 21249349f..c2687e0fd 100644
--- a/src/pages/Item/Card/ItemDiary.vue
+++ b/src/pages/Item/Card/ItemDiary.vue
@@ -1 +1,277 @@
-<template>Item diary (CREAR CUANDO SE DESARROLLE EL MODULO DE ITEMS)</template>
+<script setup>
+import { onMounted, computed, onUnmounted, reactive, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRoute } from 'vue-router';
+
+import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
+import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
+import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
+import FetchData from 'components/FetchData.vue';
+import VnSelect from 'src/components/common/VnSelect.vue';
+import VnInputDate from 'src/components/common/VnInputDate.vue';
+
+import { useStateStore } from 'stores/useStateStore';
+import { toDateFormat } from 'src/filters/date.js';
+import { dashIfEmpty } from 'src/filters';
+import { date } from 'quasar';
+import { useState } from 'src/composables/useState';
+
+const { t } = useI18n();
+const route = useRoute();
+const stateStore = useStateStore();
+const state = useState();
+
+const user = state.getUser();
+const today = ref(Date.vnNew());
+const warehousesOptions = ref([]);
+const itemBalancesRef = ref(null);
+const itemsBalanceFilter = reactive({
+    where: { itemFk: route.params.id, warehouseFk: null, date: null },
+});
+const itemBalances = ref([]);
+const warehouseFk = ref(null);
+const _showWhatsBeforeInventory = ref(false);
+
+const columns = computed(() => [
+    {
+        name: 'claim',
+        align: 'left',
+        field: 'itemFk',
+    },
+    {
+        label: t('itemDiary.date'),
+        name: 'date',
+        field: 'shipped',
+        align: 'left',
+    },
+    {
+        label: t('itemDiary.id'),
+        name: 'id',
+        align: 'left',
+    },
+    {
+        label: t('itemDiary.state'),
+        field: 'stateName',
+        name: 'state',
+        align: 'left',
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('itemDiary.reference'),
+        field: 'reference',
+        name: 'reference',
+        align: 'left',
+        format: (val) => dashIfEmpty(val),
+    },
+
+    {
+        label: t('itemDiary.client'),
+        name: 'client',
+        align: 'left',
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('itemDiary.in'),
+        field: 'invalue',
+        name: 'in',
+        align: 'left',
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('itemDiary.out'),
+        field: 'out',
+        name: 'out',
+        align: 'left',
+        format: (val) => dashIfEmpty(val),
+    },
+    {
+        label: t('itemDiary.balance'),
+        name: 'balance',
+        align: 'left',
+    },
+]);
+
+const showWhatsBeforeInventory = computed({
+    get: () => _showWhatsBeforeInventory.value,
+    set: (val) => {
+        _showWhatsBeforeInventory.value = val;
+        if (!val) itemsBalanceFilter.where.date = null;
+        else itemsBalanceFilter.where.date = new Date();
+    },
+});
+
+const fetchItemBalances = async () => await itemBalancesRef.value.fetch();
+
+const getBadgeAttrs = (_date) => {
+    const isSameDate = date.isSameDate(today.value, _date);
+    const attrs = {
+        'text-color': isSameDate ? 'black' : 'white',
+        color: isSameDate ? 'warning' : 'transparent',
+    };
+    return attrs;
+};
+
+const getIdDescriptor = (row) => {
+    let descriptor = EntryDescriptorProxy;
+    if (row.isTicket) descriptor = TicketDescriptorProxy;
+    return descriptor;
+};
+
+onMounted(async () => {
+    today.value.setHours(0, 0, 0, 0);
+    if (route.query.warehouseFk) warehouseFk.value = route.query.warehouseFk;
+    else if (user.value) warehouseFk.value = user.value.warehouseFk;
+    itemsBalanceFilter.where.warehouseFk = warehouseFk.value;
+    await fetchItemBalances();
+});
+
+onUnmounted(() => (stateStore.rightDrawer = false));
+</script>
+
+<template>
+    <FetchData
+        ref="itemBalancesRef"
+        url="Items/getBalance"
+        :filter="itemsBalanceFilter"
+        @on-fetch="(data) => (itemBalances = data)"
+    />
+    <FetchData
+        url="Warehouses"
+        :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
+        auto-load
+        @on-fetch="(data) => (warehousesOptions = data)"
+    />
+    <QToolbar class="justify-end">
+        <div id="st-data" class="row">
+            <VnSelect
+                :label="t('itemDiary.warehouse')"
+                :options="warehousesOptions"
+                hide-selected
+                option-label="name"
+                option-value="id"
+                dense
+                v-model="itemsBalanceFilter.where.warehouseFk"
+                @update:model-value="fetchItemBalances"
+                class="q-mr-lg"
+            />
+            <QCheckbox
+                :label="t('itemDiary.showBefore')"
+                v-model="showWhatsBeforeInventory"
+                @update:model-value="fetchItemBalances"
+                class="q-mr-lg"
+            />
+            <VnInputDate
+                v-if="showWhatsBeforeInventory"
+                :label="t('itemDiary.since')"
+                dense
+                v-model="itemsBalanceFilter.where.date"
+                @update:model-value="fetchItemBalances"
+            />
+        </div>
+        <QSpace />
+        <div id="st-actions"></div>
+    </QToolbar>
+    <QPage class="column items-center q-pa-md">
+        <QTable
+            :rows="itemBalances"
+            :columns="columns"
+            class="full-width q-mt-md"
+            :no-data-label="t('globals.noResults')"
+        >
+            <template #body-cell-claim="{ row }">
+                <QTd @click.stop>
+                    <QBtn
+                        v-show="row.claimFk"
+                        flat
+                        color="primary"
+                        :to="{ name: 'ClaimSummary', params: { id: row.claimFk } }"
+                        icon="vn:claims"
+                        dense
+                    >
+                        <QTooltip>
+                            {{ t('itemDiary.claim') }}: {{ row.claimFk }}
+                        </QTooltip>
+                    </QBtn>
+                </QTd>
+            </template>
+            <template #body-cell-date="{ row }">
+                <QTd @click.stop>
+                    <QBadge
+                        v-bind="getBadgeAttrs(row.shipped)"
+                        class="q-ma-none"
+                        dense
+                        style="font-size: 14px"
+                    >
+                        {{ toDateFormat(row.shipped) }}
+                    </QBadge>
+                </QTd>
+            </template>
+            <template #body-cell-id="{ row }">
+                <QTd @click.stop>
+                    <component
+                        :is="getIdDescriptor(row)"
+                        :id="row.origin"
+                        class="q-ma-none"
+                        dense
+                        style="font-size: 14px"
+                    >
+                        {{ row.origin }}
+                    </component>
+                    <span class="link">{{ row.origin }}</span>
+                </QTd>
+            </template>
+            <template #body-cell-client="{ row }">
+                <QTd @click.stop>
+                    <QBadge
+                        :color="row.highlighted ? 'warning' : 'transparent'"
+                        :text-color="row.highlighted ? 'black' : 'white'"
+                        dense
+                        style="font-size: 14px"
+                    >
+                        <span v-if="row.isTicket" class="link">
+                            {{ dashIfEmpty(row.name) }}
+                            <CustomerDescriptorProxy :id="row.clientFk" />
+                        </span>
+                        <span v-else>{{ dashIfEmpty(row.name) }}</span>
+                    </QBadge>
+                </QTd>
+            </template>
+            <template #body-cell-in="{ row }">
+                <QTd @click.stop>
+                    <span :class="{ 'is-in': row.invalue }">
+                        {{ dashIfEmpty(row.invalue) }}
+                    </span>
+                </QTd>
+            </template>
+            <template #body-cell-balance="{ row }">
+                <QTd @click.stop>
+                    <QBadge
+                        class="balance-negative"
+                        :color="
+                            row.lineFk == row.lastPreparedLineFk
+                                ? 'grey-13'
+                                : 'transparent'
+                        "
+                        :text-color="
+                            row.lineFk == row.lastPreparedLineFk
+                                ? 'black'
+                                : row.balance < 0
+                                ? 'negative'
+                                : ''
+                        "
+                        dense
+                        style="font-size: 14px"
+                    >
+                        <span>{{ dashIfEmpty(row.balance) }}</span>
+                    </QBadge>
+                </QTd>
+            </template>
+        </QTable>
+    </QPage>
+</template>
+
+<style lang="scss" scoped>
+.is-in {
+    color: $positive;
+}
+</style>
diff --git a/src/pages/Item/ItemTypeCreate.vue b/src/pages/Item/ItemTypeCreate.vue
new file mode 100644
index 000000000..af3dc34ea
--- /dev/null
+++ b/src/pages/Item/ItemTypeCreate.vue
@@ -0,0 +1,91 @@
+<script setup>
+import { reactive, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRouter } from 'vue-router';
+
+import FetchData from 'components/FetchData.vue';
+import VnSelect from 'src/components/common/VnSelect.vue';
+import FormModel from 'components/FormModel.vue';
+import VnRow from 'components/ui/VnRow.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+
+const { t } = useI18n();
+const router = useRouter();
+
+const newItemTypeForm = reactive({});
+
+const workersOptions = ref([]);
+const categoriesOptions = ref([]);
+const temperaturesOptions = ref([]);
+
+const redirectToItemTypeBasicData = (_, { id }) => {
+    router.push({ name: 'ItemTypeBasicData', params: { id } });
+};
+</script>
+
+<template>
+    <FetchData
+        url="Workers"
+        @on-fetch="(data) => (workersOptions = data)"
+        :filter="{ order: 'firstName ASC', fields: ['id', 'firstName'] }"
+        auto-load
+    />
+    <FetchData
+        url="ItemCategories"
+        @on-fetch="(data) => (categoriesOptions = data)"
+        :filter="{ order: 'name ASC', fields: ['id', 'name'] }"
+        auto-load
+    />
+    <FetchData
+        url="Temperatures"
+        @on-fetch="(data) => (temperaturesOptions = data)"
+        :filter="{ order: 'name ASC', fields: ['code', 'name'] }"
+        auto-load
+    />
+    <QPage>
+        <VnSubToolbar />
+        <FormModel
+            url-create="ItemTypes"
+            model="itemTypeCreate"
+            :form-initial-data="newItemTypeForm"
+            observe-form-changes
+            @on-data-saved="redirectToItemTypeBasicData"
+        >
+            <template #form="{ data }">
+                <VnRow class="row q-gutter-md q-mb-md">
+                    <VnInput v-model="data.code" :label="t('itemType.shared.code')" />
+                    <VnInput v-model="data.name" :label="t('itemType.shared.name')" />
+                </VnRow>
+                <VnRow class="row q-gutter-md q-mb-md">
+                    <VnSelect
+                        v-model="data.workerFk"
+                        :label="t('itemType.shared.worker')"
+                        :options="workersOptions"
+                        option-value="id"
+                        option-label="firstName"
+                        hide-selected
+                    />
+                    <VnSelect
+                        v-model="data.categoryFk"
+                        :label="t('itemType.shared.category')"
+                        :options="categoriesOptions"
+                        option-value="id"
+                        option-label="name"
+                        hide-selected
+                    />
+                </VnRow>
+                <VnRow class="row q-gutter-md q-mb-md">
+                    <VnSelect
+                        v-model="data.temperatureFk"
+                        :label="t('itemType.shared.temperature')"
+                        :options="temperaturesOptions"
+                        option-value="code"
+                        option-label="name"
+                        hide-selected
+                    />
+                </VnRow>
+            </template>
+        </FormModel>
+    </QPage>
+</template>
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
new file mode 100644
index 000000000..9d826655f
--- /dev/null
+++ b/src/pages/Item/ItemTypeList.vue
@@ -0,0 +1,142 @@
+<script setup>
+import { useI18n } from 'vue-i18n';
+import { useRouter } from 'vue-router';
+
+import VnPaginate from 'src/components/ui/VnPaginate.vue';
+import VnLv from 'src/components/ui/VnLv.vue';
+import CardList from 'src/components/ui/CardList.vue';
+import ItemTypeSummary from 'src/pages/ItemType/Card/ItemTypeSummary.vue';
+import ItemTypeFilter from 'src/pages/ItemType/ItemTypeFilter.vue';
+import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
+
+import { useStateStore } from 'stores/useStateStore';
+import { useSummaryDialog } from 'src/composables/useSummaryDialog';
+
+const stateStore = useStateStore();
+const router = useRouter();
+const { t } = useI18n();
+const { viewSummary } = useSummaryDialog();
+
+const redirectToItemTypeSummary = (id) => {
+    router.push({ name: 'ItemTypeSummary', params: { id } });
+};
+
+const redirectToCreateView = () => {
+    router.push({ name: 'ItemTypeCreate' });
+};
+
+const exprBuilder = (param, value) => {
+    switch (param) {
+        case 'name':
+            return {
+                name: { like: `%${value}%` },
+            };
+        case 'code':
+            return {
+                code: { like: `%${value}%` },
+            };
+        case 'search':
+            if (value) {
+                if (!isNaN(value)) {
+                    return { id: value };
+                } else {
+                    return {
+                        or: [
+                            {
+                                name: {
+                                    like: `%${value}%`,
+                                },
+                            },
+                            {
+                                code: {
+                                    like: `%${value}%`,
+                                },
+                            },
+                        ],
+                    };
+                }
+            }
+    }
+};
+</script>
+
+<template>
+    <template v-if="stateStore.isHeaderMounted()">
+        <Teleport to="#searchbar">
+            <VnSearchbar
+                data-key="ItemTypeList"
+                url="ItemTypes"
+                :label="t('Search item type')"
+                :info="t('Search itemType by id, name or code')"
+                :expr-builder="exprBuilder"
+            />
+        </Teleport>
+        <Teleport to="#actions-append">
+            <div class="row q-gutter-x-sm">
+                <QBtn
+                    flat
+                    @click="stateStore.toggleRightDrawer()"
+                    round
+                    dense
+                    icon="menu"
+                >
+                    <QTooltip bottom anchor="bottom right">
+                        {{ t('globals.collapseMenu') }}
+                    </QTooltip>
+                </QBtn>
+            </div>
+        </Teleport>
+    </template>
+    <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
+        <QScrollArea class="fit text-grey-8">
+            <ItemTypeFilter data-key="ItemTypeList" />
+        </QScrollArea>
+    </QDrawer>
+    <QPage class="column items-center q-pa-md">
+        <div class="vn-card-list">
+            <VnPaginate
+                data-key="ItemTypeList"
+                url="ItemTypes"
+                :order="['name']"
+                auto-load
+                :expr-builder="exprBuilder"
+            >
+                <template #body="{ rows }">
+                    <CardList
+                        v-for="row of rows"
+                        :key="row.id"
+                        :title="row.code"
+                        @click="redirectToItemTypeSummary(row.id)"
+                        :id="row.id"
+                    >
+                        <template #list-items>
+                            <VnLv :label="t('Name')" :value="row.name" />
+                        </template>
+                        <template #actions>
+                            <QBtn
+                                :label="t('components.smartCard.openSummary')"
+                                @click.stop="viewSummary(row.id, ItemTypeSummary)"
+                                color="primary"
+                                type="submit"
+                            />
+                        </template>
+                    </CardList>
+                </template>
+            </VnPaginate>
+        </div>
+    </QPage>
+    <QPageSticky :offset="[20, 20]">
+        <QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
+        <QTooltip>
+            {{ t('New item type') }}
+        </QTooltip>
+    </QPageSticky>
+</template>
+
+<i18n>
+es:
+    New item type: Nueva familia
+    Name: Nombre
+    Search item type: Buscar familia
+    Search itemType by id, name or code: Buscar familia por id, nombre o código
+</i18n>
diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml
new file mode 100644
index 000000000..ec3b134e8
--- /dev/null
+++ b/src/pages/Item/locale/en.yml
@@ -0,0 +1,13 @@
+itemDiary:
+    date: Date
+    id: Id
+    state: State
+    reference: Reference
+    client: Client
+    in: In
+    out: Out
+    balance: Balance
+    claim: Claim
+    showBefore: Show what's before the inventory
+    since: Since
+    warehouse: Warehouse
diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml
new file mode 100644
index 000000000..4f76313fa
--- /dev/null
+++ b/src/pages/Item/locale/es.yml
@@ -0,0 +1,13 @@
+itemDiary:
+    date: Fecha
+    id: Id
+    state: Estado
+    reference: Referencia
+    client: Cliente
+    in: Entrada
+    out: Salida
+    balance: Balance
+    claim: Reclamación
+    showBefore: Mostrar lo anterior al inventario
+    since: Desde
+    warehouse: Almacén
diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue
new file mode 100644
index 000000000..0bdfab951
--- /dev/null
+++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue
@@ -0,0 +1,79 @@
+<script setup>
+import { ref } from 'vue';
+import { useRoute } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+
+import FetchData from 'components/FetchData.vue';
+import FormModel from 'components/FormModel.vue';
+import VnRow from 'components/ui/VnRow.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import VnSelect from 'src/components/common/VnSelect.vue';
+
+const route = useRoute();
+const { t } = useI18n();
+
+const workersOptions = ref([]);
+const categoriesOptions = ref([]);
+const temperaturesOptions = ref([]);
+</script>
+<template>
+    <FetchData
+        url="Workers"
+        @on-fetch="(data) => (workersOptions = data)"
+        :filter="{ order: 'firstName ASC', fields: ['id', 'firstName'] }"
+        auto-load
+    />
+    <FetchData
+        url="ItemCategories"
+        @on-fetch="(data) => (categoriesOptions = data)"
+        :filter="{ order: 'name ASC', fields: ['id', 'name'] }"
+        auto-load
+    />
+    <FetchData
+        url="Temperatures"
+        @on-fetch="(data) => (temperaturesOptions = data)"
+        :filter="{ order: 'name ASC', fields: ['code', 'name'] }"
+        auto-load
+    />
+    <FormModel
+        :url="`ItemTypes/${route.params.id}`"
+        :url-update="`ItemTypes/${route.params.id}`"
+        model="itemTypeBasicData"
+        auto-load
+    >
+        <template #form="{ data }">
+            <VnRow class="row q-gutter-md q-mb-md">
+                <VnInput v-model="data.code" :label="t('shared.code')" />
+                <VnInput v-model="data.name" :label="t('shared.name')" />
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <VnSelect
+                    v-model="data.workerFk"
+                    :label="t('shared.worker')"
+                    :options="workersOptions"
+                    option-value="id"
+                    option-label="firstName"
+                    hide-selected
+                />
+                <VnSelect
+                    v-model="data.categoryFk"
+                    :label="t('shared.category')"
+                    :options="categoriesOptions"
+                    option-value="id"
+                    option-label="name"
+                    hide-selected
+                />
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <VnSelect
+                    v-model="data.temperatureFk"
+                    :label="t('shared.temperature')"
+                    :options="temperaturesOptions"
+                    option-value="code"
+                    option-label="name"
+                    hide-selected
+                />
+            </VnRow>
+        </template>
+    </FormModel>
+</template>
diff --git a/src/pages/ItemType/Card/ItemTypeCard.vue b/src/pages/ItemType/Card/ItemTypeCard.vue
new file mode 100644
index 000000000..cb8adf7f6
--- /dev/null
+++ b/src/pages/ItemType/Card/ItemTypeCard.vue
@@ -0,0 +1,12 @@
+<script setup>
+import VnCard from 'components/common/VnCard.vue';
+
+import ItemTypeDescriptor from 'src/pages/ItemType/Card/ItemTypeDescriptor.vue';
+</script>
+<template>
+    <VnCard
+        data-key="ItemTypeSummary"
+        base-url="ItemTypes"
+        :descriptor="ItemTypeDescriptor"
+    />
+</template>
diff --git a/src/pages/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/ItemType/Card/ItemTypeDescriptor.vue
new file mode 100644
index 000000000..e565a791b
--- /dev/null
+++ b/src/pages/ItemType/Card/ItemTypeDescriptor.vue
@@ -0,0 +1,85 @@
+<script setup>
+import { ref, computed } from 'vue';
+import { useRoute } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+
+import CardDescriptor from 'components/ui/CardDescriptor.vue';
+import VnLv from 'src/components/ui/VnLv.vue';
+import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
+
+import useCardDescription from 'src/composables/useCardDescription';
+
+const $props = defineProps({
+    id: {
+        type: Number,
+        required: false,
+        default: null,
+    },
+    summary: {
+        type: Object,
+        default: null,
+    },
+});
+
+const route = useRoute();
+const { t } = useI18n();
+
+const itemTypeFilter = {
+    include: [
+        { relation: 'worker' },
+        { relation: 'category' },
+        { relation: 'itemPackingType' },
+        { relation: 'temperature' },
+    ],
+};
+
+const entityId = computed(() => {
+    return $props.id || route.params.id;
+});
+
+const data = ref(useCardDescription());
+const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
+</script>
+
+<template>
+    <CardDescriptor
+        module="ItemType"
+        :url="`ItemTypes/${entityId}`"
+        :filter="itemTypeFilter"
+        :title="data.title"
+        :subtitle="data.subtitle"
+        @on-fetch="setData"
+        data-key="entry"
+    >
+        <template #header-extra-action>
+            <QBtn
+                round
+                flat
+                size="sm"
+                icon="vn:item"
+                color="white"
+                :to="{ name: 'ItemTypeList' }"
+            >
+                <QTooltip>
+                    {{ t('Go to module index') }}
+                </QTooltip>
+            </QBtn>
+        </template>
+        <template #body="{ entity }">
+            <VnLv :label="t('shared.code')" :value="entity.code" />
+            <VnLv :label="t('shared.name')" :value="entity.name" />
+            <VnLv :label="t('shared.worker')">
+                <template #value>
+                    <span class="link">{{ entity.worker?.firstName }}</span>
+                    <WorkerDescriptorProxy :id="entity.worker?.id" />
+                </template>
+            </VnLv>
+            <VnLv :label="t('shared.category')" :value="entity.category?.name" />
+        </template>
+    </CardDescriptor>
+</template>
+
+<i18n>
+es:
+    Go to module index: Ir al índice del módulo
+</i18n>
diff --git a/src/pages/ItemType/Card/ItemTypeSummary.vue b/src/pages/ItemType/Card/ItemTypeSummary.vue
new file mode 100644
index 000000000..62d1c74ab
--- /dev/null
+++ b/src/pages/ItemType/Card/ItemTypeSummary.vue
@@ -0,0 +1,109 @@
+<script setup>
+import { ref, computed, onUpdated } from 'vue';
+import { useRoute } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
+
+import CardSummary from 'components/ui/CardSummary.vue';
+import VnLv from 'src/components/ui/VnLv.vue';
+
+onUpdated(() => summaryRef.value.fetch());
+
+const route = useRoute();
+const { t } = useI18n();
+
+const $props = defineProps({
+    id: {
+        type: Number,
+        required: false,
+        default: null,
+    },
+});
+
+const itemTypeFilter = {
+    include: [
+        { relation: 'worker' },
+        { relation: 'category' },
+        { relation: 'itemPackingType' },
+        { relation: 'temperature' },
+    ],
+};
+
+const entityId = computed(() => $props.id || route.params.id);
+const summaryRef = ref();
+const itemType = ref();
+
+async function setItemTypeData(data) {
+    if (data) itemType.value = data;
+}
+</script>
+
+<template>
+    <CardSummary
+        ref="summaryRef"
+        :url="`ItemTypes/${entityId}`"
+        data-key="ItemTypeSummary"
+        :filter="itemTypeFilter"
+        @on-fetch="(data) => setItemTypeData(data)"
+        class="full-width"
+    >
+        <template #header-left>
+            <router-link
+                v-if="route.name !== 'ItemTypeSummary'"
+                :to="{ name: 'ItemTypeSummary', params: { id: entityId } }"
+                class="header link"
+            >
+                <QIcon name="open_in_new" color="white" size="sm" />
+            </router-link>
+        </template>
+        <template #header>
+            <span>
+                {{ itemType.id }} - {{ itemType.name }} -
+                {{ itemType.worker?.firstName }}
+                {{ itemType.worker?.lastName }}
+            </span>
+        </template>
+        <template #body>
+            <QCard class="vn-one">
+                <router-link
+                    :to="{ name: 'ItemTypeBasicData', params: { id: entityId } }"
+                    class="header header-link"
+                >
+                    {{ t('globals.summary.basicData') }}
+                    <QIcon name="open_in_new" />
+                </router-link>
+                <VnLv :label="t('summary.id')" :value="itemType.id" />
+                <VnLv :label="t('shared.code')" :value="itemType.code" />
+                <VnLv :label="t('shared.name')" :value="itemType.name" />
+                <VnLv :label="t('shared.worker')">
+                    <template #value>
+                        <span class="link">{{ itemType.worker?.firstName }}</span>
+                        <WorkerDescriptorProxy :id="itemType.worker?.id" />
+                    </template>
+                </VnLv>
+                <VnLv :label="t('shared.category')" :value="itemType.category?.name" />
+                <VnLv
+                    :label="t('shared.temperature')"
+                    :value="itemType.temperature?.name"
+                />
+                <VnLv :label="t('summary.life')" :value="itemType.life" />
+                <VnLv :label="t('summary.promo')" :value="itemType.promo" />
+                <VnLv
+                    :label="t('summary.itemPackingType')"
+                    :value="itemType.itemPackingType?.description"
+                />
+                <VnLv
+                    class="large-label"
+                    :label="t('summary.isUnconventionalSize')"
+                    :value="itemType.isUnconventionalSize"
+                />
+            </QCard>
+        </template>
+    </CardSummary>
+</template>
+
+<style lang="scss">
+.large-label > div.label {
+    width: 15em !important;
+}
+</style>
diff --git a/src/pages/ItemType/ItemTypeFilter.vue b/src/pages/ItemType/ItemTypeFilter.vue
new file mode 100644
index 000000000..2a86795c2
--- /dev/null
+++ b/src/pages/ItemType/ItemTypeFilter.vue
@@ -0,0 +1,55 @@
+<script setup>
+import { useI18n } from 'vue-i18n';
+import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+const { t } = useI18n();
+
+const props = defineProps({
+    dataKey: {
+        type: String,
+        required: true,
+    },
+});
+
+const emit = defineEmits(['search']);
+</script>
+
+<template>
+    <VnFilterPanel
+        :data-key="props.dataKey"
+        :search-button="true"
+        @search="emit('search')"
+    >
+        <template #tags="{ tag, formatFn }">
+            <div class="q-gutter-x-xs">
+                <strong>{{ t(`params.${tag.label}`) }}: </strong>
+                <span>{{ formatFn(tag.value) }}</span>
+            </div>
+        </template>
+        <template #body="{ params }">
+            <QItem>
+                <QItemSection>
+                    <VnInput :label="t('Name')" v-model="params.name" is-outlined />
+                </QItemSection>
+            </QItem>
+            <QItem>
+                <QItemSection>
+                    <VnInput v-model="params.code" :label="t('Code')" is-outlined />
+                </QItemSection>
+            </QItem>
+        </template>
+    </VnFilterPanel>
+</template>
+
+<i18n>
+en:
+    params:
+        name: Name
+        code: Code
+es:
+    params:
+        name: Nombre
+        code: Código
+    Name: Nombre
+    Code: Código
+</i18n>
diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/ItemType/locale/en.yml
new file mode 100644
index 000000000..7889418ea
--- /dev/null
+++ b/src/pages/ItemType/locale/en.yml
@@ -0,0 +1,12 @@
+shared:
+    code: Code
+    name: Name
+    worker: Worker
+    category: Category
+    temperature: Temperature
+summary:
+    id: id
+    life: Life
+    promo: Promo
+    itemPackingType: Item packing type
+    isUnconventionalSize: Is unconventional size
diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/ItemType/locale/es.yml
new file mode 100644
index 000000000..9a94dceb6
--- /dev/null
+++ b/src/pages/ItemType/locale/es.yml
@@ -0,0 +1,17 @@
+shared:
+    code: Código
+    name: Nombre
+    worker: Trabajador
+    category: Reino
+    temperature: Temperatura
+summary:
+    id: id
+    code: Código
+    name: Nombre
+    worker: Trabajador
+    category: Reino
+    temperature: Temperatura
+    life: Vida
+    promo: Promoción
+    itemPackingType: Tipo de embalaje
+    isUnconventionalSize: Es de tamaño poco convencional
diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue
index fcde51edf..5a3490f50 100644
--- a/src/pages/Login/LoginMain.vue
+++ b/src/pages/Login/LoginMain.vue
@@ -38,7 +38,13 @@ async function onSubmit() {
 
         if (!multimediaToken) return;
 
-        await session.login(data.token, multimediaToken.id, keepLogin.value);
+        const login = {
+            ...data,
+            created: Date.now(),
+            tokenMultimedia: multimediaToken.id,
+            keepLogin: keepLogin.value,
+        };
+        await session.login(login);
 
         quasar.notify({
             message: t('login.loginSuccess'),
diff --git a/src/pages/Shelving/Card/ShelvingSummary.vue b/src/pages/Shelving/Card/ShelvingSummary.vue
index 49470719e..649799b00 100644
--- a/src/pages/Shelving/Card/ShelvingSummary.vue
+++ b/src/pages/Shelving/Card/ShelvingSummary.vue
@@ -20,14 +20,14 @@ const router = useRouter();
 const { t } = useI18n();
 
 const entityId = computed(() => $props.id || route.params.id);
-const isDialog = Boolean($props.id);
-const hideRightDrawer = () => {
-    if (!isDialog) {
-        stateStore.rightDrawer = false;
-    }
-};
-onMounted(hideRightDrawer);
-onUnmounted(hideRightDrawer);
+const isDialog = false;
+// const hideRightDrawer = () => {
+//     if (!isDialog) {
+//         stateStore.rightDrawer = false;
+//     }
+// };
+// onMounted(hideRightDrawer);
+// onUnmounted(hideRightDrawer);
 const filter = {
     include: [
         {
@@ -46,23 +46,6 @@ const filter = {
 </script>
 
 <template>
-    <template v-if="!isDialog && stateStore.isHeaderMounted()">
-        <Teleport to="#actions-append">
-            <div class="row q-gutter-x-sm">
-                <QBtn
-                    flat
-                    @click="stateStore.toggleRightDrawer()"
-                    round
-                    dense
-                    icon="menu"
-                >
-                    <QTooltip bottom anchor="bottom right">
-                        {{ t('globals.collapseMenu') }}
-                    </QTooltip>
-                </QBtn>
-            </div>
-        </Teleport>
-    </template>
     <div class="q-pa-md">
         <CardSummary ref="summary" :url="`Shelvings/${entityId}`" :filter="filter">
             <template #header="{ entity }">
@@ -102,18 +85,4 @@ const filter = {
             </template>
         </CardSummary>
     </div>
-    <QDrawer
-        v-if="!isDialog"
-        v-model="stateStore.rightDrawer"
-        side="right"
-        :width="256"
-        show-if-above
-    >
-        <QScrollArea class="fit text-grey-8">
-            <ShelvingFilter
-                data-key="ShelvingList"
-                @search="router.push({ name: 'ShelvingList' })"
-            />
-        </QScrollArea>
-    </QDrawer>
 </template>
diff --git a/src/router/index.js b/src/router/index.js
index 3e442f0e6..7a0aedcae 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -12,6 +12,7 @@ import { useSession } from 'src/composables/useSession';
 import { useRole } from 'src/composables/useRole';
 import { useUserConfig } from 'src/composables/useUserConfig';
 import { toLowerCamel } from 'src/filters';
+import { useTokenConfig } from 'src/composables/useTokenConfig';
 
 const state = useState();
 const session = useSession();
@@ -55,6 +56,7 @@ export default route(function (/* { store, ssrContext } */) {
             if (stateRoles.length === 0) {
                 await useRole().fetch();
                 await useUserConfig().fetch();
+                await useTokenConfig().fetch();
             }
             const matches = to.matched;
             const hasRequiredRoles = matches.every((route) => {
diff --git a/src/router/modules/department.js b/src/router/modules/department.js
index aaffc3460..dfd5e64ba 100644
--- a/src/router/modules/department.js
+++ b/src/router/modules/department.js
@@ -6,6 +6,7 @@ export default {
     meta: {
         title: 'department',
         icon: 'vn:greuge',
+        moduleName: 'Department',
     },
     component: RouterView,
     redirect: { name: 'DepartmentCard' },
diff --git a/src/router/modules/index.js b/src/router/modules/index.js
index 6f4b0b35e..941358d26 100644
--- a/src/router/modules/index.js
+++ b/src/router/modules/index.js
@@ -16,6 +16,7 @@ import Entry from './entry';
 import roadmap from './roadmap';
 import Parking from './parking';
 import Agency from './agency';
+import ItemType from './itemType';
 
 export default [
     Item,
@@ -36,4 +37,5 @@ export default [
     roadmap,
     Parking,
     Agency,
+    ItemType,
 ];
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index bc1e72a94..d79ac3071 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -11,7 +11,13 @@ export default {
     component: RouterView,
     redirect: { name: 'ItemMain' },
     menus: {
-        main: ['ItemList', 'WasteBreakdown', 'ItemFixedPrice', 'ItemRequest'],
+        main: [
+            'ItemList',
+            'WasteBreakdown',
+            'ItemFixedPrice',
+            'ItemRequest',
+            'ItemTypeList',
+        ],
         card: [
             'ItemBasicData',
             'ItemLog',
@@ -68,6 +74,23 @@ export default {
                             'https://grafana.verdnatura.es/d/TTNXQAxVk';
                     },
                 },
+                {
+                    path: 'item-type-list',
+                    name: 'ItemTypeList',
+                    meta: {
+                        title: 'family',
+                        icon: 'contact_support',
+                    },
+                    component: () => import('src/pages/Item/ItemTypeList.vue'),
+                },
+                {
+                    path: 'item-type-list/create',
+                    name: 'ItemTypeCreate',
+                    meta: {
+                        title: 'itemTypeCreate',
+                    },
+                    component: () => import('src/pages/Item/ItemTypeCreate.vue'),
+                },
                 {
                     path: 'request',
                     name: 'ItemRequest',
@@ -134,8 +157,8 @@ export default {
                     path: 'diary',
                     name: 'ItemDiary',
                     meta: {
-                        title: 'basicData',
-                        icon: 'vn:settings',
+                        title: 'diary',
+                        icon: 'vn:transaction',
                     },
                     component: () => import('src/pages/Item/Card/ItemDiary.vue'),
                 },
@@ -144,7 +167,7 @@ export default {
                     name: 'ItemLog',
                     meta: {
                         title: 'log',
-                        icon: 'history',
+                        icon: 'vn:History',
                     },
                     component: () => import('src/pages/Item/Card/ItemLog.vue'),
                 },
diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js
new file mode 100644
index 000000000..8064c41ff
--- /dev/null
+++ b/src/router/modules/itemType.js
@@ -0,0 +1,46 @@
+import { RouterView } from 'vue-router';
+
+export default {
+    path: '/item/item-type',
+    name: 'ItemType',
+    meta: {
+        title: 'itemType',
+        icon: 'contact_support',
+        moduleName: 'ItemType',
+    },
+    component: RouterView,
+    redirect: { name: 'ItemTypeList' },
+    menus: {
+        main: [],
+        card: ['ItemTypeBasicData'],
+    },
+    children: [
+        {
+            name: 'ItemTypeCard',
+            path: ':id',
+            component: () => import('src/pages/ItemType/Card/ItemTypeCard.vue'),
+            redirect: { name: 'ItemTypeSummary' },
+            children: [
+                {
+                    name: 'ItemTypeSummary',
+                    path: 'summary',
+                    meta: {
+                        title: 'summary',
+                    },
+                    component: () =>
+                        import('src/pages/ItemType/Card/ItemTypeSummary.vue'),
+                },
+                {
+                    name: 'ItemTypeBasicData',
+                    path: 'basic-data',
+                    meta: {
+                        title: 'basicData',
+                        icon: 'vn:settings',
+                    },
+                    component: () =>
+                        import('src/pages/ItemType/Card/ItemTypeBasicData.vue'),
+                },
+            ],
+        },
+    ],
+};
diff --git a/src/router/modules/roadmap.js b/src/router/modules/roadmap.js
index 02edf94be..6b2aa6a13 100644
--- a/src/router/modules/roadmap.js
+++ b/src/router/modules/roadmap.js
@@ -6,6 +6,7 @@ export default {
     meta: {
         title: 'roadmap',
         icon: 'vn:troncales',
+        moduleName: 'Roadmap',
     },
     component: RouterView,
     redirect: { name: 'RouteMain' },
diff --git a/src/router/routes.js b/src/router/routes.js
index ca52441e7..92145d44e 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -10,6 +10,7 @@ import supplier from './modules/Supplier';
 import route from './modules/route';
 import travel from './modules/travel';
 import department from './modules/department';
+import ItemType from './modules/itemType';
 import shelving from 'src/router/modules/shelving';
 import order from 'src/router/modules/order';
 import entry from 'src/router/modules/entry';
@@ -73,6 +74,7 @@ const routes = [
             entry,
             parking,
             agency,
+            ItemType,
             {
                 path: '/:catchAll(.*)*',
                 name: 'NotFound',
diff --git a/test/vitest/__tests__/composables/useSession.spec.js b/test/vitest/__tests__/composables/useSession.spec.js
index f9f3dcb80..2292859a9 100644
--- a/test/vitest/__tests__/composables/useSession.spec.js
+++ b/test/vitest/__tests__/composables/useSession.spec.js
@@ -1,5 +1,5 @@
-import { vi, describe, expect, it } from 'vitest';
-import { axios } from 'app/test/vitest/helper';
+import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest';
+import { axios, flushPromises } from 'app/test/vitest/helper';
 import { useSession } from 'composables/useSession';
 import { useState } from 'composables/useState';
 
@@ -63,73 +63,148 @@ describe('session', () => {
         });
     });
 
-    describe('login', () => {
-        const expectedUser = {
-            id: 999,
-            name: `T'Challa`,
-            nickname: 'Black Panther',
-            lang: 'en',
-            userConfig: {
-                darkMode: false,
-            },
-        };
-        const rolesData = [
-            {
-                role: {
-                    name: 'salesPerson',
+    describe(
+        'login',
+        () => {
+            const expectedUser = {
+                id: 999,
+                name: `T'Challa`,
+                nickname: 'Black Panther',
+                lang: 'en',
+                userConfig: {
+                    darkMode: false,
                 },
-            },
-            {
-                role: {
-                    name: 'admin',
+            };
+            const rolesData = [
+                {
+                    role: {
+                        name: 'salesPerson',
+                    },
                 },
-            },
-        ];
+                {
+                    role: {
+                        name: 'admin',
+                    },
+                },
+            ];
 
-        it('should fetch the user roles and then set token in the sessionStorage', async () => {
-            const expectedRoles = ['salesPerson', 'admin'];
-            vi.spyOn(axios, 'get').mockResolvedValue({
-                data: { roles: rolesData, user: expectedUser },
+            it('should fetch the user roles and then set token in the sessionStorage', async () => {
+                const expectedRoles = ['salesPerson', 'admin'];
+                vi.spyOn(axios, 'get').mockResolvedValue({
+                    data: { roles: rolesData, user: expectedUser },
+                });
+
+                const expectedToken = 'mySessionToken';
+                const expectedTokenMultimedia = 'mySessionTokenMultimedia';
+                const keepLogin = false;
+
+                await session.login({
+                    token: expectedToken,
+                    tokenMultimedia: expectedTokenMultimedia,
+                    keepLogin,
+                });
+
+                const roles = state.getRoles();
+                const localToken = localStorage.getItem('token');
+                const sessionToken = sessionStorage.getItem('token');
+
+                expect(roles.value).toEqual(expectedRoles);
+                expect(localToken).toBeNull();
+                expect(sessionToken).toEqual(expectedToken);
+
+                await session.destroy(); // this clears token and user for any other test
             });
 
-            const expectedToken = 'mySessionToken';
-            const expectedTokenMultimedia = 'mySessionTokenMultimedia';
-            const keepLogin = false;
+            it('should fetch the user roles and then set token in the localStorage', async () => {
+                const expectedRoles = ['salesPerson', 'admin'];
+                vi.spyOn(axios, 'get').mockResolvedValue({
+                    data: { roles: rolesData, user: expectedUser },
+                });
 
-            await session.login(expectedToken,expectedTokenMultimedia, keepLogin);
+                const expectedToken = 'myLocalToken';
+                const expectedTokenMultimedia = 'myLocalTokenMultimedia';
+                const keepLogin = true;
 
-            const roles = state.getRoles();
-            const localToken = localStorage.getItem('token');
-            const sessionToken = sessionStorage.getItem('token');
+                await session.login({
+                    token: expectedToken,
+                    tokenMultimedia: expectedTokenMultimedia,
+                    keepLogin,
+                });
 
-            expect(roles.value).toEqual(expectedRoles);
-            expect(localToken).toBeNull();
-            expect(sessionToken).toEqual(expectedToken);
+                const roles = state.getRoles();
+                const localToken = localStorage.getItem('token');
+                const sessionToken = sessionStorage.getItem('token');
 
-           await session.destroy(); // this clears token and user for any other test
+                expect(roles.value).toEqual(expectedRoles);
+                expect(localToken).toEqual(expectedToken);
+                expect(sessionToken).toBeNull();
+
+                await session.destroy(); // this clears token and user for any other test
+            });
+        },
+        {}
+    );
+
+    describe('RenewToken', () => {
+        const expectedToken = 'myToken';
+        const expectedTokenMultimedia = 'myTokenMultimedia';
+        const currentDate = new Date();
+        beforeAll(() => {
+            const tokenConfig = {
+                id: 1,
+                renewPeriod: 21600,
+                courtesyTime: 60,
+                renewInterval: 300,
+            };
+            state.setTokenConfig(tokenConfig);
+            sessionStorage.setItem('renewPeriod', 1);
         });
+        it('NOT Should renewToken', async () => {
+            const data = {
+                token: expectedToken,
+                tokenMultimedia: expectedTokenMultimedia,
+                keepLogin: false,
+                ttl: 1,
+                created: Date.now(),
+            };
+            session.setSession(data);
+            expect(sessionStorage.getItem('keepLogin')).toBeFalsy();
+            expect(sessionStorage.getItem('created')).toBeDefined();
+            expect(sessionStorage.getItem('ttl')).toEqual(1);
+            await session.checkValidity();
+            expect(sessionStorage.getItem('token')).toEqual(expectedToken);
+            expect(sessionStorage.getItem('tokenMultimedia')).toEqual(
+                expectedTokenMultimedia
+            );
+        });
+        it('Should renewToken', async () => {
+            currentDate.setMinutes(currentDate.getMinutes() - 100);
+            const data = {
+                token: expectedToken,
+                tokenMultimedia: expectedTokenMultimedia,
+                keepLogin: false,
+                ttl: 1,
+                created: currentDate,
+            };
+            session.setSession(data);
 
-        it('should fetch the user roles and then set token in the localStorage', async () => {
-            const expectedRoles = ['salesPerson', 'admin'];
-            vi.spyOn(axios, 'get').mockResolvedValue({
-                data: { roles: rolesData, user: expectedUser },
-            });
-
-            const expectedToken = 'myLocalToken';
-            const expectedTokenMultimedia = 'myLocalTokenMultimedia';
-            const keepLogin = true;
-
-            await session.login(expectedToken, expectedTokenMultimedia, keepLogin);
-
-            const roles = state.getRoles();
-            const localToken = localStorage.getItem('token');
-            const sessionToken = sessionStorage.getItem('token');
-
-            expect(roles.value).toEqual(expectedRoles);
-            expect(localToken).toEqual(expectedToken);
-            expect(sessionToken).toBeNull();
-
-            await session.destroy(); // this clears token and user for any other test
+            vi.spyOn(axios, 'post')
+                .mockResolvedValueOnce({
+                    data: { id: '' },
+                })
+                .mockResolvedValueOnce({
+                    data: {
+                        id: '',
+                    },
+                });
+            expect(sessionStorage.getItem('keepLogin')).toBeFalsy();
+            expect(sessionStorage.getItem('created')).toBeDefined();
+            expect(sessionStorage.getItem('ttl')).toEqual(1);
+            await session.checkValidity();
+            expect(sessionStorage.getItem('token')).not.toEqual(expectedToken);
+            expect(sessionStorage.getItem('tokenMultimedia')).not.toEqual(
+                expectedTokenMultimedia
+            );
         });
     });
 });
diff --git a/test/vitest/__tests__/composables/useTokenConfig.spec.js b/test/vitest/__tests__/composables/useTokenConfig.spec.js
new file mode 100644
index 000000000..a25a4abb1
--- /dev/null
+++ b/test/vitest/__tests__/composables/useTokenConfig.spec.js
@@ -0,0 +1,31 @@
+import { vi, describe, expect, it } from 'vitest';
+import { axios, flushPromises } from 'app/test/vitest/helper';
+import { useTokenConfig } from 'composables/useTokenConfig';
+const tokenConfig = useTokenConfig();
+
+describe('useTokenConfig', () => {
+    describe('fetch', () => {
+        it('should call setTokenConfig of the state with the expected data', async () => {
+            const data = {
+                id: 1,
+                renewPeriod: 21600,
+                courtesyTime: 60,
+                renewInterval: 300,
+            };
+            vi.spyOn(axios, 'get').mockResolvedValueOnce({
+                data,
+            });
+
+            vi.spyOn(tokenConfig.state, 'setTokenConfig');
+
+            tokenConfig.fetch();
+
+            await flushPromises();
+
+            expect(tokenConfig.state.setTokenConfig).toHaveBeenCalledWith(data);
+
+            const renewPeriod = sessionStorage.getItem('renewPeriod');
+            expect(renewPeriod).toEqual(data.renewPeriod);
+        });
+    });
+});