0
0
Fork 0

Merge branch 'dev' into 7190_renewTokenMultimedia

This commit is contained in:
Javier Segarra 2024-05-02 07:44:20 +00:00
commit b530101cda
28 changed files with 774 additions and 81 deletions

View File

@ -421,12 +421,13 @@ setLogTree();
> >
<div class="timeline"> <div class="timeline">
<div class="user-avatar"> <div class="user-avatar">
<VnUserLink :worker-id="userLog.user.id"> <VnUserLink :worker-id="userLog?.user?.id">
<template #link> <template #link>
<VnAvatar <VnAvatar
:class="{ 'cursor-pointer': userLog.user.id }" :class="{ 'cursor-pointer': userLog?.user?.id }"
:worker-id="userLog.user.id" :worker-id="userLog?.user?.id"
:title="userLog.user.nickname" :title="userLog?.user?.nickname"
:show-letter="!userLog?.user"
size="lg" size="lg"
/> />
</template> </template>

View File

@ -199,8 +199,9 @@ es:
templates: templates:
pendingPayment: 'Su pedido está pendiente de pago. pendingPayment: 'Su pedido está pendiente de pago.
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.' Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
minAmount: 'Es necesario un importe mínimo de 50 (Sin IVA) en su pedido minAmount: 'Te recordamos que tu pedido {orderId} es inferior a 50.
{ orderId } con llegada { landing } para recibirlo sin portes adicionales.' Te recomendamos amplíes para no generar costes extra, provocarán un incremento de tu tarifa.
¡Un saludo!'
orderChanges: 'Pedido {orderId} con llegada estimada día { landing }: { changes }' orderChanges: 'Pedido {orderId} con llegada estimada día { landing }: { changes }'
en: Inglés en: Inglés
es: Español es: Español

View File

@ -79,6 +79,7 @@ watch(
const isLoading = ref(false); const isLoading = ref(false);
async function search() { async function search() {
store.filter.where = {};
isLoading.value = true; isLoading.value = true;
const params = { ...userParams.value }; const params = { ...userParams.value };
store.userParamsChanged = true; store.userParamsChanged = true;

View File

@ -95,6 +95,8 @@ const addFilter = async (filter, params) => {
}; };
async function fetch() { async function fetch() {
store.filter.skip = 0;
store.skip = 0;
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
if (!store.hasMoreData) { if (!store.hasMoreData) {
isLoading.value = false; isLoading.value = false;

View File

@ -81,8 +81,10 @@ async function search() {
const staticParams = Object.entries(store.userParams).filter( const staticParams = Object.entries(store.userParams).filter(
([key, value]) => value && (props.staticParams || []).includes(key) ([key, value]) => value && (props.staticParams || []).includes(key)
); );
// const filter =props?.where? { where: JSON.parse(props.where) }: {}
await arrayData.applyFilter({ await arrayData.applyFilter({
params: { params: {
// filter ,
...Object.fromEntries(staticParams), ...Object.fromEntries(staticParams),
search: searchText.value, search: searchText.value,
}, },
@ -106,6 +108,7 @@ async function search() {
let targetUrl; let targetUrl;
if (path.endsWith('/list')) targetUrl = path.replace('/list', `/${targetId}/summary`); 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); else if (path.includes(':id')) targetUrl = path.replace(':id', targetId);
await router.push({ path: targetUrl }); await router.push({ path: targetUrl });

View File

@ -1,6 +1,5 @@
<script setup> <script setup>
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
const $props = defineProps({ const $props = defineProps({

View File

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

View File

@ -1135,6 +1135,8 @@ item:
botanical: 'Botánico' botanical: 'Botánico'
barcode: 'Código de barras' barcode: 'Código de barras'
log: Historial log: Historial
itemTypeCreate: Nueva familia
family: Familia
descriptor: descriptor:
item: Artículo item: Artículo
buyer: Comprador buyer: Comprador
@ -1219,6 +1221,11 @@ item:
achieved: 'Conseguido' achieved: 'Conseguido'
concept: 'Concepto' concept: 'Concepto'
state: 'Estado' state: 'Estado'
item/itemType:
pageTitles:
itemType: Familia
basicData: Datos básicos
summary: Resumen
components: components:
topbar: {} topbar: {}
itemsFilterPanel: itemsFilterPanel:

View File

@ -2,7 +2,7 @@
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute } from 'vue-router';
import axios from 'axios'; import axios from 'axios';
import { useStateStore } from 'src/stores/useStateStore'; import { useStateStore } from 'src/stores/useStateStore';
import { toDate, toPercentage, toCurrency } from 'filters/index'; import { toDate, toPercentage, toCurrency } from 'filters/index';
@ -10,14 +10,13 @@ import { tMobile } from 'src/composables/tMobile';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const router = useRouter();
const stateStore = computed(() => useStateStore()); const stateStore = computed(() => useStateStore());
const claim = ref(null); const claim = ref(null);
const claimRef = ref(); const claimRef = ref();
@ -38,10 +37,11 @@ const marker_labels = [
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') }, { value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
]; ];
const multiplicatorValue = ref(); const multiplicatorValue = ref();
const loading = ref(false);
const columns = computed(() => [ const columns = computed(() => [
{ {
name: 'Id', name: 'id',
label: t('Id item'), label: t('Id item'),
field: (row) => row.itemFk, field: (row) => row.itemFk,
}, },
@ -119,7 +119,7 @@ async function updateDestinations(claimDestinationFk) {
async function updateDestination(claimDestinationFk, row, options = {}) { async function updateDestination(claimDestinationFk, row, options = {}) {
if (claimDestinationFk) { if (claimDestinationFk) {
await axios.post('Claims/updateClaimDestination', { await post('Claims/updateClaimDestination', {
claimDestinationFk, claimDestinationFk,
rows: Array.isArray(row) ? row : [row], rows: Array.isArray(row) ? row : [row],
}); });
@ -128,7 +128,7 @@ async function updateDestination(claimDestinationFk, row, options = {}) {
} }
async function regularizeClaim() { async function regularizeClaim() {
await axios.post(`Claims/${claimId}/regularizeClaim`); await post(`Claims/${claimId}/regularizeClaim`);
await claimRef.value.fetch(); await claimRef.value.fetch();
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
quasar.notify({ quasar.notify({
@ -147,7 +147,7 @@ async function onUpdateGreugeAccept() {
const freightPickUpPrice = const freightPickUpPrice =
(await axios.get(`GreugeConfigs/findOne`)).data.freightPickUpPrice * (await axios.get(`GreugeConfigs/findOne`)).data.freightPickUpPrice *
multiplicatorValue.value; multiplicatorValue.value;
await axios.post(`Greuges`, { await post(`Greuges`, {
clientFk: claim.value.clientFk, clientFk: claim.value.clientFk,
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(), description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
amount: freightPickUpPrice, amount: freightPickUpPrice,
@ -166,14 +166,22 @@ async function save(data) {
} }
async function importToNewRefundTicket() { async function importToNewRefundTicket() {
const query = `ClaimBeginnings/${claimId}/importToNewRefundTicket`; await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`);
await axios.post(query); await claimActionsForm.value.reload();
claimActionsForm.value.reload();
quasar.notify({ quasar.notify({
message: t('globals.dataSaved'), message: t('globals.dataSaved'),
type: 'positive', type: 'positive',
}); });
} }
async function post(query, params) {
loading.value = true;
try {
await axios.post(query, params);
} finally {
loading.value = false;
}
}
</script> </script>
<template> <template>
<FetchData <FetchData
@ -280,6 +288,7 @@ async function importToNewRefundTicket() {
:default-save="false" :default-save="false"
:default-reset="false" :default-reset="false"
@on-fetch="setData" @on-fetch="setData"
:limit="0"
auto-load auto-load
> >
<template #body> <template #body>
@ -292,6 +301,14 @@ async function importToNewRefundTicket() {
v-model:selected="selectedRows" v-model:selected="selectedRows"
:grid="$q.screen.lt.md" :grid="$q.screen.lt.md"
> >
<template #body-cell-id="{ value }">
<QTd align="center">
<span class="link">
{{ value }}
<ItemDescriptorProxy :id="value" />
</span>
</QTd>
</template>
<template #body-cell-ticket="{ value }"> <template #body-cell-ticket="{ value }">
<QTd align="center"> <QTd align="center">
<span class="link"> <span class="link">
@ -383,6 +400,7 @@ async function importToNewRefundTicket() {
icon="check" icon="check"
@click="regularizeClaim" @click="regularizeClaim"
:disable="claim.claimStateFk == resolvedStateId" :disable="claim.claimStateFk == resolvedStateId"
:loading="loading"
/> />
<QBtn <QBtn
@ -394,6 +412,7 @@ async function importToNewRefundTicket() {
:title="t('Change destination')" :title="t('Change destination')"
icon="swap_horiz" icon="swap_horiz"
@click="dialogDestination = !dialogDestination" @click="dialogDestination = !dialogDestination"
:loading="loading"
/> />
<QBtn <QBtn
color="primary" color="primary"
@ -404,6 +423,7 @@ async function importToNewRefundTicket() {
icon="Upload" icon="Upload"
@click="importToNewRefundTicket" @click="importToNewRefundTicket"
:disable="claim.claimStateFk == resolvedStateId" :disable="claim.claimStateFk == resolvedStateId"
:loading="loading"
/> />
</template> </template>
</CrudModel> </CrudModel>

View File

@ -41,6 +41,7 @@ const workers = ref([]);
const workersCopy = ref([]); const workersCopy = ref([]);
const claimStates = ref([]); const claimStates = ref([]);
const claimStatesCopy = ref([]); const claimStatesCopy = ref([]);
const optionsList = ref([]);
function setWorkers(data) { function setWorkers(data) {
workers.value = data; workers.value = data;
@ -51,9 +52,9 @@ function setClaimStates(data) {
claimStates.value = data; claimStates.value = data;
claimStatesCopy.value = data; claimStatesCopy.value = data;
} }
let optionsList;
async function getEnumValues() { async function getEnumValues() {
optionsList = [{ id: null, description: t('claim.basicData.null') }]; optionsList.value = [{ id: null, description: t('claim.basicData.null') }];
const { data } = await axios.get(`Applications/get-enum-values`, { const { data } = await axios.get(`Applications/get-enum-values`, {
params: { params: {
schema: 'vn', schema: 'vn',
@ -62,8 +63,9 @@ async function getEnumValues() {
}, },
}); });
for (let value of data) for (let value of data)
optionsList.push({ id: value, description: t(`claim.basicData.${value}`) }); optionsList.value.push({ id: value, description: t(`claim.basicData.${value}`) });
} }
getEnumValues(); getEnumValues();
const workerFilter = { const workerFilter = {

View File

@ -45,20 +45,25 @@ async function onFetchClaim(data) {
const amount = ref(); const amount = ref();
const amountClaimed = ref(); const amountClaimed = ref();
async function onFetch(rows) { async function onFetch(rows, newRows) {
if (newRows) rows = newRows;
amount.value = 0; amount.value = 0;
amountClaimed.value = 0; amountClaimed.value = 0;
if (!rows || !rows.length) return; if (!rows || !rows.length) return;
amount.value = rows.reduce( for (const row of rows) {
(accumulator, { sale }) => accumulator + sale.price * sale.quantity, const { sale } = row;
0 amount.value = amount.value + totalRow(sale);
); const price = row.quantity * sale.price;
const discount = (sale.discount * price) / 100;
amountClaimed.value = amountClaimed.value + (price - discount);
}
}
amountClaimed.value = rows.reduce( function totalRow({ price, quantity, discount }) {
(accumulator, line) => accumulator + line.sale.price * line.quantity, const amount = price * quantity;
0 const appliedDiscount = (discount * amount) / 100;
); return amount - appliedDiscount;
} }
const columns = computed(() => [ const columns = computed(() => [
@ -102,12 +107,7 @@ const columns = computed(() => [
{ {
name: 'total', name: 'total',
label: t('Total'), label: t('Total'),
field: ({ sale }) => { field: ({ sale }) => totalRow(sale),
const amount = sale.price * sale.quantity;
const appliedDiscount = (sale.discount * amount) / 100;
return amount - appliedDiscount;
},
format: (value) => toCurrency(value), format: (value) => toCurrency(value),
sortable: true, sortable: true,
}, },
@ -129,6 +129,7 @@ async function updateDiscount({ saleFk, discount, canceller }) {
await axios.post(query, body, { await axios.post(query, body, {
signal: canceller.signal, signal: canceller.signal,
}); });
await claimLinesForm.value.reload();
} }
function onUpdateDiscount(response) { function onUpdateDiscount(response) {
@ -151,8 +152,11 @@ function showImportDialog() {
.onOk(() => claimLinesForm.value.reload()); .onOk(() => claimLinesForm.value.reload());
} }
function saveWhenHasChanges() { async function saveWhenHasChanges() {
claimLinesForm.value.getChanges().updates && claimLinesForm.value.onSubmit(); if (claimLinesForm.value.getChanges().updates) {
await claimLinesForm.value.onSubmit();
await claimLinesForm.value.reload();
}
} }
</script> </script>
<template> <template>
@ -188,7 +192,6 @@ function saveWhenHasChanges() {
save-url="ClaimBeginnings/crud" save-url="ClaimBeginnings/crud"
:filter="linesFilter" :filter="linesFilter"
@on-fetch="onFetch" @on-fetch="onFetch"
@save-changes="onFetch"
v-model:selected="selected" v-model:selected="selected"
:default-save="false" :default-save="false"
:default-reset="false" :default-reset="false"

View File

@ -180,10 +180,13 @@ function handleLocation(data, location) {
:rules="validate('Worker.postcode')" :rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']" :roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions" :options="postcodesOptions"
v-model="data.location" v-model="data.postalCode"
@update:model-value="(location) => handleLocation(data, location)" @update:model-value="(location) => handleLocation(data, location)"
></VnLocation> ></VnLocation>
</div> </div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelect <VnSelect
:label="t('Agency')" :label="t('Agency')"
@ -202,7 +205,6 @@ function handleLocation(data, location) {
<VnInput :label="t('Mobile')" clearable v-model="data.mobile" /> <VnInput :label="t('Mobile')" clearable v-model="data.mobile" />
</div> </div>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<VnSelect <VnSelect
@ -229,7 +231,6 @@ function handleLocation(data, location) {
</VnSelectDialog> </VnSelectDialog>
</div> </div>
</VnRow> </VnRow>
<h4 class="q-mb-xs">{{ t('Notes') }}</h4> <h4 class="q-mb-xs">{{ t('Notes') }}</h4>
<VnRow <VnRow
:key="index" :key="index"

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"
>
<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' });
};
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>
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" />
<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')">
<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 { t } = useI18n();
const entityId = computed(() => $props.id || route.params.id); const entityId = computed(() => $props.id || route.params.id);
const isDialog = Boolean($props.id); const isDialog = false;
const hideRightDrawer = () => { // const hideRightDrawer = () => {
if (!isDialog) { // if (!isDialog) {
stateStore.rightDrawer = false; // stateStore.rightDrawer = false;
} // }
}; // };
onMounted(hideRightDrawer); // onMounted(hideRightDrawer);
onUnmounted(hideRightDrawer); // onUnmounted(hideRightDrawer);
const filter = { const filter = {
include: [ include: [
{ {
@ -46,23 +46,6 @@ const filter = {
</script> </script>
<template> <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"> <div class="q-pa-md">
<CardSummary ref="summary" :url="`Shelvings/${entityId}`" :filter="filter"> <CardSummary ref="summary" :url="`Shelvings/${entityId}`" :filter="filter">
<template #header="{ entity }"> <template #header="{ entity }">
@ -102,18 +85,4 @@ const filter = {
</template> </template>
</CardSummary> </CardSummary>
</div> </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> </template>

View File

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

View File

@ -16,6 +16,7 @@ import Entry from './entry';
import roadmap from './roadmap'; import roadmap from './roadmap';
import Parking from './parking'; import Parking from './parking';
import Agency from './agency'; import Agency from './agency';
import ItemType from './itemType';
export default [ export default [
Item, Item,
@ -36,4 +37,5 @@ export default [
roadmap, roadmap,
Parking, Parking,
Agency, Agency,
ItemType,
]; ];

View File

@ -11,7 +11,13 @@ export default {
component: RouterView, component: RouterView,
redirect: { name: 'ItemMain' }, redirect: { name: 'ItemMain' },
menus: { menus: {
main: ['ItemList', 'WasteBreakdown', 'ItemFixedPrice', 'ItemRequest'], main: [
'ItemList',
'WasteBreakdown',
'ItemFixedPrice',
'ItemRequest',
'ItemTypeList',
],
card: [ card: [
'ItemBasicData', 'ItemBasicData',
'ItemLog', 'ItemLog',
@ -68,6 +74,23 @@ export default {
'https://grafana.verdnatura.es/d/TTNXQAxVk'; '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', path: 'request',
name: 'ItemRequest', name: 'ItemRequest',

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import supplier from './modules/Supplier';
import route from './modules/route'; import route from './modules/route';
import travel from './modules/travel'; import travel from './modules/travel';
import department from './modules/department'; import department from './modules/department';
import ItemType from './modules/itemType';
import shelving from 'src/router/modules/shelving'; import shelving from 'src/router/modules/shelving';
import order from 'src/router/modules/order'; import order from 'src/router/modules/order';
import entry from 'src/router/modules/entry'; import entry from 'src/router/modules/entry';
@ -73,6 +74,7 @@ const routes = [
entry, entry,
parking, parking,
agency, agency,
ItemType,
{ {
path: '/:catchAll(.*)*', path: '/:catchAll(.*)*',
name: 'NotFound', name: 'NotFound',