Merge branch 'dev' into feature/ms-18-shelving-list

This commit is contained in:
Kevin Martinez 2023-11-28 00:16:37 -03:00
commit d65f331d1f
28 changed files with 1634 additions and 98 deletions

View File

@ -15,4 +15,14 @@ export default boot(() => {
Date.vnNow = () => { Date.vnNow = () => {
return new Date(Date.vnUTC()).getTime(); return new Date(Date.vnUTC()).getTime();
}; };
Date.vnFirstDayOfMonth = () => {
const date = new Date(Date.vnUTC());
return new Date(date.getFullYear(), date.getMonth(), 1);
};
Date.vnLastDayOfMonth = () => {
const date = new Date(Date.vnUTC());
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
};
}); });

View File

@ -83,6 +83,7 @@ const options = [
</QTooltip> </QTooltip>
</QBtn> </QBtn>
</RouterLink> </RouterLink>
<RouterLink :to="{ name: `${module}Summary`, params: { id: entity.id } }"> <RouterLink :to="{ name: `${module}Summary`, params: { id: entity.id } }">
<QBtn <QBtn
class="link" class="link"
@ -93,31 +94,21 @@ const options = [
round round
size="md" size="md"
> >
<QMenu auto-close> <QTooltip>
<QList dense v-for="option in options" :key="option"> {{ t('components.cardDescriptor.summary') }}
<QItem v-ripple clickable> </QTooltip>
{{ option }}
</QItem>
</QList>
</QMenu>
</QBtn> </QBtn>
</RouterLink> </RouterLink>
<QBtn <QBtn color="white" dense flat icon="more_vert" round size="md">
color="white"
dense
flat
icon="more_vert"
round
size="md"
v-if="slots.menu"
>
<QTooltip> <QTooltip>
{{ t('components.cardDescriptor.moreOptions') }} {{ t('components.cardDescriptor.moreOptions') }}
</QTooltip> </QTooltip>
<QMenu> <QMenu>
<QList> <QList dense v-for="option in options" :key="option">
<slot name="menu" :entity="entity" /> <QItem v-ripple clickable>
{{ option }}
</QItem>
</QList> </QList>
</QMenu> </QMenu>
</QBtn> </QBtn>

View File

@ -1,21 +1,35 @@
<script setup> <script setup>
import { ref, watch } from 'vue';
const $props = defineProps({ const $props = defineProps({
id: { type: Number, default: null }, id: { type: Number, default: null },
isSelected: { type: Boolean, default: false },
title: { type: String, default: null }, title: { type: String, default: null },
}); });
const checkSelect = ref(false);
watch(
() => $props.isSelected,
(value, prevValue) => {
checkSelect.value = value;
}
);
</script> </script>
<template> <template>
<QCard class="card q-mb-md cursor-pointer q-hoverable bg-white-7 q-pa-lg"> <QCard class="card q-mb-md cursor-pointer q-hoverable bg-white-7 q-pa-lg">
<div> <div>
<slot name="title"> <slot name="title">
<div class="flex"> <div class="flex justify-between">
<div class="title text-primary text-weight-bold text-h5"> <div class="flex">
{{ $props.title ?? `#${$props.id}` }} <div class="title text-primary text-weight-bold text-h5">
</div> {{ $props.title }}
<div class="q_chip"> </div>
<QChip outline color="grey" size="sm">ID: 12345</QChip> <QChip outline color="grey" size="sm">
ID: {{ $props.id }}
</QChip>
</div> </div>
<QCheckbox v-model="checkSelect" />
</div> </div>
</slot> </slot>
<div class="card-list-body"> <div class="card-list-body">
@ -36,12 +50,6 @@ const $props = defineProps({
margin-right: 25px; margin-right: 25px;
} }
.q_chip {
display: flex;
flex-direction: column;
justify-content: center;
}
.card-list-body { .card-list-body {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -0,0 +1,33 @@
/**
* Filtra las opciones basadas en un valor de entrada y actualiza las opciones filtradas.
*
* @param {string} val - El valor de entrada para filtrar las opciones. (la emite el evento @filter del componente QSelect)
* @param {Function} update - Función de actualización que debe ser llamada para actualizar las opciones filtradas.(la provee el evento @filter del componente QSelect)
* @param {Function} abort - Función que puede ser llamada para abortar o cancelar el filtrado actual. (la provee el evento @filter del componente QSelect)
* @param {Object} optionsToFilter - Objeto que contiene las opciones originales y filtradas.
*/
function normalizeString(text) {
return text
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
}
export function inputSelectFilter(val, update, abort, optionsToFilter) {
if (val === '') {
update(() => {
optionsToFilter.filtered = JSON.parse(
JSON.stringify(optionsToFilter.original)
);
});
return;
}
update(() => {
const searchQuery = val.toLowerCase();
optionsToFilter.filtered = optionsToFilter.original.filter((option) =>
normalizeString(option.label).includes(normalizeString(searchQuery))
);
});
}

View File

@ -424,7 +424,7 @@ export default {
comercial: 'Comercial', comercial: 'Comercial',
}, },
}, },
shelving:{ shelving: {
pageTitles: { pageTitles: {
shelving: 'Shelving', shelving: 'Shelving',
shelvingList: 'Shelving List', shelvingList: 'Shelving List',
@ -441,14 +441,14 @@ export default {
parking: 'Parking', parking: 'Parking',
priority: 'Priority', priority: 'Priority',
worker: 'Worker', worker: 'Worker',
recyclable: 'Recyclable' recyclable: 'Recyclable',
}, },
basicData: { basicData: {
code: 'Code', code: 'Code',
parking: 'Parking', parking: 'Parking',
priority: 'Priority', priority: 'Priority',
recyclable: 'Recyclable' recyclable: 'Recyclable',
} },
}, },
worker: { worker: {
pageTitles: { pageTitles: {
@ -607,6 +607,41 @@ export default {
supplierName: 'Supplier name', supplierName: 'Supplier name',
}, },
}, },
travel: {
shared: {
reference: 'Reference',
agency: 'Agency',
wareHouseOut: 'Warehouse Out',
wareHouseIn: 'Warehouse In',
landed: 'Landed',
shipped: 'Shipped',
totalEntries: 'Total entries',
},
pageTitles: {
travel: 'Travels',
list: 'List',
create: 'Create',
summary: 'Summary',
},
list: {
clone: 'Clone',
addEntry: 'Add entry',
preview: 'Preview',
},
summary: {
confirmed: 'Confirmed',
entryId: 'Entry Id',
supplier: 'Supplier',
freight: 'Freight',
package: 'Package',
delivered: 'Delivered',
received: 'Received',
entries: 'Entries',
cloneShipping: 'Clone travel',
CloneTravelAndEntries: 'Clone travel and his entries',
AddEntry: 'Add entry',
},
},
components: { components: {
topbar: {}, topbar: {},
userPanel: { userPanel: {

View File

@ -415,18 +415,18 @@ export default {
to: 'Hasta', to: 'Hasta',
company: 'Empresa', company: 'Empresa',
country: 'País', country: 'País',
clientId: 'ID Cliente', clientId: 'Id cliente',
client: 'Cliente', client: 'Cliente',
amount: 'Importe', amount: 'Importe',
base: 'Base', base: 'Base',
ticketId: 'ID Ticket', ticketId: 'Id ticket',
active: 'Activo', active: 'Activo',
hasToInvoice: 'Tiene que facturar', hasToInvoice: 'Facturar',
verifiedData: 'Datos verificados', verifiedData: 'Datos comprobados',
comercial: 'Comercial', comercial: 'Comercial',
}, },
}, },
shelving:{ shelving: {
pageTitles: { pageTitles: {
shelving: 'Carros', shelving: 'Carros',
shelvingList: 'Listado de carros', shelvingList: 'Listado de carros',
@ -443,14 +443,14 @@ export default {
parking: 'Parking', parking: 'Parking',
priority: 'Prioridad', priority: 'Prioridad',
worker: 'Trabajador', worker: 'Trabajador',
recyclable: 'Reciclable' recyclable: 'Reciclable',
}, },
basicData: { basicData: {
code: 'Código', code: 'Código',
parking: 'Parking', parking: 'Parking',
priority: 'Prioridad', priority: 'Prioridad',
recyclable: 'Reciclable' recyclable: 'Reciclable',
} },
}, },
worker: { worker: {
pageTitles: { pageTitles: {
@ -609,6 +609,41 @@ export default {
supplierName: 'Nombre del proveedor', supplierName: 'Nombre del proveedor',
}, },
}, },
travel: {
shared: {
reference: 'Referencia',
agency: 'Agencia',
wareHouseOut: 'Alm. salida',
wareHouseIn: 'Alm. entrada',
landed: 'F. entrega',
shipped: 'F. envío',
totalEntries: 'Ent. totales',
},
pageTitles: {
travel: 'Envíos',
list: 'Listado',
create: 'Crear',
summary: 'Resumen',
},
list: {
clone: 'Clonar',
addEntry: 'Añadir entrada',
preview: 'Vista previa',
},
summary: {
confirmed: 'Confirmado',
entryId: 'Id entrada',
supplier: 'Proveedor',
freight: 'Porte',
package: 'Embalaje',
delivered: 'Enviada',
received: 'Recibida',
entries: 'Entradas',
cloneShipping: 'Clonar envío',
CloneTravelAndEntries: 'Clonar travel y sus entradas',
AddEntry: 'Añadir entrada',
},
},
components: { components: {
topbar: {}, topbar: {},
userPanel: { userPanel: {

View File

@ -131,6 +131,7 @@ onUnmounted(() => {
:columns="columns" :columns="columns"
hide-bottom hide-bottom
row-key="id" row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md" class="full-width q-mt-md"
> >
<template #body-cell="props"> <template #body-cell="props">

View File

@ -1,8 +1,8 @@
<script setup> <script setup>
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { exportFile, useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import VnPaginate from 'src/components/ui/VnPaginate.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue'; import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
@ -12,10 +12,12 @@ import InvoiceOutFilter from './InvoiceOutFilter.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import CardList2 from 'src/components/ui/CardList2.vue'; import CardList2 from 'src/components/ui/CardList2.vue';
const stateStore = useStateStore();
const router = useRouter();
const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const manageCheckboxes = ref(false);
const showSelect = ref(false);
const quasar = useQuasar();
const router = useRouter();
const stateStore = useStateStore();
onMounted(() => (stateStore.rightDrawer = true)); onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
@ -32,25 +34,64 @@ function viewSummary(id) {
}, },
}); });
} }
const setShowSelect = () => {
showSelect.value = !showSelect.value;
};
const setManageCheckboxes = (downloadType) => {
console.log(downloadType);
};
const downloadCsv = (rows) => {
let file;
for (var i = 0; i < rows.length; i++) {
if (i == 0) file += Object.keys(rows[i]).join(';') + '\n';
file +=
Object.keys(rows[i])
.map(function (key) {
return rows[i][key];
})
.join(';') + '\n';
}
const status = exportFile('file.csv', file, {
encoding: 'windows-1252',
mimeType: 'text/csv;charset=windows-1252;',
});
if (status === true) {
quasar.notify({
message: t('fileAllowed'),
color: 'positive',
icon: 'check',
});
} else {
quasar.notify({
message: t('fileDenied'),
color: 'negative',
icon: 'warning',
});
}
};
</script> </script>
<template> <template>
<template v-if="stateStore.isHeaderMounted()"> <template v-if="stateStore.isHeaderMounted()">
<Teleport to="#searchbar"> <Teleport to="#searchbar">
<VnSearchbar <VnSearchbar
:info="t('youCanSearchByInvoiceReference')"
:label="t('searchInvoice')"
data-key="InvoiceOutList" data-key="InvoiceOutList"
:label="t('Search invoice')"
:info="t('You can search by invoice reference')"
/> />
</Teleport> </Teleport>
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<QBtn <QBtn
flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round
dense dense
flat
icon="menu" icon="menu"
round
> >
<QTooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
@ -67,17 +108,63 @@ function viewSummary(id) {
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<VnPaginate <VnPaginate
data-key="InvoiceOutList"
url="InvoiceOuts/filter"
order="issued DESC, id DESC"
auto-load auto-load
data-key="InvoiceOutList"
order="issued DESC, id DESC"
url="InvoiceOuts/filter"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<div class="flex justify-around q-mb-md">
<div>
<QCheckbox
label="Marcar todo"
v-model="manageCheckboxes"
@click="setShowSelect"
/>
<QBtn
:disable="!manageCheckboxes"
class="q-ml-md"
dense
flat
icon="cloud_download"
round
v-bind:class="{ dark_icon: !manageCheckboxes }"
>
<QMenu>
<QList padding dense>
<QItem
@click="setManageCheckboxes('all')"
clickable
v-ripple
>
Todo {{ rows.length }} filas(s)
</QItem>
<QItem
@click="setManageCheckboxes('partial')"
clickable
v-ripple
>
Seleccionar las {{ rows.length }} filas(s)
</QItem>
</QList>
</QMenu>
</QBtn>
</div>
<QBtn
label="Descargar"
color="primary"
type="submit"
@click="downloadCsv(rows)"
/>
</div>
<CardList2 <CardList2
v-for="row of rows" :id="row.id"
:isSelected="manageCheckboxes"
:key="row.id" :key="row.id"
:title="row.ref" :title="row.ref"
@click="navigate(row.id)" @click="navigate(row.id)"
v-for="row of rows"
> >
<template #list-items> <template #list-items>
<VnLv label="ID" :value="row.id" /> <VnLv label="ID" :value="row.id" />
@ -137,10 +224,32 @@ function viewSummary(id) {
width: 100%; width: 100%;
max-width: 60em; max-width: 60em;
} }
.dark_icon {
color: #121212;
}
.disabled,
.disabled *,
[disabled],
[disabled] * {
cursor: default !important;
}
</style> </style>
<i18n> <i18n>
es: {
Search invoice: Buscar factura emitida "en": {
You can search by invoice reference: Puedes buscar por referencia de la factura "searchInvoice": "Search issued invoice",
"fileDenied": "Browser denied file download...",
"fileAllowed": "Successful download of CSV file",
"youCanSearchByInvoiceReference": "You can search by invoice reference"
},
"es": {
"searchInvoice": "Buscar factura emitida",
"fileDenied": "El navegador denegó la descarga de archivos...",
"fileAllowed": "Descarga exitosa de archivo CSV",
"youCanSearchByInvoiceReference": "Puedes buscar por referencia de la factura"
}
}
</i18n> </i18n>

View File

@ -1,22 +1,22 @@
<script setup> <script setup>
import { onMounted, computed, ref } from 'vue'; import { onMounted, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
/* import { QBadge, QBtn } from 'quasar'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import CustomerDescriptor from 'src/pages/Customer/Card/CustomerDescriptor.vue'; */ import invoiceOutService from 'src/services/invoiceOut.service';
import invoiceOutService from 'src/services/InvoiceOut.service';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
import { QBadge, QCheckbox, exportFile } from 'quasar'; import { QBadge, QBtn, exportFile } from 'quasar';
import { toDate } from 'src/filters';
const rows = ref([]); const rows = ref([]);
const { t } = useI18n(); const { t } = useI18n();
// invoiceOutGlobalStore state and getters
const payload = ref({ const payload = ref({
from: new Date('2001-01-01'), from: Date.vnFirstDayOfMonth(),
to: new Date('2001-01-31'), to: Date.vnLastDayOfMonth(),
}); });
const selectedCustomerId = ref(0);
const filter = ref({ const filter = ref({
company: null, company: null,
country: null, country: null,
@ -41,8 +41,9 @@ const tableColumnComponents = {
props: {}, props: {},
}, },
clientId: { clientId: {
component: 'a', component: QBtn,
props: { href: '#' }, props: { flat: true, color: 'blue' },
event: (prop) => selectCustomerId(prop.value),
}, },
client: { client: {
component: 'span', component: 'span',
@ -73,8 +74,8 @@ const tableColumnComponents = {
props: { type: 'boolean' }, props: { type: 'boolean' },
}, },
comercial: { comercial: {
component: 'a', component: QBtn,
props: { href: '#' }, props: { flat: true, color: 'blue' },
}, },
}; };
@ -203,8 +204,8 @@ const search = async () => {
const refresh = () => { const refresh = () => {
payload.value = { payload.value = {
from: new Date('2001-01-01'), from: Date.vnFirstDayOfMonth(),
to: new Date('2001-01-31'), to: Date.vnLastDayOfMonth(),
}; };
filter.value = { filter.value = {
company: null, company: null,
@ -222,6 +223,10 @@ const refresh = () => {
search(); search();
}; };
const selectCustomerId = (id) => {
selectedCustomerId.value = id;
};
onMounted(async () => { onMounted(async () => {
refresh(); refresh();
}); });
@ -230,31 +235,78 @@ onMounted(async () => {
<template> <template>
<QPage class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<QTable <QTable
flat
:rows="rows" :rows="rows"
:columns="columns" :columns="columns"
hide-bottom hide-bottom
row-key="clientId" row-key="clientId"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md" class="full-width q-mt-md"
> >
<template #top-left> <template #top-left>
<div class="row justify-start items-end"> <div class="row justify-start items-end">
<QInput <QInput
dense dense
v-model="payload.from" lazy-rules
type="date" outlined
mask="date" rounded
class="q-mr-md q" placeholder="dd-mm-aaa"
:label="t('invoiceOut.negativeBases.from')" :label="t('invoiceOut.negativeBases.from')"
/> class="q-mr-md q"
:model-value="toDate(payload.from)"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
>
<QDate v-model="payload.from">
<div class="row items-center justify-end">
<QBtn
v-close-popup
label="Close"
color="primary"
flat
/>
</div>
</QDate>
</QPopupProxy>
</QIcon>
</template>
</QInput>
<QInput <QInput
dense dense
v-model="payload.to" lazy-rules
type="date" outlined
mask="date" rounded
class="q-mr-md q" placeholder="dd-mm-aaa"
:label="t('invoiceOut.negativeBases.to')" :label="t('invoiceOut.negativeBases.to')"
/> class="q-mr-md q"
:model-value="toDate(payload.to)"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
>
<QDate v-model="payload.to">
<div class="row items-center justify-end">
<QBtn
v-close-popup
label="Close"
color="primary"
flat
/>
</div>
</QDate>
</QPopupProxy>
</QIcon>
</template>
</QInput>
<QBtn <QBtn
color="primary" color="primary"
icon-right="archive" icon-right="archive"
@ -332,12 +384,7 @@ onMounted(async () => {
<QIcon name="" size="xs" /> <QIcon name="" size="xs" />
</QBadge> </QBadge>
</span> </span>
<QPopupProxy> <CustomerDescriptorProxy :id="selectedCustomerId" />
<CustomerDescriptor
v-if="selectedCustomerId === props.value"
:id="selectedCustomerId"
/>
</QPopupProxy>
</component> </component>
</QTd> </QTd>
</template> </template>

View File

@ -51,7 +51,9 @@ const isAdministrative = computed(() => {
@on-fetch="(data) => setData(data)" @on-fetch="(data) => setData(data)"
> >
<template #header-left> <template #header-left>
<QIcon name="open_in_new" color="white" size="25px" /> <a v-if="isAdministrative" class="header link" :href="supplierUrl">
<QIcon name="open_in_new" color="white" size="25px" />
</a>
</template> </template>
<template #header> <template #header>
<span>{{ supplier.name }} - {{ supplier.id }}</span> <span>{{ supplier.name }} - {{ supplier.id }}</span>

View File

@ -1,9 +1,10 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ref } from 'vue'; import { ref } from 'vue';
import suppliersService from 'src/services/Suppliers.service'; import suppliersService from 'src/services/suppliers.service';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
@ -14,7 +15,7 @@ const newSupplierName = ref();
const createSupplier = async () => { const createSupplier = async () => {
const params = { name: newSupplierName.value }; const params = { name: newSupplierName.value };
const response = await suppliersService.createSupplier(params); const response = await suppliersService.createSupplier(params);
router.push({ path: `/supplier/${response.data.id}` }); if (response.status === 200) router.push({ path: `/supplier/${response.data.id}` });
}; };
</script> </script>
@ -30,7 +31,11 @@ const createSupplier = async () => {
</template> </template>
<QPage class="q-pa-md"> <QPage class="q-pa-md">
<QForm @submit="createSupplier()" class="text-white"> <QForm
@submit="createSupplier()"
class="text-white q-mx-auto"
style="max-width: 800px"
>
<QCard class="card"> <QCard class="card">
<QInput <QInput
v-model="newSupplierName" v-model="newSupplierName"

View File

@ -0,0 +1,66 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
const stateStore = useStateStore();
const { t } = useI18n();
</script>
<template>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#searchbar">
<VnSearchbar
data-key="SuppliersList"
:limit="20"
:label="t('Search suppliers')"
/>
</Teleport>
</template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<!-- Aca iría left menu y descriptor -->
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<QToolbar class="bg-vn-dark justify-end">
<div id="st-data"></div>
<QSpace />
<div id="st-actions"></div>
</QToolbar>
<div class="q-pa-md"><RouterView></RouterView></div>
</QPage>
</QPageContainer>
</template>
<style lang="scss">
.q-scrollarea__content {
max-width: 100%;
}
</style>
<style lang="scss" scoped>
.descriptor {
max-width: 256px;
h5 {
margin: 0 15px;
}
.header {
display: flex;
justify-content: space-between;
}
.q-card__actions {
justify-content: center;
}
#descriptor-skeleton .q-card__actions {
justify-content: space-between;
}
}
</style>
<i18n>
</i18n>

View File

@ -0,0 +1,305 @@
<script setup>
import { onMounted, ref, computed, onUpdated } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { getUrl } from 'src/composables/getUrl';
import { toDate } from 'src/filters';
import travelService from 'src/services/travel.service';
import { QCheckbox, QIcon } from 'quasar';
import { toCurrency } from 'filters/index';
onUpdated(() => summaryRef.value.fetch());
const route = useRoute();
const { t } = useI18n();
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const router = useRouter();
const entityId = computed(() => $props.id || route.params.id);
const entries = ref([]);
const summaryRef = ref();
const travel = ref();
const travelUrl = ref();
onMounted(async () => {
travelUrl.value = (await getUrl('travel/')) + entityId.value;
});
const cloneTravel = () => {
const stringifiedTravelData = JSON.stringify(travel.value);
redirectToCreateView(stringifiedTravelData);
};
const headerMenuOptions = [
{ name: t('travel.summary.cloneShipping'), action: cloneTravel },
{ name: t('travel.summary.CloneTravelAndEntries'), action: null },
{ name: t('travel.summary.AddEntry'), action: null },
];
const tableColumnComponents = {
isConfirmed: {
component: () => QCheckbox,
props: (prop) => ({
disable: true,
'model-value': Boolean(prop.value),
}),
},
id: {
component: () => 'span',
props: () => {},
event: () => openEntryDescriptor(),
},
supplierName: {
component: () => 'span',
props: () => {},
event: () => {},
},
reference: {
component: () => 'span',
props: () => {},
event: () => {},
},
freightValue: {
component: () => 'span',
props: () => {},
event: () => {},
},
packageValue: {
component: () => 'span',
props: () => {},
event: () => {},
},
cc: {
component: () => 'span',
props: () => {},
event: () => {},
},
pallet: {
component: () => 'span',
props: () => {},
event: () => {},
},
m3: {
component: () => 'span',
props: () => {},
event: () => {},
},
observation: {
component: (props) => (props.value ? QIcon : null),
props: () => ({ name: 'insert_drive_file', color: 'orange', size: '25px' }),
},
};
const entriesTableColumns = computed(() => {
return [
{
label: t('travel.summary.confirmed'),
field: 'isConfirmed',
name: 'isConfirmed',
align: 'left',
},
{
label: t('travel.summary.entryId'),
field: 'id',
name: 'id',
align: 'left',
},
{
label: t('travel.summary.supplier'),
field: 'supplierName',
name: 'supplierName',
align: 'left',
},
{
label: t('travel.shared.reference'),
field: 'reference',
name: 'reference',
align: 'left',
},
{
label: t('travel.summary.freight'),
field: 'freightValue',
name: 'freightValue',
align: 'left',
format: (val) => {
return toCurrency(val);
},
},
{
label: t('travel.summary.package'),
field: 'packageValue',
name: 'packageValue',
align: 'left',
format: (val) => {
return toCurrency(val);
},
},
{ label: 'CC', field: 'cc', name: 'cc', align: 'left' },
{ label: 'Pallet', field: 'pallet', name: 'pallet', align: 'left' },
{ label: 'm³', field: 'm3', name: 'm3', align: 'left' },
{
label: '',
field: 'observation',
name: 'observation',
align: 'left',
toolTip: 'Observation three',
},
];
});
const entriesTableRows = computed(() => {
if (!entries.value && !entries.value.length > 0) return [];
return entries.value;
});
async function setTravelData(data) {
if (data) {
travel.value = data;
const entriesResponse = await travelService.getTravelEntries(travel.value.id);
if (entriesResponse.data) entries.value = entriesResponse.data;
}
}
const redirectToCreateView = (queryParams) => {
router.push({ name: 'TravelCreate', query: { travelData: queryParams } });
};
const openEntryDescriptor = () => {};
</script>
<template>
<CardSummary
ref="summaryRef"
:url="`Travels/${entityId}/getTravel`"
@on-fetch="(data) => setTravelData(data)"
>
<template #header-left>
<a class="header link" :href="travelUrl">
<QIcon name="open_in_new" color="white" size="25px" />
</a>
</template>
<template #header>
<span>{{ travel.ref }} - {{ travel.id }}</span>
</template>
<template #header-right>
<QBtn color="white" dense flat icon="more_vert" round size="md">
<QTooltip>
{{ t('components.cardDescriptor.moreOptions') }}
</QTooltip>
<QMenu>
<QList dense v-for="option in headerMenuOptions" :key="option">
<QItem v-ripple clickable @click="option.action">
{{ option.name }}
</QItem>
</QList>
</QMenu>
</QBtn>
</template>
<template #body>
<QCard class="vn-one">
<VnLv
:label="t('travel.shared.shipped')"
:value="toDate(travel.shipped)"
/>
<VnLv :label="t('travel.shared.landed')" :value="toDate(travel.landed)" />
<VnLv :label="t('travel.shared.agency')" :value="travel.agency?.name" />
<VnLv
:label="t('travel.shared.wareHouseOut')"
:value="travel.warehouseOut?.name"
/>
<VnLv
:label="t('travel.shared.wareHouseIn')"
:value="travel.warehouseIn?.name"
/>
<VnLv :label="t('travel.shared.reference')" :value="travel.ref" />
<VnLv label="m³" :value="travel.m3" />
<VnLv :label="t('travel.shared.totalEntries')" :value="travel.m3" />
<QCheckbox
v-model="travel.isDelivered"
:label="t('travel.summary.delivered')"
disable
dense
class="full-width q-my-xs"
/>
<QCheckbox
v-model="travel.isReceived"
:label="t('travel.summary.received')"
disable
dense
class="full-width q-mb-xs"
/>
</QCard>
<QCard class="vn-two" v-if="entriesTableRows.length > 0">
<a class="header" :href="travelUrl + 'entry'">
{{ t('travel.summary.entries') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QTable
:rows="entriesTableRows"
:columns="entriesTableColumns"
hide-bottom
row-key="id"
class="full-width q-mt-md"
>
<template #body-cell="props">
<QTd :props="props">
<component
:is="
tableColumnComponents[props.col.name].component(props)
"
v-bind="
tableColumnComponents[props.col.name].props(props)
"
@click="
tableColumnComponents[props.col.name].event(props)
"
class="col-content"
>
<template
v-if="
props.col.name !== 'observation' &&
props.col.name !== 'isConfirmed'
"
>{{ props.value }}</template
>
<QTooltip v-if="props.col.toolTip">{{
props.col.toolTip
}}</QTooltip>
</component>
</QTd>
</template>
</QTable>
</QCard>
</template>
</CardSummary>
</template>
<style lang="scss" scoped>
.notes {
width: max-content;
}
.cardSummary .summaryBody > .q-card > .taxes {
border: 2px solid gray;
padding: 0;
> .vn-label-value {
text-align: right;
display: flex;
flex-direction: row;
margin-top: 5px;
justify-content: flex-end;
padding-right: 20px;
}
}
</style>

View File

@ -0,0 +1,29 @@
<script setup>
import { useDialogPluginComponent } from 'quasar';
import TravelSummary from './TravelSummary.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
},
});
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script>
<template>
<QDialog ref="dialogRef" @hide="onDialogHide">
<TravelSummary v-if="$props.id" :id="$props.id" />
</QDialog>
</template>
<style lang="scss">
.q-dialog .summary .header {
position: sticky;
z-index: $z-max;
top: 0;
}
</style>

View File

@ -0,0 +1,178 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { reactive, computed } from 'vue';
import FetchData from 'components/FetchData.vue';
import { useTravelStore } from 'src/stores/travel';
import { useRouter, useRoute } from 'vue-router';
import { inputSelectFilter } from 'src/composables/inputSelectFilterFn.js';
import { toDate } from 'src/filters';
import { onBeforeMount } from 'vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const travelStore = useTravelStore();
const newTravelDataForm = reactive({
ref: null,
agencyModeFk: null,
shipped: null,
landed: null,
warehouseOutFk: null,
warehouseInFk: null,
});
const agenciesOptions = reactive({
original: [],
filtered: [],
});
const warehousesOptions = reactive({
original: [],
filtered: [],
});
onBeforeMount(() => {
if (route.query.travelData) {
const travelData = JSON.parse(route.query.travelData);
for (let key in newTravelDataForm) {
if (key === 'landed' || key === 'shipped') {
newTravelDataForm[key] = travelData[key].substring(0, 10);
} else {
newTravelDataForm[key] = travelData[key];
}
}
}
});
const createTravel = async () => {
const response = await travelStore.createTravel(newTravelDataForm);
if (response.status === 200) router.push({ path: `/travel/${response.data.id}` });
};
const onFetchAgencies = (agencies) => {
agenciesOptions.original = agencies.map((agency) => {
return { value: agency.agencyFk, label: agency.name };
});
};
const onFetchWarehouses = (warehouses) => {
warehousesOptions.original = warehouses.map((warehouse) => {
return { value: warehouse.id, label: warehouse.name };
});
};
const canSubmit = computed(() => {
for (const key in newTravelDataForm) {
if (!newTravelDataForm[key]) return false;
}
return true;
});
const redirectToTravelList = () => {
router.push({ name: 'TravelList' });
};
</script>
<template>
<FetchData url="AgencyModes" @on-fetch="(data) => onFetchAgencies(data)" auto-load />
<FetchData url="Warehouses" @on-fetch="(data) => onFetchWarehouses(data)" auto-load />
<QPage class="q-pa-md">
<QForm
@submit="createTravel()"
class="text-white column q-mx-auto"
style="max-width: 800px"
>
<QCard class="row q-pa-xl full-width card">
<QInput
v-model="newTravelDataForm.ref"
:label="t('travel.shared.reference')"
filled
style="max-width: 100%"
/>
<QSelect
:options="agenciesOptions.filtered"
v-model="newTravelDataForm.agencyModeFk"
filled
use-input
@filter="
(val, update, abort) =>
inputSelectFilter(val, update, abort, agenciesOptions)
"
:label="t('travel.shared.agency')"
transition-show="jump-up"
transition-hide="jump-up"
style="max-width: 100%"
/>
<QInput
v-model="newTravelDataForm.shipped"
type="date"
filled
mask="date"
:label="t('travel.shared.shipped')"
/>
<QInput
v-model="newTravelDataForm.landed"
type="date"
filled
mask="date"
:label="t('travel.shared.landed')"
/>
<QSelect
:options="warehousesOptions.filtered"
v-model="newTravelDataForm.warehouseOutFk"
filled
use-input
@filter="
(val, update, abort) =>
inputSelectFilter(val, update, abort, warehousesOptions)
"
:label="t('travel.shared.wareHouseOut')"
transition-show="jump-up"
transition-hide="jump-up"
/>
<QSelect
:options="warehousesOptions.filtered"
v-model="newTravelDataForm.warehouseInFk"
filled
use-input
@filter="
(val, update, abort) =>
inputSelectFilter(val, update, abort, warehousesOptions)
"
:label="t('travel.shared.wareHouseIn')"
transition-show="jump-up"
transition-hide="jump-up"
/>
</QCard>
<div class="row">
<QBtn
:label="t('globals.create')"
type="submit"
color="primary"
class="q-mt-md"
:disable="!canSubmit"
/>
<QBtn
:label="t('globals.cancel')"
class="q-mt-md"
flat
:disable="!canSubmit"
@click="redirectToTravelList()"
/>
</div>
</QForm>
</QPage>
</template>
<style lang="scss" scoped>
.card {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-gap: 20px;
}
</style>
<i18n>
</i18n>

View File

@ -0,0 +1,379 @@
<script setup>
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import { inputSelectFilter } from 'src/composables/inputSelectFilterFn.js';
import FetchData from 'components/FetchData.vue';
import { toDate } from 'src/filters';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const warehousesOptions = reactive({
original: [],
filtered: [],
});
const continentsOptions = reactive({
original: [],
filtered: [],
});
const agenciesOptions = reactive({
original: [],
filtered: [],
});
const onFetchWarehouses = (warehouses) => {
warehousesOptions.original = warehouses.map((warehouse) => {
return { value: warehouse.id, label: warehouse.name };
});
};
const onFetchContinents = (continents) => {
continentsOptions.original = continents.map((continent) => {
return { value: continent.code, label: continent.name };
});
};
const onFetchAgencies = (agencies) => {
agenciesOptions.original = agencies.map((agency) => {
return { value: agency.agencyFk, label: agency.name };
});
};
const add = (paramsObj, key) => {
if (paramsObj[key] === undefined) {
paramsObj[key] = 1;
} else {
paramsObj[key]++;
}
};
const decrement = (paramsObj, key) => {
if (paramsObj[key] === 0) return;
paramsObj[key]--;
};
</script>
<template>
<FetchData url="Warehouses" @on-fetch="(data) => onFetchWarehouses(data)" auto-load />
<FetchData url="Continents" @on-fetch="(data) => onFetchContinents(data)" auto-load />
<FetchData url="AgencyModes" @on-fetch="(data) => onFetchAgencies(data)" auto-load />
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<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 }">
<QList dense>
<QItem class="q-my-sm">
<QItemSection>
<QInput
:label="t('params.search')"
dense
lazy-rules
outlined
rounded
v-model="params.search"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QSelect
:label="t('params.agencyModeFk')"
:options="agenciesOptions.filtered"
use-input
option-value="value"
option-label="label"
emit-value
map-options
transition-show="jump-up"
transition-hide="jump-up"
dense
lazy-rules
outlined
rounded
@filter="
(val, update, abort) =>
inputSelectFilter(val, update, abort, agenciesOptions)
"
v-model="params.agencyModeFk"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QSelect
:label="t('travel.shared.wareHouseOut')"
:options="warehousesOptions.filtered"
use-input
option-value="value"
option-label="label"
emit-value
map-options
transition-show="jump-up"
transition-hide="jump-up"
dense
lazy-rules
outlined
rounded
@filter="
(val, update, abort) =>
inputSelectFilter(
val,
update,
abort,
warehousesOptions
)
"
v-model="params.warehouseOutFk"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QSelect
:label="t('params.wareHouseIn')"
:options="warehousesOptions.filtered"
use-input
option-value="value"
option-label="label"
emit-value
map-options
transition-show="jump-up"
transition-hide="jump-up"
dense
lazy-rules
outlined
rounded
@filter="
(val, update, abort) =>
inputSelectFilter(
val,
update,
abort,
warehousesOptions
)
"
v-model="params.warehouseInFk"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QInput
v-model="params.scopeDays"
type="number"
:label="t('params.scopeDays')"
dense
lazy-rules
outlined
rounded
class="input-number"
>
<template #append>
<QBtn
icon="add"
flat
dense
size="12px"
@click="add(params, 'scopeDays')"
/>
<QBtn
icon="remove"
flat
dense
size="12px"
@click="decrement(params, 'scopeDays')"
/>
</template>
</QInput>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QInput
dense
lazy-rules
outlined
rounded
placeholder="dd-mm-aaa"
:label="t('params.landedFrom')"
:model-value="toDate(params.landedFrom)"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
>
<QDate v-model="params.landedFrom">
<div class="row items-center justify-end">
<QBtn
v-close-popup
label="Close"
color="primary"
flat
/>
</div>
</QDate>
</QPopupProxy>
</QIcon>
</template>
</QInput>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QInput
dense
lazy-rules
outlined
rounded
placeholder="dd-mm-aaa"
:model-value="toDate(params.landedTo)"
:label="t('params.landedTo')"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
>
<QDate v-model="params.landedTo">
<div class="row items-center justify-end">
<QBtn
v-close-popup
label="Close"
color="primary"
flat
/>
</div>
</QDate>
</QPopupProxy>
</QIcon>
</template>
</QInput>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QSelect
:label="t('params.continent')"
:options="continentsOptions.filtered"
use-input
option-value="value"
option-label="label"
emit-value
map-options
transition-show="jump-up"
transition-hide="jump-up"
dense
lazy-rules
outlined
rounded
@filter="
(val, update, abort) =>
inputSelectFilter(
val,
update,
abort,
continentsOptions
)
"
v-model="params.continent"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<QInput
v-model="params.totalEntries"
type="number"
:label="t('params.totalEntries')"
dense
lazy-rules
outlined
rounded
min="0"
class="input-number"
>
<template #append>
<QBtn
icon="add"
flat
dense
size="12px"
@click="add(params, 'totalEntries')"
/>
<QBtn
icon="remove"
flat
dense
size="12px"
@click="decrement(params, 'totalEntries')"
/>
</template>
</QInput>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.input-number >>> input[type='number'] {
-moz-appearance: textfield;
}
.input-number >>> input::-webkit-outer-spin-button,
.input-number >>> input::-webkit-inner-spin-button {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
</style>
<i18n>
{
"en": {
"params": {
"search": "Id/Reference",
"agencyModeFk": "Agency",
"wareHouseIn": "Warehouse In",
"wareHouseOut": "Warehouse Out",
"scopeDays": "Days onward",
"landedFrom": "Landed from",
"landedTo": "Landed to",
"continent": "Continent out",
"totalEntries": "Total entries"
},
},
"es": {
"params":{
"search": "Id/Referencia",
"agencyModeFk": "Agencia",
"wareHouseIn": "Alm. entrada",
"wareHouseOut": "Alm. salida",
"scopeDays": "Días adelante",
"landedFrom": "Llegada desde",
"landedTo": "Llegada hasta",
"continent": "Cont. Salida",
"totalEntries": "Ent. totales"
},
}
}
</i18n>

View File

@ -0,0 +1,144 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue';
import { toDate } from 'src/filters/index';
import { useRouter } from 'vue-router';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import CardList2 from 'src/components/ui/CardList2.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import TravelSummaryDialog from './Card/TravelSummaryDialog.vue';
import { useTravelStore } from 'src/stores/travel.js';
import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore';
import TravelFilter from './TravelFilter.vue';
const router = useRouter();
const travelStore = useTravelStore();
const quasar = useQuasar();
const { t } = useI18n();
const stateStore = useStateStore();
const navigateToTravelId = (id) => {
router.push({ path: `/travel/${id}` });
};
const cloneTravel = (travelData) => {
const stringifiedTravelData = JSON.stringify(travelData);
redirectToCreateView(stringifiedTravelData);
};
const redirectToCreateView = (queryParams) => {
router.push({ name: 'TravelCreate', query: { travelData: queryParams } });
};
const viewSummary = (id) => {
quasar.dialog({
component: TravelSummaryDialog,
componentProps: {
id,
},
});
};
onMounted(async () => {
await travelStore.init();
stateStore.rightDrawer = true;
});
</script>
<template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<TravelFilter data-key="TravelList" />
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<VnPaginate
data-key="TravelList"
url="Travels/filter"
auto-load
order="shipped DESC, landed DESC"
>
<template #body="{ rows }">
<CardList2
v-for="row of rows"
:key="row.id"
:title="row.ref"
:id="row.id"
@click="navigateToTravelId(row.id)"
>
<template #list-items>
<VnLv
:label="t('travel.shared.agency')"
:value="row.agencyModeName"
/>
<VnLv
:label="t('travel.shared.wareHouseOut')"
:value="row.warehouseOutFk"
/>
<VnLv
:label="t('travel.shared.shipped')"
:value="toDate(row.shipped)"
/>
<VnLv
:label="t('travel.shared.landed')"
:value="toDate(row.landed)"
/>
<VnLv
:label="t('travel.shared.wareHouseIn')"
:value="row.warehouseInFk"
/>
<VnLv
:label="t('travel.shared.totalEntries')"
:value="row.totalEntries"
/>
</template>
<template #actions>
<QBtn
:label="t('travel.list.clone')"
@click.stop="cloneTravel(row)"
color="white"
outline
type="reset"
/>
<QBtn
:label="t('travel.list.addEntry')"
@click.stop="viewSummary(row.id)"
color="primary"
style="margin-top: 15px"
type="submit"
/>
<QBtn
:label="t('travel.list.preview')"
@click.stop="viewSummary(row.id)"
color="primary"
style="margin-top: 15px"
type="submit"
/>
</template>
</CardList2>
</template>
</VnPaginate>
</div>
<QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
<QTooltip>
{{ t('supplier.list.newSupplier') }}
</QTooltip>
</QPageSticky>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
</i18n>

View File

@ -0,0 +1,17 @@
<script setup>
import { useStateStore } from 'stores/useStateStore';
import LeftMenu from 'src/components/LeftMenu.vue';
const stateStore = useStateStore();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit text-grey-8">
<LeftMenu />
</QScrollArea>
</QDrawer>
<QPageContainer>
<RouterView></RouterView>
</QPageContainer>
</template>

View File

@ -3,9 +3,21 @@ import Ticket from './ticket';
import Claim from './claim'; import Claim from './claim';
import InvoiceOut from './invoiceOut'; import InvoiceOut from './invoiceOut';
import Worker from './worker'; import Worker from './worker';
import Shelving from "./shelving"; import Shelving from './shelving';
import Wagon from './wagon'; import Wagon from './wagon';
import Route from './route'; import Route from './route';
import Supplier from './Supplier'; import Supplier from './Supplier';
import Travel from './travel';
export default [Customer, Ticket, Claim, InvoiceOut, Worker, Shelving, Wagon, Route, Supplier]; export default [
Customer,
Ticket,
Claim,
InvoiceOut,
Worker,
Shelving,
Wagon,
Route,
Supplier,
Travel,
];

View File

@ -34,7 +34,7 @@ export default {
name: 'InvoiceOutGlobal', name: 'InvoiceOutGlobal',
meta: { meta: {
title: 'globalInvoicing', title: 'globalInvoicing',
icon: 'view_list', icon: 'contact_support',
}, },
component: () => import('src/pages/InvoiceOut/InvoiceOutGlobal.vue'), component: () => import('src/pages/InvoiceOut/InvoiceOutGlobal.vue'),
}, },

View File

@ -0,0 +1,59 @@
import { RouterView } from 'vue-router';
export default {
path: '/travel',
name: 'Travel',
meta: {
title: 'travel',
icon: 'vn:package',
},
component: RouterView,
redirect: { name: 'TravelMain' },
menus: {
main: ['TravelList'],
card: [],
},
children: [
{
path: '',
name: 'TravelMain',
component: () => import('src/pages/Travel/TravelMain.vue'),
redirect: { name: 'TravelList' },
children: [
{
path: 'list',
name: 'TravelList',
meta: {
title: 'list',
icon: 'view_list',
},
component: () => import('src/pages/Travel/TravelList.vue'),
},
{
path: 'create',
name: 'TravelCreate',
meta: {
title: 'create',
},
component: () => import('src/pages/Travel/TravelCreate.vue'),
},
],
},
{
name: 'TravelCard',
path: ':id',
component: () => import('src/pages/Travel/Card/TravelCard.vue'),
redirect: { name: 'TravelSummary' },
children: [
{
name: 'TravelSummary',
path: 'summary',
meta: {
title: 'summary',
},
component: () => import('src/pages/Travel/Card/TravelSummary.vue'),
},
],
},
],
};

View File

@ -6,7 +6,8 @@ import invoiceOut from './modules/invoiceOut';
import wagon from './modules/wagon'; import wagon from './modules/wagon';
import supplier from './modules/Supplier'; import supplier from './modules/Supplier';
import route from './modules/route'; import route from './modules/route';
import shelving from "src/router/modules/shelving"; import travel from './modules/travel';
import shelving from 'src/router/modules/shelving';
const routes = [ const routes = [
{ {
@ -55,6 +56,7 @@ const routes = [
wagon, wagon,
route, route,
supplier, supplier,
travel,
{ {
path: '/:catchAll(.*)*', path: '/:catchAll(.*)*',
name: 'NotFound', name: 'NotFound',

View File

@ -0,0 +1,32 @@
import axios from 'axios';
const travelService = {
getTravels: async (filter = {}) => {
try {
return await axios.get('Travels/filter', filter);
} catch (err) {
console.error(`Error fetching travels`, err);
return err.response;
}
},
createTravel: async (params) => {
try {
return await axios.patch('Travels', params);
} catch (err) {
console.error(`Error creating travel`, err);
return err.response;
}
},
getTravelEntries: async (param) => {
try {
return await axios.get(`Travels/${param}/getEntries`);
} catch (err) {
console.error(`Error fetching travel entries`, err);
return err.response;
}
},
};
export default travelService;

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useUserConfig } from 'src/composables/useUserConfig'; import { useUserConfig } from 'src/composables/useUserConfig';
import invoiceOutService from 'src/services/InvoiceOut.service.js'; import invoiceOutService from 'src/services/invoiceOut.service';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
const { notify } = useNotify(); const { notify } = useNotify();

36
src/stores/travel.js Normal file
View File

@ -0,0 +1,36 @@
import { defineStore } from 'pinia';
import travelService from 'src/services/travel.service';
export const useTravelStore = defineStore({
id: 'travel',
state: () => ({
initialDataLoading: true,
travels: [],
}),
actions: {
async init() {
await this.fetchAllData();
},
async fetchAllData() {
const { data } = await travelService.getTravels();
this.travels = data || [];
},
async createTravel(travelData) {
const params = {
ref: travelData.ref,
agencyModeFk: travelData.agencyModeFk.value,
warehouseOutFk: travelData.warehouseOutFk.value,
warehouseInFk: travelData.warehouseInFk.value,
landed: new Date(travelData.landed),
shipped: new Date(travelData.shipped),
};
return await travelService.createTravel(params);
},
},
getters: {},
});

View File

@ -16,6 +16,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
'wagon', 'wagon',
'route', 'route',
'supplier', 'supplier',
'travel',
]; ];
const pinnedModules = ref([]); const pinnedModules = ref([]);
const role = useRole(); const role = useRole();