Item types #319

Merged
jsegarra merged 28 commits from :feature/ItemFamily into dev 2024-05-02 07:39:59 +00:00
21 changed files with 704 additions and 41 deletions

View File

@ -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;

View File

@ -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 });

View File

@ -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({

View File

@ -1135,6 +1135,8 @@ item:
tax: Tax
barcode: Barcode
botanical: Botanical
itemTypeCreate: New item type
family: Item Type
descriptor:
item: Item
buyer: Buyer
@ -1219,6 +1221,11 @@ item:
minSalesQuantity: 'Cantidad mínima de venta'
genus: 'Genus'
specie: 'Specie'
item/itemType:
pageTitles:
itemType: Item type
basicData: Basic data
summary: Summary
components:

Las tradccuiones no están dentro de la carpeta que toca

Las tradccuiones no están dentro de la carpeta que toca
topbar: {}
itemsFilterPanel:

View File

@ -1134,6 +1134,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 +1220,11 @@ item:
achieved: 'Conseguido'
concept: 'Concepto'
state: 'Estado'
item/itemType:
pageTitles:
itemType: Familia
basicData: Datos básicos
summary: Resumen
components:
topbar: {}
itemsFilterPanel:

View File

@ -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"
Review

$router.push(...)

$router.push(...)
>
<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>

View File

@ -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' });
};
Outdated
Review

En Salix no esta así.
Pero en todo caso seria con un OR.

En Salix no esta así. Pero en todo caso seria con un OR.

exprBuilder corregido.

Commit: 6276c433d1

exprBuilder corregido. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/6276c433d13b9afea9e3e2726bd0dda469bdc14e
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>

View File

@ -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>

View File

@ -0,0 +1,12 @@
<script setup>
Review

Falta implementar VnCard

Falta implementar VnCard
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>

View File

@ -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" />

WorkerDescriptor

WorkerDescriptor
<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>

View File

@ -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')">
jsegarra marked this conversation as resolved Outdated

WorkerDescriptorProxy

WorkerDescriptorProxy
<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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -6,6 +6,7 @@ export default {
meta: {
title: 'department',
icon: 'vn:greuge',
moduleName: 'Department',
},
component: RouterView,
redirect: { name: 'DepartmentCard' },

View File

@ -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,
];

View File

@ -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',

View File

@ -0,0 +1,46 @@
import { RouterView } from 'vue-router';
export default {
path: '/item/item-type',
jsegarra marked this conversation as resolved Outdated

He añadido item, para que redirija bien

He añadido item, para que redirija bien
name: 'ItemType',
meta: {
title: 'itemType',
icon: 'contact_support',
jsegarra marked this conversation as resolved
Review

Añadir moduleName: 'ItemType'

Añadir moduleName: 'ItemType'
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'),
},
],
},
],
};

View File

@ -6,6 +6,7 @@ export default {
meta: {
title: 'roadmap',
icon: 'vn:troncales',
moduleName: 'Roadmap',
},
component: RouterView,
redirect: { name: 'RouteMain' },

View File

@ -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',