forked from verdnatura/salix-front
Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 6971-MoveBtnToSubtoolbar
This commit is contained in:
commit
61e7a77882
|
@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2414.01] - 2024-04-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
## [2400.01] - 2024-01-04
|
## [2400.01] - 2024-01-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -5,13 +5,13 @@ Lilium frontend
|
||||||
## Install the dependencies
|
## Install the dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install quasar cli
|
### Install quasar cli
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo bun install -g @quasar/cli
|
sudo npm install -g @quasar/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
||||||
|
@ -23,13 +23,13 @@ quasar dev
|
||||||
### Run unit tests
|
### Run unit tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run test:unit
|
pnpm run test:unit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run e2e tests
|
### Run e2e tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run test:e2e
|
pnpm run test:e2e
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build the app for production
|
### Build the app for production
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "24.12.0",
|
"version": "24.14.0",
|
||||||
"description": "Salix frontend",
|
"description": "Salix frontend",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Verdnatura",
|
"author": "Verdnatura",
|
||||||
|
|
|
@ -11,7 +11,7 @@ axios.defaults.baseURL = '/api/';
|
||||||
|
|
||||||
const onRequest = (config) => {
|
const onRequest = (config) => {
|
||||||
const token = session.getToken();
|
const token = session.getToken();
|
||||||
if (token.length && config.headers) {
|
if (token.length && !config.headers.Authorization) {
|
||||||
config.headers.Authorization = token;
|
config.headers.Authorization = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref, onMounted, nextTick } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
@ -16,9 +16,8 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onDataSaved']);
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const bicInputRef = ref(null);
|
||||||
const bankEntityFormData = reactive({
|
const bankEntityFormData = reactive({
|
||||||
name: null,
|
name: null,
|
||||||
bic: null,
|
bic: null,
|
||||||
|
@ -32,9 +31,14 @@ const countriesFilter = {
|
||||||
|
|
||||||
const countriesOptions = ref([]);
|
const countriesOptions = ref([]);
|
||||||
|
|
||||||
const onDataSaved = (data) => {
|
const onDataSaved = (formData, requestResponse) => {
|
||||||
emit('onDataSaved', data);
|
emit('onDataSaved', formData, requestResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
bicInputRef.value.focus();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -50,7 +54,7 @@ const onDataSaved = (data) => {
|
||||||
:title="t('title')"
|
:title="t('title')"
|
||||||
:subtitle="t('subtitle')"
|
:subtitle="t('subtitle')"
|
||||||
:form-initial-data="bankEntityFormData"
|
:form-initial-data="bankEntityFormData"
|
||||||
@on-data-saved="onDataSaved($event)"
|
@on-data-saved="onDataSaved"
|
||||||
>
|
>
|
||||||
<template #form-inputs="{ data, validate }">
|
<template #form-inputs="{ data, validate }">
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
@ -64,6 +68,7 @@ const onDataSaved = (data) => {
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnInput
|
<VnInput
|
||||||
|
ref="bicInputRef"
|
||||||
:label="t('swift')"
|
:label="t('swift')"
|
||||||
v-model="data.bic"
|
v-model="data.bic"
|
||||||
:required="true"
|
:required="true"
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
import VnInputDate from './common/VnInputDate.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const manualInvoiceFormData = reactive({
|
||||||
|
maxShipped: Date.vnNew(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const formModelPopupRef = ref();
|
||||||
|
const invoiceOutSerialsOptions = ref([]);
|
||||||
|
const taxAreasOptions = ref([]);
|
||||||
|
const ticketsOptions = ref([]);
|
||||||
|
const clientsOptions = ref([]);
|
||||||
|
const isLoading = computed(() => formModelPopupRef.value?.isLoading);
|
||||||
|
|
||||||
|
const onDataSaved = async (formData, requestResponse) => {
|
||||||
|
emit('onDataSaved', formData, requestResponse);
|
||||||
|
if (requestResponse && requestResponse.id)
|
||||||
|
router.push({ name: 'InvoiceOutSummary', params: { id: requestResponse.id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="InvoiceOutSerials"
|
||||||
|
:filter="{ where: { code: { neq: 'R' } }, order: ['code'] }"
|
||||||
|
@on-fetch="(data) => (invoiceOutSerialsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="TaxAreas"
|
||||||
|
:filter="{ order: ['code'] }"
|
||||||
|
@on-fetch="(data) => (taxAreasOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Tickets"
|
||||||
|
:filter="{ fields: ['id', 'nickname'], order: 'shipped DESC', limit: 30 }"
|
||||||
|
@on-fetch="(data) => (ticketsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Clients"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
|
||||||
|
@on-fetch="(data) => (clientsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModelPopup
|
||||||
|
ref="formModelPopupRef"
|
||||||
|
:title="t('Create manual invoice')"
|
||||||
|
url-create="InvoiceOuts/createManualInvoice"
|
||||||
|
model="invoiceOut"
|
||||||
|
:form-initial-data="manualInvoiceFormData"
|
||||||
|
@on-data-saved="onDataSaved"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data }">
|
||||||
|
<span v-if="isLoading" class="text-primary invoicing-text">
|
||||||
|
<QIcon name="warning" class="fill-icon q-mr-sm" size="md" />
|
||||||
|
{{ t('Invoicing in progress...') }}
|
||||||
|
</span>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Ticket')"
|
||||||
|
:options="ticketsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="id"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.ticketFk"
|
||||||
|
@update:model-value="data.clientFk = null"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
|
||||||
|
<QItemLabel caption>{{
|
||||||
|
scope.opt?.nickname
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<span class="row items-center" style="max-width: max-content">{{
|
||||||
|
t('Or')
|
||||||
|
}}</span>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Client')"
|
||||||
|
:options="clientsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.clientFk"
|
||||||
|
@update:model-value="data.ticketFk = null"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Serial')"
|
||||||
|
:options="invoiceOutSerialsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.serial"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Area')"
|
||||||
|
:options="taxAreasOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.taxArea"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<VnInput
|
||||||
|
:label="t('Reference')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="data.reference"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.invoicing-text {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: $primary;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Create manual invoice: Crear factura manual
|
||||||
|
Ticket: Ticket
|
||||||
|
Client: Cliente
|
||||||
|
Max date: Fecha límite
|
||||||
|
Serial: Serie
|
||||||
|
Area: Area
|
||||||
|
Reference: Referencia
|
||||||
|
Or: O
|
||||||
|
Invoicing in progress...: Facturación en progreso...
|
||||||
|
</i18n>
|
|
@ -10,6 +10,7 @@ import { useValidator } from 'src/composables/useValidator';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
||||||
import VnConfirm from './ui/VnConfirm.vue';
|
import VnConfirm from './ui/VnConfirm.vue';
|
||||||
|
import { tMobile } from 'src/composables/tMobile';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
|
@ -43,6 +44,10 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
defaultButtons: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
autoLoad: {
|
autoLoad: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -61,6 +66,10 @@ const $props = defineProps({
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
clearStoreOnUnmount: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
saveFn: {
|
saveFn: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -69,10 +78,6 @@ const $props = defineProps({
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
save,
|
|
||||||
});
|
|
||||||
|
|
||||||
const componentIsRendered = ref(false);
|
const componentIsRendered = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -109,7 +114,12 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
state.unset($props.model);
|
// Restauramos los datos originales en el store si se realizaron cambios en el formulario pero no se guardaron, evitando modificaciones erróneas.
|
||||||
|
if (hasChanges.value) {
|
||||||
|
state.set($props.model, originalData.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($props.clearStoreOnUnmount) state.unset($props.model);
|
||||||
});
|
});
|
||||||
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
@ -119,7 +129,19 @@ const hasChanges = ref(!$props.observeFormChanges);
|
||||||
const originalData = ref({ ...$props.formInitialData });
|
const originalData = ref({ ...$props.formInitialData });
|
||||||
const formData = computed(() => state.get($props.model));
|
const formData = computed(() => state.get($props.model));
|
||||||
const formUrl = computed(() => $props.url);
|
const formUrl = computed(() => $props.url);
|
||||||
|
const defaultButtons = computed(() => ({
|
||||||
|
save: {
|
||||||
|
color: 'primary',
|
||||||
|
icon: 'restart_alt',
|
||||||
|
label: 'globals.save',
|
||||||
|
},
|
||||||
|
reset: {
|
||||||
|
color: 'primary',
|
||||||
|
icon: 'save',
|
||||||
|
label: 'globals.reset',
|
||||||
|
},
|
||||||
|
...$props.defaultButtons,
|
||||||
|
}));
|
||||||
const startFormWatcher = () => {
|
const startFormWatcher = () => {
|
||||||
watch(
|
watch(
|
||||||
() => formData.value,
|
() => formData.value,
|
||||||
|
@ -131,10 +153,6 @@ const startFormWatcher = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function tMobile(...args) {
|
|
||||||
if (!quasar.platform.is.mobile) return t(...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const { data } = await axios.get($props.url, {
|
const { data } = await axios.get($props.url, {
|
||||||
params: { filter: JSON.stringify($props.filter) },
|
params: { filter: JSON.stringify($props.filter) },
|
||||||
|
@ -205,6 +223,11 @@ watch(formUrl, async () => {
|
||||||
reset();
|
reset();
|
||||||
fetch();
|
fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
save,
|
||||||
|
isLoading,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column items-center full-width">
|
<div class="column items-center full-width">
|
||||||
|
@ -233,21 +256,21 @@ watch(formUrl, async () => {
|
||||||
<QBtnGroup push class="q-gutter-x-sm">
|
<QBtnGroup push class="q-gutter-x-sm">
|
||||||
<slot name="moreActions" />
|
<slot name="moreActions" />
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="tMobile('globals.reset')"
|
:label="tMobile(defaultButtons.reset.label)"
|
||||||
color="primary"
|
:color="defaultButtons.reset.color"
|
||||||
icon="restart_alt"
|
:icon="defaultButtons.reset.icon"
|
||||||
flat
|
flat
|
||||||
@click="reset"
|
@click="reset"
|
||||||
:disable="!hasChanges"
|
:disable="!hasChanges"
|
||||||
:title="t('globals.reset')"
|
:title="t(defaultButtons.reset.label)"
|
||||||
/>
|
/>
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="tMobile('globals.save')"
|
:label="tMobile(defaultButtons.save.label)"
|
||||||
color="primary"
|
:color="defaultButtons.save.color"
|
||||||
icon="save"
|
:icon="defaultButtons.save.icon"
|
||||||
@click="save"
|
@click="save"
|
||||||
:disable="!hasChanges"
|
:disable="!hasChanges"
|
||||||
:title="t('globals.save')"
|
:title="t(defaultButtons.save.label)"
|
||||||
/>
|
/>
|
||||||
</QBtnGroup>
|
</QBtnGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
@ -39,27 +39,34 @@ const $props = defineProps({
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const formModelRef = ref(null);
|
||||||
const closeButton = ref(null);
|
const closeButton = ref(null);
|
||||||
const isLoading = ref(false);
|
|
||||||
|
|
||||||
const onDataSaved = (dataSaved) => {
|
const onDataSaved = (formData, requestResponse) => {
|
||||||
emit('onDataSaved', dataSaved);
|
emit('onDataSaved', formData, requestResponse);
|
||||||
closeForm();
|
closeForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeForm = () => {
|
const isLoading = computed(() => formModelRef.value?.isLoading);
|
||||||
|
|
||||||
|
const closeForm = async () => {
|
||||||
if (closeButton.value) closeButton.value.click();
|
if (closeButton.value) closeButton.value.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
isLoading,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormModel
|
<FormModel
|
||||||
|
ref="formModelRef"
|
||||||
:form-initial-data="formInitialData"
|
:form-initial-data="formInitialData"
|
||||||
:observe-form-changes="false"
|
:observe-form-changes="false"
|
||||||
:default-actions="false"
|
:default-actions="false"
|
||||||
:url-create="urlCreate"
|
:url-create="urlCreate"
|
||||||
:model="model"
|
:model="model"
|
||||||
@on-data-saved="onDataSaved($event)"
|
@on-data-saved="onDataSaved"
|
||||||
>
|
>
|
||||||
<template #form="{ data, validate }">
|
<template #form="{ data, validate }">
|
||||||
<span ref="closeButton" class="close-icon" v-close-popup>
|
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onSubmit']);
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
defaultSubmitButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
defaultCancelButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
customSubmitButtonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const closeButton = ref(null);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
emit('onSubmit');
|
||||||
|
closeForm();
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeForm = () => {
|
||||||
|
if (closeButton.value) closeButton.value.click();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QForm
|
||||||
|
@submit="onSubmit($event)"
|
||||||
|
class="all-pointer-events full-width"
|
||||||
|
style="max-width: 800px"
|
||||||
|
>
|
||||||
|
<QCard class="q-pa-lg">
|
||||||
|
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||||
|
<QIcon name="close" size="sm" />
|
||||||
|
</span>
|
||||||
|
<h1 class="title">{{ title }}</h1>
|
||||||
|
<p>{{ subtitle }}</p>
|
||||||
|
<slot name="form-inputs" />
|
||||||
|
<div class="q-mt-lg row justify-end">
|
||||||
|
<QBtn
|
||||||
|
v-if="defaultSubmitButton"
|
||||||
|
:label="customSubmitButtonLabel || t('globals.save')"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
v-if="defaultCancelButton"
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
class="q-ml-sm"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<slot name="customButtons" />
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
|
</QForm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.title {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,12 +10,12 @@ import UserPanel from 'components/UserPanel.vue';
|
||||||
import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
|
import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
const token = session.getToken();
|
const { getTokenMultimedia } = useSession();
|
||||||
|
const token = getTokenMultimedia();
|
||||||
const appName = 'Lilium';
|
const appName = 'Lilium';
|
||||||
|
|
||||||
onMounted(() => stateStore.setMounted());
|
onMounted(() => stateStore.setMounted());
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import FormPopup from './FormPopup.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
invoiceOutData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const transferInvoiceParams = reactive({
|
||||||
|
id: $props.invoiceOutData?.id,
|
||||||
|
refFk: $props.invoiceOutData?.ref,
|
||||||
|
});
|
||||||
|
const closeButton = ref(null);
|
||||||
|
const clientsOptions = ref([]);
|
||||||
|
const rectificativeTypeOptions = ref([]);
|
||||||
|
const siiTypeInvoiceOutsOptions = ref([]);
|
||||||
|
const invoiceCorrectionTypesOptions = ref([]);
|
||||||
|
|
||||||
|
const closeForm = () => {
|
||||||
|
if (closeButton.value) closeButton.value.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const transferInvoice = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post(
|
||||||
|
'InvoiceOuts/transferInvoice',
|
||||||
|
transferInvoiceParams
|
||||||
|
);
|
||||||
|
notify(t('Transferred invoice'), 'positive');
|
||||||
|
closeForm();
|
||||||
|
router.push('InvoiceOutSummary', { id: data.id });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error transfering invoice', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Clients"
|
||||||
|
@on-fetch="(data) => (clientsOptions = data)"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'id', limit: 30 }"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="CplusRectificationTypes"
|
||||||
|
:filter="{ order: 'description' }"
|
||||||
|
@on-fetch="(data) => (rectificativeTypeOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="SiiTypeInvoiceOuts"
|
||||||
|
:filter="{ where: { code: { like: 'R%' } } }"
|
||||||
|
@on-fetch="(data) => (siiTypeInvoiceOutsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="InvoiceCorrectionTypes"
|
||||||
|
@on-fetch="(data) => (invoiceCorrectionTypesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormPopup
|
||||||
|
@on-submit="transferInvoice()"
|
||||||
|
:title="t('Transfer invoice')"
|
||||||
|
:custom-submit-button-label="t('Transfer client')"
|
||||||
|
:default-cancel-button="false"
|
||||||
|
>
|
||||||
|
<template #form-inputs>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Client')"
|
||||||
|
:options="clientsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.newClientFk"
|
||||||
|
:required="true"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
#{{ scope.opt?.id }} -
|
||||||
|
{{ scope.opt?.name }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Rectificative type')"
|
||||||
|
:options="rectificativeTypeOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.cplusRectificationTypeFk"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Class')"
|
||||||
|
:options="siiTypeInvoiceOutsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.siiTypeInvoiceOutFk"
|
||||||
|
:required="true"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ scope.opt?.code }} -
|
||||||
|
{{ scope.opt?.description }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Type')"
|
||||||
|
:options="invoiceCorrectionTypesOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Transfer invoice: Transferir factura
|
||||||
|
Transfer client: Transferir cliente
|
||||||
|
Client: Cliente
|
||||||
|
Rectificative type: Tipo rectificativa
|
||||||
|
Class: Clase
|
||||||
|
Type: Tipo
|
||||||
|
Transferred invoice: Factura transferida
|
||||||
|
</i18n>
|
|
@ -44,7 +44,7 @@ const darkMode = computed({
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
const token = session.getToken();
|
const token = session.getTokenMultimedia();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
updatePreferences();
|
updatePreferences();
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
<script setup>
|
||||||
|
import {useDialogPluginComponent} from 'quasar';
|
||||||
|
import {useI18n} from 'vue-i18n';
|
||||||
|
import {computed, ref} from 'vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from "composables/useNotify";
|
||||||
|
|
||||||
|
const MESSAGE_MAX_LENGTH = 160;
|
||||||
|
|
||||||
|
const {t} = useI18n();
|
||||||
|
const {notify} = useNotify();
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
destinationFk: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits([...useDialogPluginComponent.emits, 'sent']);
|
||||||
|
const {dialogRef, onDialogHide} = useDialogPluginComponent();
|
||||||
|
|
||||||
|
const smsRules = [
|
||||||
|
(val) => (val && val.length > 0) || t("The message can't be empty"),
|
||||||
|
(val) =>
|
||||||
|
(val && new Blob([val]).size <= MESSAGE_MAX_LENGTH) ||
|
||||||
|
t("The message it's too long"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const message = ref('');
|
||||||
|
|
||||||
|
const charactersRemaining = computed(
|
||||||
|
() => MESSAGE_MAX_LENGTH - new Blob([message.value]).size
|
||||||
|
);
|
||||||
|
|
||||||
|
const charactersChipColor = computed(() => {
|
||||||
|
if (charactersRemaining.value < 0) {
|
||||||
|
return 'negative';
|
||||||
|
}
|
||||||
|
if (charactersRemaining.value <= 25) {
|
||||||
|
return 'warning';
|
||||||
|
}
|
||||||
|
return 'primary';
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!props.destination) {
|
||||||
|
throw new Error(`The destination can't be empty`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.value) {
|
||||||
|
throw new Error(`The message can't be empty`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charactersRemaining.value < 0) {
|
||||||
|
throw new Error(`The message it's too long`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post(props.url, {
|
||||||
|
destination: props.destination,
|
||||||
|
destinationFk: props.destinationFk,
|
||||||
|
message: message.value,
|
||||||
|
...props.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data) {
|
||||||
|
emit('sent', response.data);
|
||||||
|
notify('globals.smsSent', 'positive');
|
||||||
|
}
|
||||||
|
emit('ok', response.data);
|
||||||
|
emit('hide', response.data);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<QCard class="full-width dialog">
|
||||||
|
<QCardSection class="row">
|
||||||
|
<span v-if="title" class="text-h6">{{ title }}</span>
|
||||||
|
<QSpace />
|
||||||
|
<QBtn icon="close" flat round dense v-close-popup />
|
||||||
|
</QCardSection>
|
||||||
|
<QForm @submit="onSubmit">
|
||||||
|
<QCardSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="message"
|
||||||
|
type="textarea"
|
||||||
|
:rules="smsRules"
|
||||||
|
:label="t('Message')"
|
||||||
|
:placeholder="t('Message')"
|
||||||
|
:rows="5"
|
||||||
|
required
|
||||||
|
clearable
|
||||||
|
no-error-icon
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon name="info">
|
||||||
|
<QTooltip>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
'Special characters like accents counts as a multiple'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</VnInput>
|
||||||
|
<p class="q-mb-none q-mt-md">
|
||||||
|
{{ t('Characters remaining') }}:
|
||||||
|
<QChip :color="charactersChipColor">
|
||||||
|
{{ charactersRemaining }}
|
||||||
|
</QChip>
|
||||||
|
</p>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn type="button" flat v-close-popup class="text-primary">
|
||||||
|
{{ t('globals.cancel') }}
|
||||||
|
</QBtn>
|
||||||
|
<QBtn type="submit" color="primary">{{ t('Send') }}</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QForm>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dialog {
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Message: Mensaje
|
||||||
|
Send: Enviar
|
||||||
|
Characters remaining: Carácteres restantes
|
||||||
|
Special characters like accents counts as a multiple: Carácteres especiales como los acentos cuentan como varios
|
||||||
|
The destination can't be empty: El destinatario no puede estar vacio
|
||||||
|
The message can't be empty: El mensaje no puede estar vacio
|
||||||
|
The message it's too long: El mensaje es demasiado largo
|
||||||
|
</i18n>
|
|
@ -27,6 +27,10 @@ const $props = defineProps({
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const warehouses = ref();
|
const warehouses = ref();
|
||||||
|
@ -65,14 +69,15 @@ function mapperDms(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUrl() {
|
function getUrl() {
|
||||||
|
if ($props.url) return $props.url;
|
||||||
if ($props.formInitialData) return 'dms/' + $props.formInitialData.id + '/updateFile';
|
if ($props.formInitialData) return 'dms/' + $props.formInitialData.id + '/updateFile';
|
||||||
return `${$props.model}/${route.params.id}/uploadFile`;
|
return `${$props.model}/${route.params.id}/uploadFile`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
const body = mapperDms(dms.value);
|
const body = mapperDms(dms.value);
|
||||||
await axios.post(getUrl(), body[0], body[1]);
|
const response = await axios.post(getUrl(), body[0], body[1]);
|
||||||
emit('onDataSaved', body[1].params);
|
emit('onDataSaved', body[1].params, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultData() {
|
function defaultData() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'update:options', 'keyup.enter']);
|
const emit = defineEmits(['update:modelValue', 'update:options', 'keyup.enter']);
|
||||||
|
@ -17,7 +17,7 @@ const $props = defineProps({
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
||||||
|
const vnInputRef = ref(null);
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get() {
|
get() {
|
||||||
return $props.modelValue;
|
return $props.modelValue;
|
||||||
|
@ -40,6 +40,14 @@ const styleAttrs = computed(() => {
|
||||||
const onEnterPress = () => {
|
const onEnterPress = () => {
|
||||||
emit('keyup.enter');
|
emit('keyup.enter');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const focus = () => {
|
||||||
|
vnInputRef.value.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -59,6 +59,9 @@ const toggleForm = () => {
|
||||||
:name="actionIcon"
|
:name="actionIcon"
|
||||||
:size="actionIcon === 'add' ? 'xs' : 'sm'"
|
:size="actionIcon === 'add' ? 'xs' : 'sm'"
|
||||||
:class="['default-icon', { '--add-icon': actionIcon === 'add' }]"
|
:class="['default-icon', { '--add-icon': actionIcon === 'add' }]"
|
||||||
|
:style="{
|
||||||
|
'font-variation-settings': `'FILL' ${1}`,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<QTooltip v-if="tooltip">{{ tooltip }}</QTooltip>
|
<QTooltip v-if="tooltip">{{ tooltip }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
|
@ -35,6 +36,8 @@ const $props = defineProps({
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const state = useState();
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
@ -64,6 +67,7 @@ async function getData() {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
|
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
|
||||||
|
state.set($props.dataKey, data);
|
||||||
emit('onFetch', data);
|
emit('onFetch', data);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
|
||||||
onMounted(() => fetch());
|
|
||||||
|
|
||||||
const entity = ref();
|
const entity = ref();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
|
@ -16,14 +15,25 @@ const props = defineProps({
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
entityId: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['onFetch']);
|
const emit = defineEmits(['onFetch']);
|
||||||
|
const route = useRoute();
|
||||||
|
const isSummary = ref();
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
entity,
|
entity,
|
||||||
fetch,
|
fetch,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
isSummary.value = String(route.path).endsWith('/summary');
|
||||||
|
fetch();
|
||||||
|
});
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const params = {};
|
const params = {};
|
||||||
|
|
||||||
|
@ -48,7 +58,17 @@ watch(props, async () => {
|
||||||
<template v-if="entity">
|
<template v-if="entity">
|
||||||
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
||||||
<slot name="header-left">
|
<slot name="header-left">
|
||||||
<span></span>
|
<router-link
|
||||||
|
v-if="!isSummary && route.meta.moduleName"
|
||||||
|
class="header link"
|
||||||
|
:to="{
|
||||||
|
name: `${route.meta.moduleName}Summary`,
|
||||||
|
params: { id: entityId || entity.id },
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<QIcon name="open_in_new" color="white" size="sm" />
|
||||||
|
</router-link>
|
||||||
|
<span v-else></span>
|
||||||
</slot>
|
</slot>
|
||||||
<slot name="header" :entity="entity">
|
<slot name="header" :entity="entity">
|
||||||
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
||||||
|
|
|
@ -10,8 +10,8 @@ const $props = defineProps({
|
||||||
size: { type: String, default: null },
|
size: { type: String, default: null },
|
||||||
title: { type: String, default: null },
|
title: { type: String, default: null },
|
||||||
});
|
});
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const title = computed(() => $props.title ?? t('globals.system'));
|
const title = computed(() => $props.title ?? t('globals.system'));
|
||||||
|
|
|
@ -44,7 +44,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
offset: {
|
offset: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 500,
|
default: 0,
|
||||||
},
|
},
|
||||||
skeleton: {
|
skeleton: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -54,6 +54,10 @@ const props = defineProps({
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
disableInfiniteScroll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onPaginate']);
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
||||||
|
@ -122,10 +126,11 @@ async function paginate() {
|
||||||
emit('onPaginate');
|
emit('onPaginate');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onLoad(...params) {
|
async function onLoad(index, done) {
|
||||||
if (!store.data) return;
|
if (!store.data) {
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
|
||||||
const done = params[1];
|
|
||||||
if (store.data.length === 0 || !props.url) return done(false);
|
if (store.data.length === 0 || !props.url) return done(false);
|
||||||
|
|
||||||
pagination.value.page = pagination.value.page + 1;
|
pagination.value.page = pagination.value.page + 1;
|
||||||
|
@ -174,7 +179,8 @@ async function onLoad(...params) {
|
||||||
v-if="store.data"
|
v-if="store.data"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
:offset="offset"
|
:offset="offset"
|
||||||
class="full-width full-height"
|
:disable="disableInfiniteScroll || !arrayData.hasMoreData.value"
|
||||||
|
class="full-width"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
>
|
>
|
||||||
<slot name="body" :rows="store.data"></slot>
|
<slot name="body" :rows="store.data"></slot>
|
||||||
|
|
|
@ -118,7 +118,12 @@ async function search() {
|
||||||
autofocus
|
autofocus
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<QIcon name="search" v-if="!quasar.platform.is.mobile" />
|
<QIcon
|
||||||
|
v-if="!quasar.platform.is.mobile"
|
||||||
|
class="cursor-pointer"
|
||||||
|
name="search"
|
||||||
|
@click="search"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { getUrl } from './getUrl';
|
import { getUrl } from './getUrl';
|
||||||
|
|
||||||
const session = useSession();
|
const {getTokenMultimedia} = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
export async function downloadFile(dmsId) {
|
export async function downloadFile(dmsId) {
|
||||||
let appUrl = await getUrl('', 'lilium');
|
let appUrl = await getUrl('', 'lilium');
|
||||||
|
|
|
@ -1,30 +1,56 @@
|
||||||
import { useState } from './useState';
|
import { useState } from './useState';
|
||||||
import { useRole } from './useRole';
|
import { useRole } from './useRole';
|
||||||
import { useUserConfig } from './useUserConfig';
|
import { useUserConfig } from './useUserConfig';
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from './useNotify';
|
||||||
|
|
||||||
export function useSession() {
|
export function useSession() {
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
function getToken() {
|
function getToken() {
|
||||||
const localToken = localStorage.getItem('token');
|
const localToken = localStorage.getItem('token');
|
||||||
const sessionToken = sessionStorage.getItem('token');
|
const sessionToken = sessionStorage.getItem('token');
|
||||||
|
|
||||||
return localToken || sessionToken || '';
|
return localToken || sessionToken || '';
|
||||||
}
|
}
|
||||||
|
function getTokenMultimedia() {
|
||||||
|
const localTokenMultimedia = localStorage.getItem('tokenMultimedia');
|
||||||
|
const sessionTokenMultimedia = sessionStorage.getItem('tokenMultimedia');
|
||||||
|
|
||||||
|
return localTokenMultimedia || sessionTokenMultimedia || '';
|
||||||
|
}
|
||||||
|
|
||||||
function setToken(data) {
|
function setToken(data) {
|
||||||
if (data.keepLogin) {
|
if (data.keepLogin) {
|
||||||
localStorage.setItem('token', data.token);
|
localStorage.setItem('token', data.token);
|
||||||
|
localStorage.setItem('tokenMultimedia', data.tokenMultimedia);
|
||||||
} else {
|
} else {
|
||||||
sessionStorage.setItem('token', data.token);
|
sessionStorage.setItem('token', data.token);
|
||||||
|
sessionStorage.setItem('tokenMultimedia', data.tokenMultimedia);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function destroyToken(url, storage, key) {
|
||||||
function destroy() {
|
if (storage.getItem(key)) {
|
||||||
if (localStorage.getItem('token'))
|
try {
|
||||||
localStorage.removeItem('token')
|
await axios.post(url, null, {
|
||||||
|
headers: { Authorization: storage.getItem(key) },
|
||||||
if (sessionStorage.getItem('token'))
|
});
|
||||||
sessionStorage.removeItem('token');
|
} catch (error) {
|
||||||
|
notify('errors.statusUnauthorized', 'negative');
|
||||||
|
} finally {
|
||||||
|
storage.removeItem(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function destroy() {
|
||||||
|
const tokens = {
|
||||||
|
tokenMultimedia: 'Accounts/logout',
|
||||||
|
token: 'VnUsers/logout',
|
||||||
|
};
|
||||||
|
for (const [key, url] of Object.entries(tokens)) {
|
||||||
|
await destroyToken(url, localStorage, key);
|
||||||
|
await destroyToken(url, sessionStorage, key);
|
||||||
|
}
|
||||||
|
|
||||||
const { setUser } = useState();
|
const { setUser } = useState();
|
||||||
|
|
||||||
|
@ -37,8 +63,8 @@ export function useSession() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function login(token, keepLogin) {
|
async function login(token, tokenMultimedia, keepLogin) {
|
||||||
setToken({ token, keepLogin });
|
setToken({ token, tokenMultimedia, keepLogin });
|
||||||
|
|
||||||
await useRole().fetch();
|
await useRole().fetch();
|
||||||
await useUserConfig().fetch();
|
await useUserConfig().fetch();
|
||||||
|
@ -53,6 +79,7 @@ export function useSession() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getToken,
|
getToken,
|
||||||
|
getTokenMultimedia,
|
||||||
setToken,
|
setToken,
|
||||||
destroy,
|
destroy,
|
||||||
login,
|
login,
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
export function useVnConfirm() {
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const openConfirmationModal = (title, message, promise, successFn) => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
promise: promise,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
if (successFn) successFn();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openConfirmationModal };
|
||||||
|
}
|
|
@ -97,6 +97,15 @@ select:-webkit-autofill {
|
||||||
background-color: var(--vn-light-gray);
|
background-color: var(--vn-light-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fill-icon {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vn-table-separation-row {
|
||||||
|
height: 16px !important;
|
||||||
|
background-color: var(--vn-gray) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Estilo para el asterisco en campos requeridos */
|
/* Estilo para el asterisco en campos requeridos */
|
||||||
.q-field.required .q-field__label:after {
|
.q-field.required .q-field__label:after {
|
||||||
content: ' *';
|
content: ' *';
|
||||||
|
|
|
@ -31,6 +31,7 @@ export default {
|
||||||
close: 'Close',
|
close: 'Close',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
confirm: 'Confirm',
|
confirm: 'Confirm',
|
||||||
|
assign: 'Assign',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
yes: 'Yes',
|
yes: 'Yes',
|
||||||
no: 'No',
|
no: 'No',
|
||||||
|
@ -73,6 +74,7 @@ export default {
|
||||||
company: 'Company',
|
company: 'Company',
|
||||||
fieldRequired: 'Field required',
|
fieldRequired: 'Field required',
|
||||||
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
|
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
|
||||||
|
smsSent: 'SMS sent',
|
||||||
confirmDeletion: 'Confirm deletion',
|
confirmDeletion: 'Confirm deletion',
|
||||||
confirmDeletionMessage: 'Are you sure you want to delete this?',
|
confirmDeletionMessage: 'Are you sure you want to delete this?',
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
|
@ -590,6 +592,7 @@ export default {
|
||||||
company: 'Company',
|
company: 'Company',
|
||||||
dued: 'Due date',
|
dued: 'Due date',
|
||||||
shortDued: 'Due date',
|
shortDued: 'Due date',
|
||||||
|
amount: 'Amount',
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
issued: 'Issued',
|
issued: 'Issued',
|
||||||
|
@ -623,7 +626,7 @@ export default {
|
||||||
fillDates: 'Invoice date and the max date should be filled',
|
fillDates: 'Invoice date and the max date should be filled',
|
||||||
invoiceDateLessThanMaxDate: 'Invoice date can not be less than max date',
|
invoiceDateLessThanMaxDate: 'Invoice date can not be less than max date',
|
||||||
invoiceWithFutureDate: 'Exists an invoice with a future date',
|
invoiceWithFutureDate: 'Exists an invoice with a future date',
|
||||||
noTicketsToInvoice: 'There are not clients to invoice',
|
noTicketsToInvoice: 'There are not tickets to invoice',
|
||||||
criticalInvoiceError: 'Critical invoicing error, process stopped',
|
criticalInvoiceError: 'Critical invoicing error, process stopped',
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
|
@ -833,6 +836,7 @@ export default {
|
||||||
notifications: 'Notifications',
|
notifications: 'Notifications',
|
||||||
workerCreate: 'New worker',
|
workerCreate: 'New worker',
|
||||||
department: 'Department',
|
department: 'Department',
|
||||||
|
pda: 'PDA',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
|
@ -874,6 +878,13 @@ export default {
|
||||||
subscribed: 'Subscribed to the notification',
|
subscribed: 'Subscribed to the notification',
|
||||||
unsubscribed: 'Unsubscribed from the notification',
|
unsubscribed: 'Unsubscribed from the notification',
|
||||||
},
|
},
|
||||||
|
pda: {
|
||||||
|
newPDA: 'New PDA',
|
||||||
|
currentPDA: 'Current PDA',
|
||||||
|
model: 'Model',
|
||||||
|
serialNumber: 'Serial number',
|
||||||
|
removePDA: 'Deallocate PDA',
|
||||||
|
},
|
||||||
create: {
|
create: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
lastName: 'Last name',
|
lastName: 'Last name',
|
||||||
|
@ -939,6 +950,22 @@ export default {
|
||||||
uncompleteTrays: 'There are incomplete trays',
|
uncompleteTrays: 'There are incomplete trays',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'route/roadmap': {
|
||||||
|
pageTitles: {
|
||||||
|
roadmap: 'Roadmap',
|
||||||
|
summary: 'Summary',
|
||||||
|
basicData: 'Basic Data',
|
||||||
|
stops: 'Stops'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
roadmap: {
|
||||||
|
pageTitles: {
|
||||||
|
roadmap: 'Roadmap',
|
||||||
|
summary: 'Summary',
|
||||||
|
basicData: 'Basic Data',
|
||||||
|
stops: 'Stops'
|
||||||
|
},
|
||||||
|
},
|
||||||
route: {
|
route: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
routes: 'Routes',
|
routes: 'Routes',
|
||||||
|
@ -947,6 +974,11 @@ export default {
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
basicData: 'Basic Data',
|
basicData: 'Basic Data',
|
||||||
summary: 'Summary',
|
summary: 'Summary',
|
||||||
|
RouteRoadmap: 'Roadmaps',
|
||||||
|
RouteRoadmapCreate: 'Create roadmap',
|
||||||
|
tickets: 'Tickets',
|
||||||
|
log: 'Log',
|
||||||
|
autonomous: 'Autonomous',
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
|
|
@ -31,6 +31,7 @@ export default {
|
||||||
close: 'Cerrar',
|
close: 'Cerrar',
|
||||||
cancel: 'Cancelar',
|
cancel: 'Cancelar',
|
||||||
confirm: 'Confirmar',
|
confirm: 'Confirmar',
|
||||||
|
assign: 'Asignar',
|
||||||
back: 'Volver',
|
back: 'Volver',
|
||||||
yes: 'Si',
|
yes: 'Si',
|
||||||
no: 'No',
|
no: 'No',
|
||||||
|
@ -73,6 +74,7 @@ export default {
|
||||||
company: 'Empresa',
|
company: 'Empresa',
|
||||||
fieldRequired: 'Campo requerido',
|
fieldRequired: 'Campo requerido',
|
||||||
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
|
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
|
||||||
|
smsSent: 'SMS enviado',
|
||||||
confirmDeletion: 'Confirmar eliminación',
|
confirmDeletion: 'Confirmar eliminación',
|
||||||
confirmDeletionMessage: '¿Seguro que quieres eliminar?',
|
confirmDeletionMessage: '¿Seguro que quieres eliminar?',
|
||||||
description: 'Descripción',
|
description: 'Descripción',
|
||||||
|
@ -309,8 +311,8 @@ export default {
|
||||||
reference: 'Referencia',
|
reference: 'Referencia',
|
||||||
invoiceNumber: 'Núm. factura',
|
invoiceNumber: 'Núm. factura',
|
||||||
ordered: 'Pedida',
|
ordered: 'Pedida',
|
||||||
confirmed: 'Confirmado',
|
confirmed: 'Confirmada',
|
||||||
booked: 'Asentado',
|
booked: 'Contabilizada',
|
||||||
raid: 'Redada',
|
raid: 'Redada',
|
||||||
excludedFromAvailable: 'Inventario',
|
excludedFromAvailable: 'Inventario',
|
||||||
travelReference: 'Referencia',
|
travelReference: 'Referencia',
|
||||||
|
@ -590,6 +592,7 @@ export default {
|
||||||
company: 'Empresa',
|
company: 'Empresa',
|
||||||
dued: 'Fecha vencimineto',
|
dued: 'Fecha vencimineto',
|
||||||
shortDued: 'F. vencimiento',
|
shortDued: 'F. vencimiento',
|
||||||
|
amount: 'Importe',
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
issued: 'Fecha emisión',
|
issued: 'Fecha emisión',
|
||||||
|
@ -625,7 +628,7 @@ export default {
|
||||||
invoiceDateLessThanMaxDate:
|
invoiceDateLessThanMaxDate:
|
||||||
'La fecha de la factura no puede ser menor que la fecha máxima',
|
'La fecha de la factura no puede ser menor que la fecha máxima',
|
||||||
invoiceWithFutureDate: 'Existe una factura con una fecha futura',
|
invoiceWithFutureDate: 'Existe una factura con una fecha futura',
|
||||||
noTicketsToInvoice: 'No hay clientes para facturar',
|
noTicketsToInvoice: 'No existen tickets para facturar',
|
||||||
criticalInvoiceError: 'Error crítico en la facturación, proceso detenido',
|
criticalInvoiceError: 'Error crítico en la facturación, proceso detenido',
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
|
@ -833,6 +836,7 @@ export default {
|
||||||
notifications: 'Notificaciones',
|
notifications: 'Notificaciones',
|
||||||
workerCreate: 'Nuevo trabajador',
|
workerCreate: 'Nuevo trabajador',
|
||||||
department: 'Departamentos',
|
department: 'Departamentos',
|
||||||
|
pda: 'PDA',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
name: 'Nombre',
|
name: 'Nombre',
|
||||||
|
@ -874,6 +878,13 @@ export default {
|
||||||
subscribed: 'Se ha suscrito a la notificación',
|
subscribed: 'Se ha suscrito a la notificación',
|
||||||
unsubscribed: 'Se ha dado de baja de la notificación',
|
unsubscribed: 'Se ha dado de baja de la notificación',
|
||||||
},
|
},
|
||||||
|
pda: {
|
||||||
|
newPDA: 'Nueva PDA',
|
||||||
|
currentPDA: 'PDA Actual',
|
||||||
|
model: 'Modelo',
|
||||||
|
serialNumber: 'Número de serie',
|
||||||
|
removePDA: 'Desasignar PDA',
|
||||||
|
},
|
||||||
create: {
|
create: {
|
||||||
name: 'Nombre',
|
name: 'Nombre',
|
||||||
lastName: 'Apellido',
|
lastName: 'Apellido',
|
||||||
|
@ -939,6 +950,22 @@ export default {
|
||||||
uncompleteTrays: 'Hay bandejas sin completar',
|
uncompleteTrays: 'Hay bandejas sin completar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'route/roadmap': {
|
||||||
|
pageTitles: {
|
||||||
|
roadmap: 'Troncales',
|
||||||
|
summary: 'Resumen',
|
||||||
|
basicData: 'Datos básicos',
|
||||||
|
stops: 'Paradas'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
roadmap: {
|
||||||
|
pageTitles: {
|
||||||
|
roadmap: 'Troncales',
|
||||||
|
summary: 'Resumen',
|
||||||
|
basicData: 'Datos básicos',
|
||||||
|
stops: 'Paradas'
|
||||||
|
},
|
||||||
|
},
|
||||||
route: {
|
route: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
routes: 'Rutas',
|
routes: 'Rutas',
|
||||||
|
@ -946,7 +973,12 @@ export default {
|
||||||
RouteList: 'Listado',
|
RouteList: 'Listado',
|
||||||
create: 'Crear',
|
create: 'Crear',
|
||||||
basicData: 'Datos básicos',
|
basicData: 'Datos básicos',
|
||||||
summary: 'Summary',
|
summary: 'Resumen',
|
||||||
|
RouteRoadmap: 'Troncales',
|
||||||
|
RouteRoadmapCreate: 'Crear troncal',
|
||||||
|
tickets: 'Tickets',
|
||||||
|
log: 'Historial',
|
||||||
|
autonomous: 'Autónomos',
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
|
|
@ -13,8 +13,8 @@ import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
const claimFilter = {
|
const claimFilter = {
|
||||||
fields: [
|
fields: [
|
||||||
|
|
|
@ -11,8 +11,8 @@ import FetchData from 'components/FetchData.vue';
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
const claimId = computed(() => router.currentRoute.value.params.id);
|
const claimId = computed(() => router.currentRoute.value.params.id);
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -172,6 +172,7 @@ function openDialog(dmsId) {
|
||||||
<CardSummary
|
<CardSummary
|
||||||
ref="summary"
|
ref="summary"
|
||||||
:url="`Claims/${entityId}/getSummary`"
|
:url="`Claims/${entityId}/getSummary`"
|
||||||
|
:entity-id="entityId"
|
||||||
@on-fetch="getClaimDms"
|
@on-fetch="getClaimDms"
|
||||||
>
|
>
|
||||||
<template #header="{ entity: { claim } }">
|
<template #header="{ entity: { claim } }">
|
||||||
|
@ -270,7 +271,7 @@ function openDialog(dmsId) {
|
||||||
>
|
>
|
||||||
<ItemDescriptorProxy
|
<ItemDescriptorProxy
|
||||||
v-if="col.name == 'description'"
|
v-if="col.name == 'description'"
|
||||||
:id="props.row.id"
|
:id="props.row.sale.itemFk"
|
||||||
:sale-fk="props.row.saleFk"
|
:sale-fk="props.row.saleFk"
|
||||||
></ItemDescriptorProxy>
|
></ItemDescriptorProxy>
|
||||||
</QTh>
|
</QTh>
|
||||||
|
|
|
@ -11,8 +11,8 @@ import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
const workers = ref([]);
|
const workers = ref([]);
|
||||||
const workersCopy = ref([]);
|
const workersCopy = ref([]);
|
||||||
|
|
|
@ -61,6 +61,7 @@ const onFilterTravelSelected = (formData, id) => {
|
||||||
:url-update="`Entries/${route.params.id}`"
|
:url-update="`Entries/${route.params.id}`"
|
||||||
model="entry"
|
model="entry"
|
||||||
auto-load
|
auto-load
|
||||||
|
:clear-store-on-unmount="false"
|
||||||
>
|
>
|
||||||
<template #form="{ data }">
|
<template #form="{ data }">
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
|
|
@ -43,7 +43,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
item: {
|
item: {
|
||||||
component: QBtn,
|
component: QBtn,
|
||||||
props: {
|
props: {
|
||||||
color: 'blue',
|
color: 'primary',
|
||||||
flat: true,
|
flat: true,
|
||||||
},
|
},
|
||||||
event: () => ({}),
|
event: () => ({}),
|
||||||
|
@ -54,6 +54,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
class: 'input-number',
|
class: 'input-number',
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -67,6 +68,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
'use-input': true,
|
'use-input': true,
|
||||||
'hide-selected': true,
|
'hide-selected': true,
|
||||||
options: packagingsOptions.value,
|
options: packagingsOptions.value,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -76,6 +78,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
class: 'input-number',
|
class: 'input-number',
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -84,6 +87,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
props: {
|
props: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -92,6 +96,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
props: {
|
props: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -100,6 +105,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
props: {
|
props: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -108,6 +114,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
props: {
|
props: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -116,6 +123,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
props: {
|
props: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -124,6 +132,7 @@ const tableColumnComponents = computed(() => ({
|
||||||
props: {
|
props: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
min: 0,
|
min: 0,
|
||||||
|
dense: true,
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
@ -276,7 +285,7 @@ const toggleGroupingMode = async (buy, mode) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showLockIcon = (groupingMode, mode) => {
|
const lockIconType = (groupingMode, mode) => {
|
||||||
if (mode === 'packing') {
|
if (mode === 'packing') {
|
||||||
return groupingMode === 2 ? 'lock' : 'lock_open';
|
return groupingMode === 2 ? 'lock' : 'lock_open';
|
||||||
} else {
|
} else {
|
||||||
|
@ -320,17 +329,21 @@ const showLockIcon = (groupingMode, mode) => {
|
||||||
:columns="entriesTableColumns"
|
:columns="entriesTableColumns"
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
hide-bottom
|
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
:grid="$q.screen.lt.md"
|
:grid="$q.screen.lt.md"
|
||||||
v-model:selected="rowsSelected"
|
v-model:selected="rowsSelected"
|
||||||
|
:no-data-label="t('globals.noResults')"
|
||||||
>
|
>
|
||||||
<template #body="props">
|
<template #body="props">
|
||||||
<QTr>
|
<QTr>
|
||||||
<QTd>
|
<QTd>
|
||||||
<QCheckbox v-model="props.selected" />
|
<QCheckbox v-model="props.selected" />
|
||||||
</QTd>
|
</QTd>
|
||||||
<QTd v-for="col in props.cols" :key="col.name">
|
<QTd
|
||||||
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
|
style="max-width: 100px"
|
||||||
|
>
|
||||||
<component
|
<component
|
||||||
:is="tableColumnComponents[col.name].component"
|
:is="tableColumnComponents[col.name].component"
|
||||||
v-bind="tableColumnComponents[col.name].props"
|
v-bind="tableColumnComponents[col.name].props"
|
||||||
|
@ -350,7 +363,7 @@ const showLockIcon = (groupingMode, mode) => {
|
||||||
>
|
>
|
||||||
<QBtn
|
<QBtn
|
||||||
:icon="
|
:icon="
|
||||||
showLockIcon(props.row.groupingMode, col.name)
|
lockIconType(props.row.groupingMode, col.name)
|
||||||
"
|
"
|
||||||
@click="toggleGroupingMode(props.row, col.name)"
|
@click="toggleGroupingMode(props.row, col.name)"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
|
@ -359,6 +372,16 @@ const showLockIcon = (groupingMode, mode) => {
|
||||||
dense
|
dense
|
||||||
unelevated
|
unelevated
|
||||||
push
|
push
|
||||||
|
:style="{
|
||||||
|
'font-variation-settings': `'FILL' ${
|
||||||
|
lockIconType(
|
||||||
|
props.row.groupingMode,
|
||||||
|
col.name
|
||||||
|
) === 'lock'
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
}`,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
|
@ -397,7 +420,7 @@ const showLockIcon = (groupingMode, mode) => {
|
||||||
</QTr>
|
</QTr>
|
||||||
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
||||||
<QTr v-if="props.rowIndex !== rows.length - 1" class="separation-row">
|
<QTr v-if="props.rowIndex !== rows.length - 1" class="separation-row">
|
||||||
<QTd colspan="12" style="height: 24px" />
|
<QTd colspan="12" class="vn-table-separation-row" />
|
||||||
</QTr>
|
</QTr>
|
||||||
</template>
|
</template>
|
||||||
<template #item="props">
|
<template #item="props">
|
||||||
|
@ -448,9 +471,6 @@ const showLockIcon = (groupingMode, mode) => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.separation-row {
|
|
||||||
background-color: var(--vn-gray) !important;
|
|
||||||
}
|
|
||||||
.grid-style-transition {
|
.grid-style-transition {
|
||||||
transition: transform 0.28s, background-color 0.28s;
|
transition: transform 0.28s, background-color 0.28s;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
|
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
import { toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
import { usePrintService } from 'composables/usePrintService';
|
import { usePrintService } from 'composables/usePrintService';
|
||||||
|
|
||||||
|
@ -25,6 +26,8 @@ const $props = defineProps({
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openReport } = usePrintService();
|
const { openReport } = usePrintService();
|
||||||
|
const state = useState();
|
||||||
|
const entryDescriptorRef = ref(null);
|
||||||
|
|
||||||
const entryFilter = {
|
const entryFilter = {
|
||||||
include: [
|
include: [
|
||||||
|
@ -71,6 +74,8 @@ const data = ref(useCardDescription());
|
||||||
const setData = (entity) =>
|
const setData = (entity) =>
|
||||||
(data.value = useCardDescription(entity.supplier.nickname, entity.id));
|
(data.value = useCardDescription(entity.supplier.nickname, entity.id));
|
||||||
|
|
||||||
|
const currentEntry = computed(() => state.get('entry'));
|
||||||
|
|
||||||
const getEntryRedirectionFilter = (entry) => {
|
const getEntryRedirectionFilter = (entry) => {
|
||||||
let entryTravel = entry && entry.travel;
|
let entryTravel = entry && entry.travel;
|
||||||
|
|
||||||
|
@ -95,17 +100,20 @@ const getEntryRedirectionFilter = (entry) => {
|
||||||
const showEntryReport = () => {
|
const showEntryReport = () => {
|
||||||
openReport(`Entries/${route.params.id}/entry-order-pdf`);
|
openReport(`Entries/${route.params.id}/entry-order-pdf`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CardDescriptor
|
<CardDescriptor
|
||||||
|
ref="entryDescriptorRef"
|
||||||
module="Entry"
|
module="Entry"
|
||||||
:url="`Entries/${entityId}`"
|
:url="`Entries/${entityId}`"
|
||||||
:filter="entryFilter"
|
:filter="entryFilter"
|
||||||
:title="data.title"
|
:title="data.title"
|
||||||
:subtitle="data.subtitle"
|
:subtitle="data.subtitle"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
data-key="entryData"
|
data-key="entry"
|
||||||
>
|
>
|
||||||
<template #menu="{ entity }">
|
<template #menu="{ entity }">
|
||||||
<QItem v-ripple clickable @click="showEntryReport(entity)">
|
<QItem v-ripple clickable @click="showEntryReport(entity)">
|
||||||
|
@ -126,17 +134,17 @@ const showEntryReport = () => {
|
||||||
:value="entity.travel?.warehouseOut?.name"
|
:value="entity.travel?.warehouseOut?.name"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #icons="{ entity }">
|
<template #icons>
|
||||||
<QCardActions class="q-gutter-x-md">
|
<QCardActions class="q-gutter-x-md">
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="entity.isExcludedFromAvailable"
|
v-if="currentEntry.isExcludedFromAvailable"
|
||||||
name="vn:inventory"
|
name="vn:inventory"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<QIcon v-if="entity.isRaid" name="vn:web" color="primary" size="xs">
|
<QIcon v-if="currentEntry.isRaid" name="vn:net" color="primary" size="xs">
|
||||||
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</QCardActions>
|
</QCardActions>
|
||||||
|
|
|
@ -165,20 +165,27 @@ const fetchEntryBuys = async () => {
|
||||||
@on-fetch="(data) => setEntryData(data)"
|
@on-fetch="(data) => setEntryData(data)"
|
||||||
>
|
>
|
||||||
<template #header-left>
|
<template #header-left>
|
||||||
<a class="header-link" :href="entryUrl">
|
<router-link
|
||||||
|
v-if="route.name !== 'EntrySummary'"
|
||||||
|
:to="{ name: 'EntrySummary', params: { id: entityId } }"
|
||||||
|
class="header link"
|
||||||
|
:href="entryUrl"
|
||||||
|
>
|
||||||
<QIcon name="open_in_new" color="white" size="sm" />
|
<QIcon name="open_in_new" color="white" size="sm" />
|
||||||
</a>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
<template #header>
|
<template #header>
|
||||||
<span>{{ entry.id }} - {{ entry.supplier.nickname }}</span>
|
<span>{{ entry.id }} - {{ entry.supplier.nickname }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a class="header header-link" :href="`#/entry/${entityId}/basic-data`">
|
<router-link
|
||||||
|
:to="{ name: 'EntryBasicData', params: { id: entityId } }"
|
||||||
|
class="header header-link"
|
||||||
|
>
|
||||||
{{ t('globals.summary.basicData') }}
|
{{ t('globals.summary.basicData') }}
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</router-link>
|
||||||
|
|
||||||
<VnLv :label="t('entry.summary.commission')" :value="entry.commission" />
|
<VnLv :label="t('entry.summary.commission')" :value="entry.commission" />
|
||||||
|
|
||||||
|
@ -192,37 +199,15 @@ const fetchEntryBuys = async () => {
|
||||||
:label="t('entry.summary.invoiceNumber')"
|
:label="t('entry.summary.invoiceNumber')"
|
||||||
:value="entry.invoiceNumber"
|
:value="entry.invoiceNumber"
|
||||||
/>
|
/>
|
||||||
<QCheckbox
|
|
||||||
:label="t('entry.summary.ordered')"
|
|
||||||
v-model="entry.isOrdered"
|
|
||||||
:disable="true"
|
|
||||||
/>
|
|
||||||
<QCheckbox
|
|
||||||
:label="t('entry.summary.confirmed')"
|
|
||||||
v-model="entry.isConfirmed"
|
|
||||||
:disable="true"
|
|
||||||
/>
|
|
||||||
<QCheckbox
|
|
||||||
:label="t('entry.summary.booked')"
|
|
||||||
v-model="entry.isBooked"
|
|
||||||
:disable="true"
|
|
||||||
/>
|
|
||||||
<QCheckbox
|
|
||||||
:label="t('entry.summary.raid')"
|
|
||||||
v-model="entry.isRaid"
|
|
||||||
:disable="true"
|
|
||||||
/>
|
|
||||||
<QCheckbox
|
|
||||||
:label="t('entry.summary.excludedFromAvailable')"
|
|
||||||
v-model="entry.isExcludedFromAvailable"
|
|
||||||
:disable="true"
|
|
||||||
/>
|
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a class="header header-link" :href="entryUrl">
|
<router-link
|
||||||
{{ t('Travel data') }}
|
:to="{ name: 'EntryBasicData', params: { id: entityId } }"
|
||||||
|
class="header header-link"
|
||||||
|
>
|
||||||
|
{{ t('globals.summary.basicData') }}
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</router-link>
|
||||||
|
|
||||||
<VnLv :label="t('entry.summary.travelReference')">
|
<VnLv :label="t('entry.summary.travelReference')">
|
||||||
<template #value>
|
<template #value>
|
||||||
|
@ -250,7 +235,7 @@ const fetchEntryBuys = async () => {
|
||||||
|
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
:label="t('entry.summary.travelDelivered')"
|
:label="t('entry.summary.travelDelivered')"
|
||||||
v-model="entry.isDelivered"
|
v-model="entry.travel.isDelivered"
|
||||||
:disable="true"
|
:disable="true"
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv
|
||||||
|
@ -265,7 +250,41 @@ const fetchEntryBuys = async () => {
|
||||||
|
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
:label="t('entry.summary.travelReceived')"
|
:label="t('entry.summary.travelReceived')"
|
||||||
v-model="entry.isReceived"
|
v-model="entry.travel.isReceived"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'TravelSummary', params: { id: entry.travel.id } }"
|
||||||
|
class="header header-link"
|
||||||
|
>
|
||||||
|
{{ t('Travel data') }}
|
||||||
|
<QIcon name="open_in_new" />
|
||||||
|
</router-link>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('entry.summary.ordered')"
|
||||||
|
v-model="entry.isOrdered"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('entry.summary.confirmed')"
|
||||||
|
v-model="entry.isConfirmed"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('entry.summary.booked')"
|
||||||
|
v-model="entry.isBooked"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('entry.summary.raid')"
|
||||||
|
v-model="entry.isRaid"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('entry.summary.excludedFromAvailable')"
|
||||||
|
v-model="entry.isExcludedFromAvailable"
|
||||||
:disable="true"
|
:disable="true"
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
@ -277,9 +296,9 @@ const fetchEntryBuys = async () => {
|
||||||
<QTable
|
<QTable
|
||||||
:rows="entryBuys"
|
:rows="entryBuys"
|
||||||
:columns="entriesTableColumns"
|
:columns="entriesTableColumns"
|
||||||
hide-bottom
|
|
||||||
row-key="index"
|
row-key="index"
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
|
:no-data-label="t('globals.noResults')"
|
||||||
>
|
>
|
||||||
<template #body="{ cols, row, rowIndex }">
|
<template #body="{ cols, row, rowIndex }">
|
||||||
<QTr no-hover>
|
<QTr no-hover>
|
||||||
|
@ -325,11 +344,8 @@ const fetchEntryBuys = async () => {
|
||||||
</QTd>
|
</QTd>
|
||||||
</QTr>
|
</QTr>
|
||||||
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
||||||
<QTr
|
<QTr v-if="rowIndex !== entryBuys.length - 1">
|
||||||
v-if="rowIndex !== entryBuys.length - 1"
|
<QTd colspan="10" class="vn-table-separation-row" />
|
||||||
class="separation-row"
|
|
||||||
>
|
|
||||||
<QTd colspan="10" style="height: 24px" />
|
|
||||||
</QTr>
|
</QTr>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
|
@ -338,13 +354,7 @@ const fetchEntryBuys = async () => {
|
||||||
</CardSummary>
|
</CardSummary>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.separation-row {
|
|
||||||
background-color: var(--vn-gray) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Travel data: 'Datos envío'
|
Travel data: Datos envío
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -20,8 +20,8 @@ import { dashIfEmpty } from 'src/filters';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ onMounted(async () => {
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="row.isRaid"
|
v-if="row.isRaid"
|
||||||
name="vn:web"
|
name="vn:net"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup>
|
||||||
|
import InvoiceInDescriptor from "pages/InvoiceIn/Card/InvoiceInDescriptor.vue";
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QPopupProxy>
|
||||||
|
<InvoiceInDescriptor v-if="$props.id" :id="$props.id" />
|
||||||
|
</QPopupProxy>
|
||||||
|
</template>
|
|
@ -2,13 +2,15 @@
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toCurrency, toDate } from 'src/filters';
|
|
||||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
|
||||||
import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
|
import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
|
||||||
|
|
||||||
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
|
import { toCurrency, toDate } from 'src/filters';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
@ -23,7 +25,6 @@ const { t } = useI18n();
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
const descriptor = ref();
|
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
include: [
|
include: [
|
||||||
|
@ -42,6 +43,8 @@ const filter = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const descriptor = ref();
|
||||||
|
|
||||||
function ticketFilter(invoice) {
|
function ticketFilter(invoice) {
|
||||||
return JSON.stringify({ refFk: invoice.ref });
|
return JSON.stringify({ refFk: invoice.ref });
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
|
||||||
data-key="invoiceOutData"
|
data-key="invoiceOutData"
|
||||||
>
|
>
|
||||||
<template #menu="{ entity }">
|
<template #menu="{ entity }">
|
||||||
<InvoiceOutDescriptorMenu :invoice-out="entity" />
|
<InvoiceOutDescriptorMenu :invoice-out-data="entity" />
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
||||||
|
|
|
@ -1,40 +1,260 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
|
||||||
|
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||||
|
|
||||||
|
import useNotify from 'src/composables/useNotify';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import { usePrintService } from 'composables/usePrintService';
|
||||||
|
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
invoiceOutData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const router = useRouter();
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { openReport, sendEmail } = usePrintService();
|
||||||
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const transferInvoiceDialogRef = ref();
|
||||||
|
const invoiceFormType = ref('pdf');
|
||||||
|
const defaultEmailAddress = ref($props.invoiceOutData.client?.email);
|
||||||
|
|
||||||
|
const showInvoicePdf = () => {
|
||||||
|
const url = `api/InvoiceOuts/${$props.invoiceOutData.id}/download?access_token=${token}`;
|
||||||
|
window.open(url, '_blank');
|
||||||
|
};
|
||||||
|
|
||||||
|
const showInvoiceCsv = () => {
|
||||||
|
openReport(`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-csv`, {
|
||||||
|
recipientId: $props.invoiceOutData.client.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showSendInvoiceDialog = (type) => {
|
||||||
|
invoiceFormType.value = type;
|
||||||
|
quasar.dialog({
|
||||||
|
component: SendEmailDialog,
|
||||||
|
componentProps: {
|
||||||
|
data: {
|
||||||
|
address: defaultEmailAddress.value,
|
||||||
|
},
|
||||||
|
promise: sendEmailInvoice,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendEmailInvoice = async ({ address }) => {
|
||||||
|
try {
|
||||||
|
if (!address) notify(`The email can't be empty`, 'negative');
|
||||||
|
|
||||||
|
if (invoiceFormType.value === 'pdf') {
|
||||||
|
return sendEmail(`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-email`, {
|
||||||
|
recipientId: $props.invoiceOutData.client.id,
|
||||||
|
recipient: address,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return sendEmail(
|
||||||
|
`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-csv-email`,
|
||||||
|
{
|
||||||
|
recipientId: $props.invoiceOutData.client.id,
|
||||||
|
recipient: address,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error sending email', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToInvoiceOutList = () => {
|
||||||
|
router.push({ name: 'InvoiceOutList' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteInvoice = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post(`InvoiceOuts/${$props.invoiceOutData.id}/delete`);
|
||||||
|
notify(t('InvoiceOut deleted'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting invoice out', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const bookInvoice = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post(`InvoiceOuts/${$props.invoiceOutData.ref}/book`);
|
||||||
|
notify(t('InvoiceOut booked'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error booking invoice out', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateInvoicePdf = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post(`InvoiceOuts/${$props.invoiceOutData.id}/createPdf`);
|
||||||
|
notify(t('The invoice PDF document has been regenerated'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error generating invoice out pdf', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const refundInvoice = async (withWarehouse) => {
|
||||||
|
try {
|
||||||
|
const params = { ref: $props.invoiceOutData.ref, withWarehouse: withWarehouse };
|
||||||
|
const { data } = await axios.post('InvoiceOuts/refund', params);
|
||||||
|
notify(
|
||||||
|
t('refundInvoiceSuccessMessage', {
|
||||||
|
refundTicket: data[0].id,
|
||||||
|
}),
|
||||||
|
'positive'
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error generating invoice out pdf', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable @click="transferInvoiceDialogRef.show()">
|
||||||
<QItemSection>{{ t('Transfer invoice to') }}</QItemSection>
|
<QItemSection>{{ t('Transfer invoice to...') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable>
|
||||||
<QItemSection>{{ t('See invoice') }}</QItemSection>
|
<QItemSection>{{ t('Show invoice...') }}</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QIcon name="keyboard_arrow_right" />
|
||||||
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start">
|
||||||
|
<QList>
|
||||||
|
<QItem v-ripple clickable @click="showInvoicePdf()">
|
||||||
|
<QItemSection>{{ t('As PDF') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-ripple clickable @click="showInvoiceCsv()">
|
||||||
|
<QItemSection>{{ t('As CSV') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable>
|
||||||
<QItemSection>{{ t('Send invoice') }}</QItemSection>
|
<QItemSection>{{ t('Send invoice...') }}</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QIcon name="keyboard_arrow_right" />
|
||||||
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start">
|
||||||
|
<QList>
|
||||||
|
<QItem v-ripple clickable @click="showSendInvoiceDialog('pdf')">
|
||||||
|
<QItemSection>{{ t('Send PDF') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable @click="showSendInvoiceDialog('csv')">
|
||||||
|
<QItemSection>{{ t('Send CSV') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
|
</QItem>
|
||||||
|
<QItem
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('Confirm deletion'),
|
||||||
|
t('Are you sure you want to delete this invoice?'),
|
||||||
|
deleteInvoice,
|
||||||
|
redirectToInvoiceOutList
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
|
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem
|
||||||
<QItemSection>{{ t('Post invoice') }}</QItemSection>
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
'',
|
||||||
|
t('Are you sure you want to book this invoice?'),
|
||||||
|
bookInvoice
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('Book invoice') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('Generate PDF invoice document'),
|
||||||
|
t('Are you sure you want to generate/regenerate the PDF invoice?'),
|
||||||
|
generateInvoicePdf
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('Generate PDF invoice') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable>
|
||||||
<QItemSection>{{ t('Regenerate invoice PDF') }}</QItemSection>
|
<QItemSection>{{ t('Refund...') }}</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QIcon name="keyboard_arrow_right" />
|
||||||
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start">
|
||||||
|
<QList>
|
||||||
|
<QItem v-ripple clickable @click="refundInvoice(true)">
|
||||||
|
<QItemSection>{{ t('With warehouse') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable @click="refundInvoice(false)">
|
||||||
<QItemSection>{{ t('Pass') }}</QItemSection>
|
<QItemSection>{{ t('Without warehouse') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Create a single ticket with all the content of the current invoice') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QItem>
|
||||||
|
|
||||||
|
<QDialog ref="transferInvoiceDialogRef">
|
||||||
|
<TransferInvoiceForm :invoice-out-data="invoiceOutData" />
|
||||||
|
</QDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Transfer invoice to: Transferir factura a
|
Transfer invoice to...: Transferir factura a...
|
||||||
See invoice: Ver factura
|
Show invoice...: Ver factura...
|
||||||
Send invoice: Enviar factura
|
Send invoice...: Enviar factura...
|
||||||
Delete invoice: Eliminar factura
|
Delete invoice: Eliminar factura
|
||||||
Post invoice: Asentar factura
|
Book invoice: Asentar factura
|
||||||
Regenerate invoice PDF: Regenerar PDF factura
|
Generate PDF invoice: Generar PDF factura
|
||||||
Pass: Abono
|
Refund...: Abono
|
||||||
|
As PDF: como PDF
|
||||||
|
As CSV: como CSV
|
||||||
|
Send PDF: Enviar PDF
|
||||||
|
Send CSV: Enviar CSV
|
||||||
|
With warehouse: Con almacén
|
||||||
|
Without warehouse: Sin almacén
|
||||||
|
InvoiceOut deleted: Factura eliminada
|
||||||
|
Confirm deletion: Confirmar eliminación
|
||||||
|
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
|
||||||
|
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||||
|
InvoiceOut booked: Factura asentada
|
||||||
|
Generate PDF invoice document: Generar PDF de la factura
|
||||||
|
Are you sure you want to generate/regenerate the PDF invoice?: ¿Seguro que quieres generar/regenerar el PDF de la factura?
|
||||||
|
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
||||||
|
Create a single ticket with all the content of the current invoice: Crear un ticket único con todo el contenido de la factura actual
|
||||||
|
refundInvoiceSuccessMessage: Se ha creado el siguiente ticket de abono {refundTicket}
|
||||||
|
The email can't be empty: El email no puede estar vacío
|
||||||
|
|
||||||
|
en:
|
||||||
|
refundInvoiceSuccessMessage: The following refund ticket have been created {refundTicket}
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { toCurrency, toDate } from 'src/filters';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import { getUrl } from 'src/composables/getUrl';
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
fetch();
|
fetch();
|
||||||
|
@ -67,35 +69,43 @@ const taxColumns = ref([
|
||||||
const ticketsColumns = ref([
|
const ticketsColumns = ref([
|
||||||
{
|
{
|
||||||
name: 'item',
|
name: 'item',
|
||||||
label: 'invoiceOut.summary.ticketId',
|
label: t('invoiceOut.summary.ticketId'),
|
||||||
field: (row) => row.id,
|
field: (row) => row.id,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'quantity',
|
name: 'quantity',
|
||||||
label: 'invoiceOut.summary.nickname',
|
label: t('invoiceOut.summary.nickname'),
|
||||||
field: (row) => row.nickname,
|
field: (row) => row.nickname,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'landed',
|
name: 'landed',
|
||||||
label: 'invoiceOut.summary.shipped',
|
label: t('invoiceOut.summary.shipped'),
|
||||||
field: (row) => row.shipped,
|
field: (row) => row.shipped,
|
||||||
format: (value) => toDate(value),
|
format: (value) => toDate(value),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'landed',
|
name: 'landed',
|
||||||
label: 'invoiceOut.summary.totalWithVat',
|
label: t('invoiceOut.summary.totalWithVat'),
|
||||||
field: (row) => row.totalWithVat,
|
field: (row) => row.totalWithVat,
|
||||||
format: (value) => toCurrency(value),
|
format: (value) => toCurrency(value),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CardSummary ref="summary" :url="`InvoiceOuts/${entityId}/summary`">
|
<CardSummary
|
||||||
|
ref="summary"
|
||||||
|
:url="`InvoiceOuts/${entityId}/summary`"
|
||||||
|
:entity-id="entityId"
|
||||||
|
>
|
||||||
<template #header="{ entity: { invoiceOut } }">
|
<template #header="{ entity: { invoiceOut } }">
|
||||||
<div>{{ invoiceOut.ref }} - {{ invoiceOut.client?.socialName }}</div>
|
<div>{{ invoiceOut.ref }} - {{ invoiceOut.client?.socialName }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -147,12 +157,21 @@ const ticketsColumns = ref([
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</a>
|
||||||
<QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
|
<QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
|
||||||
<template #header="props">
|
<template #body-cell-item="{ value }">
|
||||||
<QTr :props="props">
|
<QTd>
|
||||||
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
<QBtn flat color="primary">
|
||||||
{{ t(col.label) }}
|
{{ value }}
|
||||||
</QTh>
|
<TicketDescriptorProxy :id="value" />
|
||||||
</QTr>
|
</QBtn>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-quantity="{ value, row }">
|
||||||
|
<QTd>
|
||||||
|
<QBtn flat color="primary" dense>
|
||||||
|
{{ value }}
|
||||||
|
<CustomerDescriptorProxy :id="row.id" />
|
||||||
|
</QBtn>
|
||||||
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
|
@ -21,6 +21,7 @@ const {
|
||||||
nPdfs,
|
nPdfs,
|
||||||
totalPdfs,
|
totalPdfs,
|
||||||
errors,
|
errors,
|
||||||
|
addresses,
|
||||||
} = storeToRefs(invoiceOutGlobalStore);
|
} = storeToRefs(invoiceOutGlobalStore);
|
||||||
|
|
||||||
const selectedCustomerId = ref(null);
|
const selectedCustomerId = ref(null);
|
||||||
|
@ -86,6 +87,14 @@ const selectCustomerId = (id) => {
|
||||||
selectedCustomerId.value = id;
|
selectedCustomerId.value = id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return status.value === 'invoicing'
|
||||||
|
? `${t(`status.${status.value}`)} ${
|
||||||
|
addresses.value[getAddressNumber.value]?.clientId
|
||||||
|
}`
|
||||||
|
: t(`status.${status.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => (stateStore.rightDrawer = true));
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
stateStore.rightDrawer = false;
|
stateStore.rightDrawer = false;
|
||||||
|
@ -103,7 +112,7 @@ onUnmounted(() => {
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
<QCard v-if="status" class="card">
|
<QCard v-if="status" class="card">
|
||||||
<QCardSection class="card-section">
|
<QCardSection class="card-section">
|
||||||
<span class="text">{{ t(`status.${status}`) }}</span>
|
<span class="text">{{ statusText }}</span>
|
||||||
<span class="text">{{
|
<span class="text">{{
|
||||||
t('invoiceOut.globalInvoices.statusCard.percentageText', {
|
t('invoiceOut.globalInvoices.statusCard.percentageText', {
|
||||||
getPercentage: getPercentage,
|
getPercentage: getPercentage,
|
||||||
|
|
|
@ -13,13 +13,8 @@ const { t } = useI18n();
|
||||||
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
||||||
|
|
||||||
// invoiceOutGlobalStore state and getters
|
// invoiceOutGlobalStore state and getters
|
||||||
const {
|
const { initialDataLoading, formInitialData, invoicing, status } =
|
||||||
initialDataLoading,
|
storeToRefs(invoiceOutGlobalStore);
|
||||||
formInitialData,
|
|
||||||
|
|
||||||
invoicing,
|
|
||||||
status,
|
|
||||||
} = storeToRefs(invoiceOutGlobalStore);
|
|
||||||
|
|
||||||
// invoiceOutGlobalStore actions
|
// invoiceOutGlobalStore actions
|
||||||
const { makeInvoice, setStatusValue } = invoiceOutGlobalStore;
|
const { makeInvoice, setStatusValue } = invoiceOutGlobalStore;
|
||||||
|
@ -32,13 +27,7 @@ const printersOptions = ref([]);
|
||||||
|
|
||||||
const clientsOptions = ref([]);
|
const clientsOptions = ref([]);
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({});
|
||||||
companyFk: null,
|
|
||||||
invoiceDate: null,
|
|
||||||
maxShipped: null,
|
|
||||||
clientId: null,
|
|
||||||
printer: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const optionsInitialData = computed(() => {
|
const optionsInitialData = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,25 +2,32 @@
|
||||||
import { onMounted, onUnmounted, ref } 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 { exportFile, useQuasar } from 'quasar';
|
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
|
||||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
|
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
|
||||||
import { toDate, toCurrency } from 'src/filters/index';
|
|
||||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
import InvoiceOutFilter from './InvoiceOutFilter.vue';
|
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import CardList from 'src/components/ui/CardList.vue';
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import CreateManualInvoiceForm from 'src/components/CreateManualInvoiceForm.vue';
|
||||||
|
|
||||||
|
import InvoiceOutFilter from './InvoiceOutFilter.vue';
|
||||||
|
import { toDate, toCurrency } from 'src/filters/index';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const selectedCards = ref(new Map());
|
|
||||||
const quasar = useQuasar();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
|
||||||
|
const manualInvoiceDialogRef = ref(null);
|
||||||
|
const selectedCards = ref(new Map());
|
||||||
|
|
||||||
onMounted(() => (stateStore.rightDrawer = true));
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
@ -52,36 +59,36 @@ const toggleAllCards = (cardsData) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadCsv = () => {
|
const openPdf = () => {
|
||||||
|
try {
|
||||||
if (selectedCards.value.size === 0) return;
|
if (selectedCards.value.size === 0) return;
|
||||||
const selectedCardsArray = Array.from(selectedCards.value.values());
|
const selectedCardsArray = Array.from(selectedCards.value.values());
|
||||||
let file;
|
|
||||||
for (var i = 0; i < selectedCardsArray.length; i++) {
|
if (selectedCards.value.size === 1) {
|
||||||
if (i == 0) file += Object.keys(selectedCardsArray[i]).join(';') + '\n';
|
const [invoiceOut] = selectedCardsArray;
|
||||||
file +=
|
const url = `api/InvoiceOuts/${invoiceOut.id}/download?access_token=${token}`;
|
||||||
Object.keys(selectedCardsArray[i])
|
window.open(url, '_blank');
|
||||||
.map(function (key) {
|
|
||||||
return selectedCardsArray[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 {
|
} else {
|
||||||
quasar.notify({
|
const invoiceOutIdsArray = selectedCardsArray.map(
|
||||||
message: t('fileDenied'),
|
(invoiceOut) => invoiceOut.id
|
||||||
color: 'negative',
|
);
|
||||||
icon: 'warning',
|
const invoiceOutIds = invoiceOutIdsArray.join(',');
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
access_token: token,
|
||||||
|
ids: invoiceOutIds,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const url = `api/InvoiceOuts/downloadZip?${params}`;
|
||||||
|
window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error opening PDF');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreateInvoiceModal = () => {
|
||||||
|
manualInvoiceDialogRef.value.show();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -119,52 +126,21 @@ const downloadCsv = () => {
|
||||||
<VnPaginate
|
<VnPaginate
|
||||||
auto-load
|
auto-load
|
||||||
data-key="InvoiceOutList"
|
data-key="InvoiceOutList"
|
||||||
order="issued DESC, id DESC"
|
:order="['issued DESC', 'id DESC']"
|
||||||
url="InvoiceOuts/filter"
|
url="InvoiceOuts/filter"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<VnSubToolbar class="bg-vn-dark justify-end">
|
<VnSubToolbar class="bg-vn-dark justify-end">
|
||||||
<template #st-actions>
|
<template #st-actions>
|
||||||
<QBtn
|
<QBtn
|
||||||
@click="downloadCsv()"
|
@click="openPdf()"
|
||||||
class="q-mr-xl"
|
class="q-mr-md"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
icon="cloud_download"
|
||||||
:disable="selectedCards.size === 0"
|
:disable="selectedCards.size === 0"
|
||||||
:label="t('globals.download')"
|
|
||||||
/>
|
|
||||||
<!-- <QBtnDropdown
|
|
||||||
class="q-mr-xl"
|
|
||||||
color="primary"
|
|
||||||
:disable="!manageCheckboxes && arrayElements.length < 1"
|
|
||||||
:label="t('globals.download')"
|
|
||||||
v-else
|
|
||||||
>
|
>
|
||||||
<QList>
|
<QTooltip>{{ t('downloadPdf') }}</QTooltip>
|
||||||
<QItem clickable v-close-popup @click="downloadCsv(rows)">
|
</QBtn>
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>
|
|
||||||
{{
|
|
||||||
t('globals.allRows', {
|
|
||||||
numberRows: rows.length,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
|
|
||||||
<QItem clickable v-close-popup @click="downloadCsv(rows)">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>
|
|
||||||
{{
|
|
||||||
t('globals.selectRows', {
|
|
||||||
numberRows: rows.length,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</QList>
|
|
||||||
</QBtnDropdown> -->
|
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
left-label
|
left-label
|
||||||
:label="t('globals.markAll')"
|
:label="t('globals.markAll')"
|
||||||
|
@ -189,18 +165,23 @@ const downloadCsv = () => {
|
||||||
>
|
>
|
||||||
<template #list-items>
|
<template #list-items>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceOut.list.shortIssued')"
|
:label="t('invoiceOut.list.issued')"
|
||||||
:title-label="t('invoiceOut.list.issued')"
|
|
||||||
:value="toDate(row.issued)"
|
:value="toDate(row.issued)"
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceOut.list.amount')"
|
:label="t('invoiceOut.list.amount')"
|
||||||
:value="toCurrency(row.amount)"
|
:value="toCurrency(row.amount)"
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv :label="t('invoiceOut.list.client')">
|
||||||
:label="t('invoiceOut.list.client')"
|
<template #value>
|
||||||
:value="row.clientSocialName"
|
<span class="link" @click.stop>
|
||||||
|
{{ row?.clientSocialName }}
|
||||||
|
<CustomerDescriptorProxy
|
||||||
|
:id="row?.clientFk"
|
||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceOut.list.shortCreated')"
|
:label="t('invoiceOut.list.shortCreated')"
|
||||||
:title-label="t('invoiceOut.list.created')"
|
:title-label="t('invoiceOut.list.created')"
|
||||||
|
@ -217,13 +198,6 @@ const downloadCsv = () => {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<QBtn
|
|
||||||
:label="t('components.smartCard.openCard')"
|
|
||||||
@click.stop="navigate(row.id)"
|
|
||||||
class="bg-vn-dark"
|
|
||||||
outline
|
|
||||||
type="reset"
|
|
||||||
/>
|
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('components.smartCard.openSummary')"
|
:label="t('components.smartCard.openSummary')"
|
||||||
@click.stop="viewSummary(row.id, InvoiceOutSummary)"
|
@click.stop="viewSummary(row.id, InvoiceOutSummary)"
|
||||||
|
@ -237,6 +211,20 @@ const downloadCsv = () => {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VnPaginate>
|
</VnPaginate>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<QBtn fab icon="add" color="primary" @click="openCreateInvoiceModal()" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('createInvoice') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
|
||||||
|
<QDialog
|
||||||
|
ref="manualInvoiceDialogRef"
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<CreateManualInvoiceForm />
|
||||||
|
</QDialog>
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -246,9 +234,13 @@ en:
|
||||||
fileDenied: Browser denied file download...
|
fileDenied: Browser denied file download...
|
||||||
fileAllowed: Successful download of CSV file
|
fileAllowed: Successful download of CSV file
|
||||||
youCanSearchByInvoiceReference: You can search by invoice reference
|
youCanSearchByInvoiceReference: You can search by invoice reference
|
||||||
|
downloadPdf: Download PDF
|
||||||
|
createInvoice: Make invoice
|
||||||
es:
|
es:
|
||||||
searchInvoice: Buscar factura emitida
|
searchInvoice: Buscar factura emitida
|
||||||
fileDenied: El navegador denegó la descarga de archivos...
|
fileDenied: El navegador denegó la descarga de archivos...
|
||||||
fileAllowed: Descarga exitosa de archivo CSV
|
fileAllowed: Descarga exitosa de archivo CSV
|
||||||
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
|
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
|
||||||
|
downloadPdf: Descargar PDF
|
||||||
|
createInvoice: Crear factura
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onBeforeMount } from 'vue';
|
import { ref, computed, onBeforeMount, onMounted, nextTick } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QCheckbox, QBtn } from 'quasar';
|
|
||||||
|
|
||||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import InvoiceOutNegativeFilter from './InvoiceOutNegativeBasesFilter.vue';
|
import InvoiceOutNegativeFilter from './InvoiceOutNegativeBasesFilter.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
|
||||||
import { toCurrency } from 'src/filters';
|
import { toCurrency } from 'src/filters';
|
||||||
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
|
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
|
||||||
|
|
||||||
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
@ -45,78 +45,16 @@ onBeforeMount(async () => {
|
||||||
stateStore.rightDrawer = true;
|
stateStore.rightDrawer = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const componentIsRendered = ref(false);
|
||||||
|
|
||||||
|
onMounted(() =>
|
||||||
|
nextTick(() => {
|
||||||
|
componentIsRendered.value = true;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const rows = computed(() => arrayData.value.store.data);
|
const rows = computed(() => arrayData.value.store.data);
|
||||||
|
|
||||||
const selectedCustomerId = ref(0);
|
|
||||||
const selectedWorkerId = ref(0);
|
|
||||||
|
|
||||||
const tableColumnComponents = {
|
|
||||||
company: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
country: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
clientId: {
|
|
||||||
component: QBtn,
|
|
||||||
props: () => ({ flat: true, color: 'blue' }),
|
|
||||||
event: (prop) => selectCustomerId(prop.value),
|
|
||||||
},
|
|
||||||
client: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
base: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
ticketId: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
component: QCheckbox,
|
|
||||||
props: (prop) => ({
|
|
||||||
disable: true,
|
|
||||||
'model-value': Boolean(prop.value),
|
|
||||||
}),
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
hasToInvoice: {
|
|
||||||
component: QCheckbox,
|
|
||||||
props: (prop) => ({
|
|
||||||
disable: true,
|
|
||||||
'model-value': Boolean(prop.value),
|
|
||||||
}),
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
verifiedData: {
|
|
||||||
component: QCheckbox,
|
|
||||||
props: (prop) => ({
|
|
||||||
disable: true,
|
|
||||||
'model-value': Boolean(prop.value),
|
|
||||||
}),
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
comercial: {
|
|
||||||
component: QBtn,
|
|
||||||
props: () => ({ flat: true, color: 'blue' }),
|
|
||||||
event: (prop) => selectWorkerId(prop.row.comercialId),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
label: t('invoiceOut.negativeBases.company'),
|
label: t('invoiceOut.negativeBases.company'),
|
||||||
|
@ -205,20 +143,17 @@ const downloadCSV = async () => {
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectCustomerId = (id) => {
|
|
||||||
selectedCustomerId.value = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectWorkerId = (id) => {
|
|
||||||
selectedWorkerId.value = id;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-if="stateStore.isHeaderMounted()">
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
<Teleport
|
||||||
<QBtn color="primary" icon-right="archive" no-caps @click="downloadCSV()" />
|
to="#st-actions"
|
||||||
|
v-if="stateStore?.isSubToolbarShown() && componentIsRendered"
|
||||||
|
>
|
||||||
|
<QBtn color="primary" icon-right="download" no-caps @click="downloadCSV()">
|
||||||
|
<QTooltip>{{ t('Download as CSV') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
@ -236,31 +171,37 @@ const selectWorkerId = (id) => {
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
>
|
>
|
||||||
<template #body-cell="props">
|
<template #body-cell-clientId="{ row }">
|
||||||
<QTd :props="props">
|
<QTd>
|
||||||
<component
|
<QBtn flat dense color="blue"> {{ row.clientId }}</QBtn>
|
||||||
:is="tableColumnComponents[props.col.name].component"
|
<CustomerDescriptorProxy :id="row.clientId" />
|
||||||
class="col-content"
|
</QTd>
|
||||||
v-bind="tableColumnComponents[props.col.name].props(props)"
|
|
||||||
@click="tableColumnComponents[props.col.name].event(props)"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-if="
|
|
||||||
props.col.name !== 'active' &&
|
|
||||||
props.col.name !== 'hasToInvoice' &&
|
|
||||||
props.col.name !== 'verifiedData'
|
|
||||||
"
|
|
||||||
>{{ props.value }}
|
|
||||||
</template>
|
</template>
|
||||||
<CustomerDescriptorProxy
|
<template #body-cell-ticketId="{ row }">
|
||||||
v-if="props.col.name === 'clientId'"
|
<QTd>
|
||||||
:id="selectedCustomerId"
|
<QBtn flat dense color="blue"> {{ row.ticketFk }}</QBtn>
|
||||||
/>
|
<TicketDescriptorProxy :id="row.ticketFk" />
|
||||||
<VnUserLink
|
</QTd>
|
||||||
v-if="props.col.name === 'comercial'"
|
</template>
|
||||||
:worker-id="selectedWorkerId"
|
<template #body-cell-comercial="{ row }">
|
||||||
/>
|
<QTd>
|
||||||
</component>
|
<QBtn flat dense color="blue">{{ row.comercialName }}</QBtn>
|
||||||
|
<WorkerDescriptorProxy :id="row.comercialId" />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-active="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QCheckbox :model-value="!!row.isActive" disable />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-hasToInvoice="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QCheckbox :model-value="!!row.hasToInvoice" disable />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-verifiedData="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QCheckbox :model-value="!!row.isTaxDataChecked" disable />
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
|
@ -274,4 +215,7 @@ const selectWorkerId = (id) => {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<i18n></i18n>
|
<i18n>
|
||||||
|
es:
|
||||||
|
Download as CSV: Descargar como CSV
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -41,7 +41,7 @@ const quasar = useQuasar();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getToken } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const getItemAvatar = async () => {
|
const getItemAvatar = async () => {
|
||||||
const token = getToken();
|
const token = getTokenMultimedia();
|
||||||
const timeStamp = `timestamp=${Date.now()}`;
|
const timeStamp = `timestamp=${Date.now()}`;
|
||||||
image.value = `/api/Images/catalog/200x200/${entityId.value}/download?access_token=${token}&${timeStamp}`;
|
image.value = `/api/Images/catalog/200x200/${entityId.value}/download?access_token=${token}&${timeStamp}`;
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,8 +30,15 @@ async function onSubmit() {
|
||||||
const { data } = await axios.post('Accounts/login', params);
|
const { data } = await axios.post('Accounts/login', params);
|
||||||
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
const {
|
||||||
|
data: { multimediaToken },
|
||||||
|
} = await axios.get('VnUsers/ShareToken', {
|
||||||
|
headers: { Authorization: data.token },
|
||||||
|
});
|
||||||
|
|
||||||
await session.login(data.token, keepLogin.value);
|
if (!multimediaToken) return;
|
||||||
|
|
||||||
|
await session.login(data.token, multimediaToken.id, keepLogin.value);
|
||||||
|
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: t('login.loginSuccess'),
|
message: t('login.loginSuccess'),
|
||||||
|
|
|
@ -11,8 +11,8 @@ import toCurrency from '../../../filters/toCurrency';
|
||||||
|
|
||||||
const DEFAULT_PRICE_KG = 0;
|
const DEFAULT_PRICE_KG = 0;
|
||||||
|
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
|
|
|
@ -17,9 +17,9 @@ import axios from 'axios';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
const orderSummary = ref({
|
const orderSummary = ref({
|
||||||
total: null,
|
total: null,
|
||||||
vat: null,
|
vat: null,
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const agencyAgreementList = ref([]);
|
||||||
|
const supplierList = ref([]);
|
||||||
|
|
||||||
|
const exprBuilder = (param, value) => {
|
||||||
|
switch (param) {
|
||||||
|
case 'agencyModeFk':
|
||||||
|
return { 'a.agencyModeFk': value };
|
||||||
|
case 'supplierFk':
|
||||||
|
return { 'a.supplierName': value };
|
||||||
|
case 'routeFk':
|
||||||
|
return { 'a.routeFk': value };
|
||||||
|
case 'created':
|
||||||
|
case 'agencyFk':
|
||||||
|
case 'packages':
|
||||||
|
case 'm3':
|
||||||
|
case 'kmTotal':
|
||||||
|
case 'price':
|
||||||
|
case 'invoiceInFk':
|
||||||
|
return { [`a.${param}`]: value };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="AgencyModes"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (agencyList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Agencies"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (agencyAgreementList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers"
|
||||||
|
:filter="{ fields: ['name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (supplierList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:expr-builder="exprBuilder"
|
||||||
|
search-button
|
||||||
|
@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 }">
|
||||||
|
<QList dense>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.routeFk" :label="t('ID')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm" v-if="agencyList">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency route')"
|
||||||
|
v-model="params.agencyModeFk"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
is-clearable
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm" v-if="agencyAgreementList">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency agreement')"
|
||||||
|
v-model="params.agencyFk"
|
||||||
|
:options="agencyAgreementList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
is-clearable
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm" v-if="supplierList">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Autonomous')"
|
||||||
|
v-model="params.supplierFk"
|
||||||
|
:options="supplierList"
|
||||||
|
option-value="name"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
is-clearable
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.created"
|
||||||
|
:label="t('Date')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.from"
|
||||||
|
:label="t('From')"
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.to"
|
||||||
|
:label="t('To')"
|
||||||
|
is-outlined
|
||||||
|
is-clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.packages" :label="t('Packages')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.m3" :label="t('m3')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.kmTotal" :label="t('Km')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.price" :label="t('Price')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.invoiceInFk" :label="t('Received')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
agencyModeFk: Agency route
|
||||||
|
m3: m³
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
date: Date
|
||||||
|
agencyFk: Agency agreement
|
||||||
|
packages: Packages
|
||||||
|
price: Price
|
||||||
|
invoiceInFk: Received
|
||||||
|
supplierFk: Autonomous
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
agencyModeFk: Agencia ruta
|
||||||
|
m3: m³
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
date: Fecha
|
||||||
|
agencyFk: Agencia Acuerdo
|
||||||
|
packages: Bultos
|
||||||
|
price: Precio
|
||||||
|
invoiceInFk: Recibida
|
||||||
|
supplierFk: Autónomos
|
||||||
|
From: Desde
|
||||||
|
To: Hasta
|
||||||
|
Date: Fecha
|
||||||
|
Agency route: Agencia Ruta
|
||||||
|
Agency agreement: Agencia Acuerdo
|
||||||
|
Packages: Bultos
|
||||||
|
Price: Precio
|
||||||
|
Received: Recibida
|
||||||
|
Autonomous: Autónomos
|
||||||
|
</i18n>
|
|
@ -11,12 +11,15 @@ import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import VnInputTime from 'components/common/VnInputTime.vue';
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
import RouteSearchbar from "pages/Route/Card/RouteSearchbar.vue";
|
||||||
|
import {useStateStore} from "stores/useStateStore";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const shelvingId = route.params?.id || null;
|
const stateStore = useStateStore();
|
||||||
const isNew = Boolean(!shelvingId);
|
const shelvingId = ref(route.params?.id || null);
|
||||||
|
const isNew = Boolean(!shelvingId.value);
|
||||||
const defaultInitialData = {
|
const defaultInitialData = {
|
||||||
agencyModeFk: null,
|
agencyModeFk: null,
|
||||||
created: null,
|
created: null,
|
||||||
|
@ -78,6 +81,11 @@ const onSave = (data, response) => {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VnSubToolbar />
|
<VnSubToolbar />
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<RouteSearchbar />
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
<FetchData
|
<FetchData
|
||||||
url="Workers/search"
|
url="Workers/search"
|
||||||
:filter="{ fields: ['id', 'nickname'] }"
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
@ -89,7 +97,7 @@ const onSave = (data, response) => {
|
||||||
<FetchData
|
<FetchData
|
||||||
url="AgencyModes/isActive"
|
url="AgencyModes/isActive"
|
||||||
:filter="{ fields: ['id', 'name'] }"
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
sort-by="name ASC"
|
sort-by="name"
|
||||||
limit="30"
|
limit="30"
|
||||||
@on-fetch="(data) => (agencyList = data)"
|
@on-fetch="(data) => (agencyList = data)"
|
||||||
auto-load
|
auto-load
|
||||||
|
@ -103,13 +111,13 @@ const onSave = (data, response) => {
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FormModel
|
<FormModel
|
||||||
:url="isNew ? null : `Routes/${shelvingId}`"
|
:url="isNew ? null : `Routes/${route.params?.id}`"
|
||||||
:url-create="isNew ? 'Routes' : null"
|
:url-create="isNew ? 'Routes' : null"
|
||||||
:observe-form-changes="!isNew"
|
:observe-form-changes="!isNew"
|
||||||
:filter="routeFilter"
|
:filter="routeFilter"
|
||||||
model="route"
|
model="route"
|
||||||
:auto-load="!isNew"
|
:auto-load="!isNew"
|
||||||
:form-initial-data="defaultInitialData"
|
:form-initial-data="isNew ? defaultInitialData : null"
|
||||||
@on-data-saved="onSave"
|
@on-data-saved="onSave"
|
||||||
>
|
>
|
||||||
<template #form="{ data }">
|
<template #form="{ data }">
|
||||||
|
@ -131,7 +139,7 @@ const onSave = (data, response) => {
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<QItemLabel>{{ opt.name }}</QItemLabel>
|
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||||
<QItemLabel caption>
|
<QItemLabel caption>
|
||||||
{{ opt.nickname }},{{ opt.code }}
|
{{ opt.nickname }}, {{ opt.code }}
|
||||||
</QItemLabel>
|
</QItemLabel>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
@ -212,6 +220,8 @@ const onSave = (data, response) => {
|
||||||
<VnInput
|
<VnInput
|
||||||
v-model="data.description"
|
v-model="data.description"
|
||||||
:label="t('Description')"
|
:label="t('Description')"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -230,4 +240,5 @@ es:
|
||||||
Hour finished: Hora fin
|
Hour finished: Hora fin
|
||||||
Description: Descripción
|
Description: Descripción
|
||||||
Is served: Se ha servido
|
Is served: Se ha servido
|
||||||
|
Created: Creado
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
<script setup>
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
|
||||||
|
const columns = ref([
|
||||||
|
{
|
||||||
|
name: 'ticket',
|
||||||
|
label: t('Ticket'),
|
||||||
|
field: (row) => row.id,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'client',
|
||||||
|
label: t('Client'),
|
||||||
|
field: (row) => row.nickname,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'province',
|
||||||
|
label: t('Province'),
|
||||||
|
field: (row) => row?.address?.province?.name,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'city',
|
||||||
|
label: t('City'),
|
||||||
|
field: (row) => row?.address?.city,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pc',
|
||||||
|
label: t('PC'),
|
||||||
|
field: (row) => row?.address?.postalCode,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'address',
|
||||||
|
label: t('Address'),
|
||||||
|
field: (row) => row?.address?.street,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'zone',
|
||||||
|
label: t('Zone'),
|
||||||
|
field: (row) => row?.zone?.name,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const isLoading = ref(true);
|
||||||
|
const ticketList = ref([]);
|
||||||
|
|
||||||
|
const onDataFetched = (data) => {
|
||||||
|
ticketList.value = data;
|
||||||
|
isLoading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedTicket = ref(null);
|
||||||
|
const unlinkZone = async (ticket) => {
|
||||||
|
await axios.post('Routes/unlink', {
|
||||||
|
agencyModeId: ticket?.agencyModeFk,
|
||||||
|
zoneId: ticket.zoneFk,
|
||||||
|
});
|
||||||
|
selectedTicket.value = null;
|
||||||
|
refreshKey.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setTicketsRoute = async () => {
|
||||||
|
if (!selectedRows.value?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
(selectedRows.value || [])
|
||||||
|
.filter((ticket) => ticket?.id)
|
||||||
|
.map((ticket) =>
|
||||||
|
axios.patch(`Routes/${$props.id}/insertTicket`, { ticketId: ticket.id })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
await axios.post(`Routes/${$props.id}/updateVolume`);
|
||||||
|
emit('ok');
|
||||||
|
emit('hide');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
:key="refreshKey"
|
||||||
|
:url="`Routes/${$props.id}/getSuggestedTickets`"
|
||||||
|
@on-fetch="onDataFetched"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<QDialog
|
||||||
|
:model-value="Boolean(selectedTicket)"
|
||||||
|
@update:model-value="(value) => (!value ? (selectedTicket = null) : null)"
|
||||||
|
class="confirmation-dialog"
|
||||||
|
>
|
||||||
|
<QCard style="min-width: 350px">
|
||||||
|
<QCardSection>
|
||||||
|
<p class="text-h6 q-ma-none">{{ t('Unlink selected zone?') }}</p>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
v-close-popup
|
||||||
|
class="text-primary"
|
||||||
|
/>
|
||||||
|
<QBtn color="primary" v-close-popup @click="unlinkZone(selectedTicket)">
|
||||||
|
{{ t('Unlink') }}
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<QCard class="full-width dialog">
|
||||||
|
<QCardSection class="row items-center q-pb-none">
|
||||||
|
<div class="text-h6">{{ t('Tickets to add') }}</div>
|
||||||
|
<QSpace />
|
||||||
|
<QBtn icon="close" flat round dense v-close-popup />
|
||||||
|
</QCardSection>
|
||||||
|
<QCardSection>
|
||||||
|
<QTable
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="isLoading"
|
||||||
|
:rows="ticketList"
|
||||||
|
flat
|
||||||
|
row-key="id"
|
||||||
|
selection="multiple"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
|
<template #body-cell-ticket="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<span class="link">
|
||||||
|
{{ props.value }}
|
||||||
|
<TicketDescriptorProxy :id="props?.row?.id" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-client="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<span class="link">
|
||||||
|
{{ props.value }}
|
||||||
|
<CustomerDescriptorProxy :id="props?.row?.clientFk" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-actions="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<QIcon
|
||||||
|
name="link_off"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="selectedTicket = props?.row"
|
||||||
|
>
|
||||||
|
<QTooltip
|
||||||
|
>{{
|
||||||
|
t('Unlink zone', {
|
||||||
|
zone: props?.row?.zone?.name,
|
||||||
|
agency: props?.row?.agencyMode?.name,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn flat v-close-popup class="text-primary"
|
||||||
|
>{{ t('globals.cancel') }}
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="setTicketsRoute"
|
||||||
|
>{{ t('globals.add') }}
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.confirmation-dialog {
|
||||||
|
z-index: 6001 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
max-width: 1280px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
Unlink zone: Unlink zone {zone} from agency {agency}
|
||||||
|
es:
|
||||||
|
Tickets to add: Tickets a añadir
|
||||||
|
Client: Cliente
|
||||||
|
Province: Provincia
|
||||||
|
City: Población
|
||||||
|
PC: CP
|
||||||
|
Address: Dirección
|
||||||
|
Zone: Zona
|
||||||
|
Unlink zone: Desvincular zona {zone} de agencia {agency}
|
||||||
|
Unlink: Desvincular
|
||||||
|
</i18n>
|
|
@ -9,6 +9,7 @@ const { t } = useI18n();
|
||||||
data-key="RouteList"
|
data-key="RouteList"
|
||||||
:label="t('Search route')"
|
:label="t('Search route')"
|
||||||
:info="t('You can search by route reference')"
|
:info="t('You can search by route reference')"
|
||||||
|
custom-route-redirect-name="RouteList"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@ import VnLv from 'components/ui/VnLv.vue';
|
||||||
import { QIcon } from 'quasar';
|
import { QIcon } from 'quasar';
|
||||||
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
|
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
|
||||||
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
import axios from 'axios';
|
|
||||||
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
||||||
|
import { openBuscaman } from 'src/utils/buscaman';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -104,7 +105,7 @@ const ticketColumns = ref([
|
||||||
label: t('route.summary.ticket'),
|
label: t('route.summary.ticket'),
|
||||||
field: (row) => row?.id,
|
field: (row) => row?.id,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'right',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'observations',
|
name: 'observations',
|
||||||
|
@ -114,30 +115,20 @@ const ticketColumns = ref([
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const openBuscaman = async (route, ticket) => {
|
|
||||||
if (!route.vehicleFk) throw new Error(`The route doesn't have a vehicle`);
|
|
||||||
const response = await axios.get(`Routes/${route.vehicleFk}/getDeliveryPoint`);
|
|
||||||
if (!response.data)
|
|
||||||
throw new Error(`The route's vehicle doesn't have a delivery point`);
|
|
||||||
|
|
||||||
const address = `${response.data}+to:${ticket.postalCode} ${ticket.city} ${ticket.street}`;
|
|
||||||
window.open(
|
|
||||||
'https://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=' +
|
|
||||||
encodeURI(address),
|
|
||||||
'_blank'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="q-pa-md">
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
<CardSummary ref="summary" :url="`Routes/${entityId}/summary`">
|
<Teleport to="#searchbar">
|
||||||
<template #header-left>
|
<RouteSearchbar />
|
||||||
<RouterLink :to="{ name: `RouteSummary`, params: { id: entityId } }">
|
</Teleport>
|
||||||
<QIcon name="open_in_new" color="white" size="sm" />
|
|
||||||
</RouterLink>
|
|
||||||
</template>
|
</template>
|
||||||
|
<div class="q-pa-md full-width">
|
||||||
|
<CardSummary
|
||||||
|
ref="summary"
|
||||||
|
:url="`Routes/${entityId}/summary`"
|
||||||
|
:entity-id="entityId"
|
||||||
|
>
|
||||||
<template #header="{ entity }">
|
<template #header="{ entity }">
|
||||||
<span>{{ `${entity?.route.id} - ${entity?.route?.description}` }}</span>
|
<span>{{ `${entity?.route.id} - ${entity?.route?.description}` }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -207,9 +198,10 @@ const openBuscaman = async (route, ticket) => {
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
||||||
<QCard class="vn-max">
|
<QCard class="vn-max">
|
||||||
<div class="header">
|
<a class="header" :href="`#/route/${entityId}/tickets`">
|
||||||
{{ t('route.summary.tickets') }}
|
{{ t('route.summary.tickets') }}
|
||||||
</div>
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
</a>
|
||||||
<QTable
|
<QTable
|
||||||
:columns="ticketColumns"
|
:columns="ticketColumns"
|
||||||
:rows="entity?.tickets"
|
:rows="entity?.tickets"
|
||||||
|
@ -222,7 +214,7 @@ const openBuscaman = async (route, ticket) => {
|
||||||
<QTd auto-width>
|
<QTd auto-width>
|
||||||
<span
|
<span
|
||||||
class="text-primary cursor-pointer"
|
class="text-primary cursor-pointer"
|
||||||
@click="openBuscaman(entity?.route, row)"
|
@click="openBuscaman(entity?.route?.vehicleFk, [row])"
|
||||||
>
|
>
|
||||||
{{ value }}
|
{{ value }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -237,7 +229,7 @@ const openBuscaman = async (route, ticket) => {
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-ticket="{ value, row }">
|
<template #body-cell-ticket="{ value, row }">
|
||||||
<QTd auto-width>
|
<QTd auto-width class="text-center">
|
||||||
<span class="text-primary cursor-pointer">
|
<span class="text-primary cursor-pointer">
|
||||||
{{ value }}
|
{{ value }}
|
||||||
<TicketDescriptorProxy :id="row?.id" />
|
<TicketDescriptorProxy :id="row?.id" />
|
||||||
|
|
|
@ -12,8 +12,8 @@ import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
const selected = ref([]);
|
const selected = ref([]);
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
|
@ -91,6 +91,24 @@ function downloadPdfs() {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
<template v-if="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="column items-center">
|
<div class="column items-center">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<VnPaginate
|
<VnPaginate
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
<script setup>
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import RoadmapAddStopForm from 'pages/Route/Roadmap/RoadmapAddStopForm.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
roadmapFk: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const roadmapStopForm = reactive({
|
||||||
|
roadmapFk: Number(props.roadmapFk) || 0,
|
||||||
|
warehouseFk: null,
|
||||||
|
eta: new Date().toISOString(),
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateDefaultStop = (data) => {
|
||||||
|
const eta = new Date(data.etd);
|
||||||
|
eta.setDate(eta.getDate() + 1);
|
||||||
|
roadmapStopForm.eta = eta.toISOString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = async () => {
|
||||||
|
isLoading.value = true;
|
||||||
|
try {
|
||||||
|
await axios.post('ExpeditionTrucks', { ...roadmapStopForm });
|
||||||
|
emit('ok');
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
emit('hide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
:url="`Roadmaps/${roadmapFk}`"
|
||||||
|
auto-load
|
||||||
|
:filter="{ fields: ['etd'] }"
|
||||||
|
@on-fetch="updateDefaultStop"
|
||||||
|
/>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<QCard class="q-pa-lg">
|
||||||
|
<QCardSection class="row absolute absolute-top z-fullscreen">
|
||||||
|
<QSpace />
|
||||||
|
<QBtn icon="close" flat round dense v-close-popup />
|
||||||
|
</QCardSection>
|
||||||
|
<RoadmapAddStopForm
|
||||||
|
:roadmap-fk="roadmapFk"
|
||||||
|
:form-data="roadmapStopForm"
|
||||||
|
layout="dialog"
|
||||||
|
/>
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click="onSave"
|
||||||
|
unelevated
|
||||||
|
/>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card-section {
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Warehouse: Almacén
|
||||||
|
ETA date: Fecha ETA
|
||||||
|
ETA time: Hora ETA
|
||||||
|
Description: Descripción
|
||||||
|
</i18n>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
formData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
roadmapFk: {
|
||||||
|
type: [Number, String],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: 'row',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const warehouseList = ref([]);
|
||||||
|
const form = computed(() => props.formData);
|
||||||
|
const isDialog = computed(() => props.layout === 'dialog');
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Warehouses"
|
||||||
|
auto-load
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (warehouseList = data)"
|
||||||
|
/>
|
||||||
|
<div :class="[isDialog ? 'column' : 'form-gap', 'full-width flex']">
|
||||||
|
<QCardSection class="flex-grow q-px-none flex-1">
|
||||||
|
<VnSelectFilter
|
||||||
|
v-model.number="form.warehouseFk"
|
||||||
|
class="full-width"
|
||||||
|
:label="t('Warehouse')"
|
||||||
|
:options="warehouseList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<div :class="[!isDialog ? 'flex-2' : null, 'form-gap flex no-wrap']">
|
||||||
|
<QCardSection class="row q-px-none flex-1">
|
||||||
|
<VnInputDate
|
||||||
|
v-model="form.eta"
|
||||||
|
class="full-width"
|
||||||
|
:label="t('ETA date')"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardSection class="row q-px-none flex-1">
|
||||||
|
<VnInputTime
|
||||||
|
v-model="form.eta"
|
||||||
|
class="full-width"
|
||||||
|
:label="t('ETA hour')"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
</div>
|
||||||
|
<QCardSection class="q-px-none flex-1">
|
||||||
|
<VnInput
|
||||||
|
v-model="form.description"
|
||||||
|
class="full-width"
|
||||||
|
:label="t('Description')"
|
||||||
|
type="textarea"
|
||||||
|
:rows="1"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-gap {
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-1 {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-2 {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Warehouse: Almacén
|
||||||
|
ETA date: Fecha ETA
|
||||||
|
ETA hour: Hora ETA
|
||||||
|
Description: Descripción
|
||||||
|
Remove stop: Eliminar parada
|
||||||
|
Add stop: Añadir parada
|
||||||
|
</i18n>
|
|
@ -0,0 +1,143 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import {ref} from "vue";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const supplierList = ref([]);
|
||||||
|
const filter = { include: [{ relation: 'supplier' }] };
|
||||||
|
const onSave = (data, response) => {
|
||||||
|
router.push({ name: 'RoadmapSummary', params: { id: response?.id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnSubToolbar />
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers"
|
||||||
|
auto-load
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
sort-by="nickname"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (supplierList = data)"
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url="`Roadmaps/${route.params?.id}`"
|
||||||
|
observe-form-changes
|
||||||
|
:filter="filter"
|
||||||
|
model="roadmap"
|
||||||
|
auto-load
|
||||||
|
@on-data-saved="onSave"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput v-model="data.name" :label="t('Roadmap')" clearable />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate v-model="data.etd" :label="t('ETD date')" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputTime v-model="data.etd" :label="t('ETD hour')" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.tractorPlate"
|
||||||
|
:label="t('Tractor plate')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.trailerPlate"
|
||||||
|
:label="t('Trailer plate')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Carrier')"
|
||||||
|
v-model="data.supplierFk"
|
||||||
|
:options="supplierList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel
|
||||||
|
>{{ opt.id }} - {{ opt.nickname }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.price"
|
||||||
|
:label="t('Price')"
|
||||||
|
type="number"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.driverName"
|
||||||
|
:label="t('Driver name')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.phone"
|
||||||
|
:label="t('Phone')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.observations"
|
||||||
|
:label="t('Observations')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Roadmap: Troncal
|
||||||
|
ETD date: Fecha ETD
|
||||||
|
ETD hour: Hora ETD
|
||||||
|
Tractor plate: Matrícula tractor
|
||||||
|
Trailer plate: Matrícula trailer
|
||||||
|
Carrier: Transportista
|
||||||
|
Price: Precio
|
||||||
|
Driver name: Nombre del conductor
|
||||||
|
Phone: Teléfono
|
||||||
|
Observations: Observaciones
|
||||||
|
</i18n>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup>
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import RoadmapDescriptor from "pages/Route/Roadmap/RoadmapDescriptor.vue";
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
|
<QScrollArea class="fit">
|
||||||
|
<RoadmapDescriptor />
|
||||||
|
<QSeparator />
|
||||||
|
<LeftMenu source="card" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPageContainer>
|
||||||
|
<QPage>
|
||||||
|
<RouterView></RouterView>
|
||||||
|
</QPage>
|
||||||
|
</QPageContainer>
|
||||||
|
</template>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const defaultInitialData = {
|
||||||
|
etd: Date.vnNew().toISOString(),
|
||||||
|
};
|
||||||
|
const onSave = (data, response) => {
|
||||||
|
router.push({ name: 'RoadmapSummary', params: { id: response?.id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnSubToolbar />
|
||||||
|
<FormModel
|
||||||
|
:url="null"
|
||||||
|
url-create="Roadmaps"
|
||||||
|
model="roadmap"
|
||||||
|
:observe-form-changes="false"
|
||||||
|
:auto-load="false"
|
||||||
|
:form-initial-data="defaultInitialData"
|
||||||
|
@on-data-saved="onSave"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.name"
|
||||||
|
:label="t('Roadmap')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate v-model="data.etd" :label="t('ETD date')" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputTime v-model="data.etd" :label="t('ETD hour')" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Roadmap: Troncal
|
||||||
|
ETD date: Fecha ETD
|
||||||
|
ETD hour: Hora ETD
|
||||||
|
</i18n>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<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 'components/ui/VnLv.vue';
|
||||||
|
import useCardDescription from 'composables/useCardDescription';
|
||||||
|
import {dashIfEmpty, toDateHour} from 'src/filters';
|
||||||
|
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
import RoadmapDescriptorMenu from "pages/Route/Roadmap/RoadmapDescriptorMenu.vue";
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const entityId = computed(() => {
|
||||||
|
return $props.id || route.params.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = { include: [{ relation: 'supplier' }] };
|
||||||
|
const data = ref(useCardDescription());
|
||||||
|
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardDescriptor
|
||||||
|
module="Roadmap"
|
||||||
|
:url="`Roadmaps/${entityId}`"
|
||||||
|
:filter="filter"
|
||||||
|
:title="data.title"
|
||||||
|
:subtitle="data.subtitle"
|
||||||
|
data-key="Roadmap"
|
||||||
|
@on-fetch="setData"
|
||||||
|
>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<VnLv :label="t('Roadmap')" :value="entity?.name" />
|
||||||
|
<VnLv :label="t('ETD')" :value="toDateHour(entity?.etd)" />
|
||||||
|
<VnLv :label="t('Carrier')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link" v-if="entity?.supplier?.id">
|
||||||
|
{{ dashIfEmpty(entity?.supplier?.nickname) }}
|
||||||
|
<SupplierDescriptorProxy :id="entity?.supplier?.id" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
</template>
|
||||||
|
<template #menu="{ entity }">
|
||||||
|
<RoadmapDescriptorMenu :route="entity" />
|
||||||
|
</template>
|
||||||
|
</CardDescriptor>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Roadmap: Troncal
|
||||||
|
Carrier: Transportista
|
||||||
|
</i18n>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
route: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
function confirmRemove() {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Confirm deletion'),
|
||||||
|
message: t('Are you sure you want to delete this roadmap?'),
|
||||||
|
promise: remove,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => await router.push({ name: 'RouteRoadmap' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove() {
|
||||||
|
if (!props.route.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await axios.delete(`Roadmaps/${props.route.id}`);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataDeleted'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QItem @click="confirmRemove" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="delete" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('Delete roadmap') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Confirm deletion: Confirmar eliminación
|
||||||
|
Are you sure you want to delete this roadmap?: ¿Estás seguro de que quieres eliminar esta troncal?
|
||||||
|
Delete roadmap: Eliminar troncal
|
||||||
|
</i18n>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup>
|
||||||
|
import RoadmapDescriptor from 'pages/Route/Roadmap/RoadmapDescriptor.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QPopupProxy>
|
||||||
|
<RoadmapDescriptor v-if="$props.id" :id="$props.id" />
|
||||||
|
</QPopupProxy>
|
||||||
|
</template>
|
|
@ -0,0 +1,183 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const supplierList = ref([]);
|
||||||
|
const exprBuilder = (param, value) => {
|
||||||
|
switch (param) {
|
||||||
|
case 'tractorPlate':
|
||||||
|
case 'trailerPlate':
|
||||||
|
case 'driverName':
|
||||||
|
case 'phone':
|
||||||
|
return { [param]: { like: `%${value}%` } };
|
||||||
|
case 'supplierFk':
|
||||||
|
case 'price':
|
||||||
|
return { [param]: value };
|
||||||
|
case 'from':
|
||||||
|
return { etd: { gte: value } };
|
||||||
|
case 'to':
|
||||||
|
return { etd: { lte: value } };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers"
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
sort-by="nickname"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (supplierList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:search-button="true"
|
||||||
|
:expr-builder="exprBuilder"
|
||||||
|
@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 class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate v-model="params.from" :label="t('From')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate v-model="params.to" :label="t('To')" is-outlined />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.tractorPlate"
|
||||||
|
:label="t('Tractor Plate')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.trailerPlate"
|
||||||
|
:label="t('Trailer Plate')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-if="supplierList" class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Carrier')"
|
||||||
|
v-model="params.supplierFk"
|
||||||
|
:options="supplierList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel
|
||||||
|
>{{ opt.id }} - {{ opt.nickname }}</QItemLabel
|
||||||
|
>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.price"
|
||||||
|
:label="t('Price')"
|
||||||
|
type="number"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.driverName"
|
||||||
|
:label="t('Driver name')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.phone"
|
||||||
|
:label="t('Phone')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
tractorPlate: Tractor Plate
|
||||||
|
trailerPlate: Trailer Plate
|
||||||
|
supplierFk: Carrier
|
||||||
|
price: Price
|
||||||
|
driverName: Driver name
|
||||||
|
phone: Phone
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
tractorPlate: Matrícula del tractor
|
||||||
|
trailerPlate: Matrícula del trailer
|
||||||
|
supplierFk: Transportista
|
||||||
|
price: Precio
|
||||||
|
driverName: Nombre del conductor
|
||||||
|
phone: Teléfono
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
From: Desde
|
||||||
|
To: Hasta
|
||||||
|
Tractor Plate: Matrícula del tractor
|
||||||
|
Trailer Plate: Matrícula del trailer
|
||||||
|
Carrier: Transportista
|
||||||
|
Price: Precio
|
||||||
|
Driver name: Nombre del conductor
|
||||||
|
Phone: Teléfono
|
||||||
|
</i18n>
|
|
@ -0,0 +1,95 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import RoadmapAddStopForm from "pages/Route/Roadmap/RoadmapAddStopForm.vue";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const roadmapStopsCrudRef = ref(null);
|
||||||
|
|
||||||
|
const defaultStop = ref({
|
||||||
|
roadmapFk: Number(route.params?.id) || 0,
|
||||||
|
eta: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateDefaultStop = (data) => {
|
||||||
|
const eta = new Date(data.etd);
|
||||||
|
eta.setDate(eta.getDate() + 1);
|
||||||
|
defaultStop.value.eta = eta.toISOString();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (roadmapStopsCrudRef.value) roadmapStopsCrudRef.value.reload();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnSubToolbar />
|
||||||
|
<FetchData
|
||||||
|
:url="`Roadmaps/${route.params?.id}`"
|
||||||
|
auto-load
|
||||||
|
:filter="{ fields: ['etd'] }"
|
||||||
|
@on-fetch="updateDefaultStop"
|
||||||
|
/>
|
||||||
|
<div class="q-pa-lg">
|
||||||
|
<CrudModel
|
||||||
|
ref="roadmapStopsCrudRef"
|
||||||
|
data-key="ExpeditionTrucks"
|
||||||
|
url="ExpeditionTrucks"
|
||||||
|
model="ExpeditionTrucks"
|
||||||
|
:filter="{ where: { roadmapFk: route.params?.id } }"
|
||||||
|
:default-remove="false"
|
||||||
|
:data-required="defaultStop"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard class="q-pa-md">
|
||||||
|
<QCardSection
|
||||||
|
v-for="(row, index) in rows"
|
||||||
|
:key="index"
|
||||||
|
class="row no-wrap"
|
||||||
|
>
|
||||||
|
<RoadmapAddStopForm :roadmap-fk="route.params?.id" :form-data="row" />
|
||||||
|
<div class="col-1 row justify-center items-center">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="roadmapStopsCrudRef.remove([row])"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Remove stop') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardSection>
|
||||||
|
<QIcon
|
||||||
|
name="add"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="roadmapStopsCrudRef.insert()"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add stop') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QCardSection>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Warehouse: Almacén
|
||||||
|
ETA date: Fecha ETA
|
||||||
|
ETA hour: Hora ETA
|
||||||
|
Description: Descripción
|
||||||
|
Remove stop: Eliminar parada
|
||||||
|
Add stop: Añadir parada
|
||||||
|
</i18n>
|
|
@ -0,0 +1,172 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { QIcon, useQuasar } from 'quasar';
|
||||||
|
import { dashIfEmpty, toDateHour } from 'src/filters';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
import VnLinkPhone from 'components/ui/VnLinkPhone.vue';
|
||||||
|
import RoadmapAddStopDialog from 'pages/Route/Roadmap/RoadmapAddStopDialog.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const route = useRoute();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const summary = ref(null);
|
||||||
|
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 columns = ref([
|
||||||
|
{
|
||||||
|
name: 'warehouse',
|
||||||
|
label: t('Warehouse'),
|
||||||
|
field: (row) => dashIfEmpty(row?.warehouse?.name),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ETA',
|
||||||
|
label: t('ETA'),
|
||||||
|
field: (row) => toDateHour(row?.eta),
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const filter = {
|
||||||
|
include: [
|
||||||
|
{ relation: 'supplier' },
|
||||||
|
{ relation: 'worker' },
|
||||||
|
{
|
||||||
|
relation: 'expeditionTruck',
|
||||||
|
scope: { include: [{ relation: 'warehouse' }] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const openAddStopDialog = () => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: RoadmapAddStopDialog,
|
||||||
|
componentProps: { roadmapFk: entityId.value },
|
||||||
|
})
|
||||||
|
.onOk(() => summary.value.fetch());
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="q-pa-md full-width">
|
||||||
|
<CardSummary ref="summary" :url="`Roadmaps/${entityId}`" :filter="filter">
|
||||||
|
<template #header-left>
|
||||||
|
<RouterLink :to="{ name: `RoadmapSummary`, params: { id: entityId } }">
|
||||||
|
<QIcon name="open_in_new" color="white" size="sm" />
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #header="{ entity }">
|
||||||
|
<span>{{ `${entity?.id} - ${entity?.name}` }}</span>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnLv :label="t('Carrier')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link" v-if="entity?.supplier?.id">
|
||||||
|
{{ dashIfEmpty(entity?.supplier?.nickname) }}
|
||||||
|
<SupplierDescriptorProxy :id="entity?.supplier?.id" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv :label="t('ETD')" :value="toDateHour(entity?.etd)" />
|
||||||
|
<VnLv
|
||||||
|
:label="t('Tractor Plate')"
|
||||||
|
:value="dashIfEmpty(entity?.tractorPlate)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('Trailer Plate')"
|
||||||
|
:value="dashIfEmpty(entity?.trailerPlate)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnLv :label="t('Phone')" :value="dashIfEmpty(entity?.phone)">
|
||||||
|
<template #value>
|
||||||
|
<span>
|
||||||
|
{{ dashIfEmpty(entity?.phone) }}
|
||||||
|
<VnLinkPhone :phone-number="entity?.phone" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('Worker')"
|
||||||
|
:value="
|
||||||
|
entity?.worker?.firstName
|
||||||
|
? `${entity?.worker?.firstName} ${entity?.worker?.lastName}`
|
||||||
|
: '-'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('Observations')"
|
||||||
|
:value="dashIfEmpty(entity?.observations)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-max">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a class="header" :href="`#/route/roadmap/${entityId}/stops`">
|
||||||
|
{{ t('Stops') }}
|
||||||
|
<QIcon name="open_in_new" color="primary">
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Go to stops') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</a>
|
||||||
|
<QIcon
|
||||||
|
name="add_circle"
|
||||||
|
color="primary"
|
||||||
|
class="q-ml-lg header cursor-pointer"
|
||||||
|
@click.stop="openAddStopDialog"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add stop') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:rows="entity?.expeditionTruck"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
row-key="id"
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CardSummary>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Carrier: Transportista
|
||||||
|
Tractor Plate: Matrícula tractor
|
||||||
|
Trailer Plate: Matrícula trailer
|
||||||
|
Phone: Teléfono
|
||||||
|
Worker: Trabajador
|
||||||
|
Observations: Observaciones
|
||||||
|
Stops: Paradas
|
||||||
|
Warehouse: Almacén
|
||||||
|
Go to stops: Ir a paradas
|
||||||
|
Add stop: Añadir parada
|
||||||
|
</i18n>
|
|
@ -0,0 +1,344 @@
|
||||||
|
<script setup>
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { dashIfEmpty, toCurrency, toDate } from 'src/filters';
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
import RouteDescriptorProxy from 'pages/Route/Card/RouteDescriptorProxy.vue';
|
||||||
|
import InvoiceInDescriptorProxy from 'pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
|
||||||
|
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import useNotify from 'composables/useNotify';
|
||||||
|
import RouteAutonomousFilter from 'pages/Route/Card/RouteAutonomousFilter.vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
|
||||||
|
import { useSummaryDialog } from 'composables/useSummaryDialog';
|
||||||
|
import VnDms from 'components/common/VnDms.vue';
|
||||||
|
import { useState } from 'composables/useState';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const router = useRouter();
|
||||||
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
const state = useState();
|
||||||
|
const user = state.getUser();
|
||||||
|
const dmsDialog = ref({
|
||||||
|
show: false,
|
||||||
|
initialForm: {},
|
||||||
|
rowsToCreateInvoiceIn: [],
|
||||||
|
});
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'ID',
|
||||||
|
label: t('ID'),
|
||||||
|
field: (row) => row.routeFk,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'date',
|
||||||
|
label: t('Date'),
|
||||||
|
field: (row) => toDate(row.created),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'agency-route',
|
||||||
|
label: t('Agency route'),
|
||||||
|
field: (row) => row?.agencyModeName,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'agency-agreement',
|
||||||
|
label: t('Agency agreement'),
|
||||||
|
field: (row) => row?.agencyAgreement,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packages',
|
||||||
|
label: t('Packages'),
|
||||||
|
field: (row) => dashIfEmpty(row.packages),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'volume',
|
||||||
|
label: 'm³',
|
||||||
|
field: (row) => dashIfEmpty(row.m3),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'km',
|
||||||
|
label: t('Km'),
|
||||||
|
field: (row) => dashIfEmpty(row?.kmTotal),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
label: t('Price'),
|
||||||
|
field: (row) => (row?.price ? toCurrency(row.price) : '-'),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'received',
|
||||||
|
label: t('Received'),
|
||||||
|
field: (row) => row?.invoiceInFk,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'autonomous',
|
||||||
|
label: t('Autonomous'),
|
||||||
|
field: (row) => row?.supplierName,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
|
||||||
|
const total = computed(() => selectedRows.value.reduce((item) => item?.price || 0, 0));
|
||||||
|
|
||||||
|
const openDmsUploadDialog = async () => {
|
||||||
|
dmsDialog.value.rowsToCreateInvoiceIn = selectedRows.value
|
||||||
|
.filter(
|
||||||
|
(agencyTerm) => agencyTerm.supplierFk === selectedRows.value?.[0].supplierFk
|
||||||
|
)
|
||||||
|
.map((agencyTerm) => ({
|
||||||
|
routeFk: agencyTerm.routeFk,
|
||||||
|
supplierFk: agencyTerm.supplierFk,
|
||||||
|
created: agencyTerm.created,
|
||||||
|
totalPrice: total.value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (dmsDialog.value.rowsToCreateInvoiceIn.length !== selectedRows.value.length) {
|
||||||
|
dmsDialog.value.rowsToCreateInvoiceIn = [];
|
||||||
|
dmsDialog.value.initialForm = null;
|
||||||
|
return notify(t('Two autonomous cannot be counted at the same time'), 'negative');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dmsType = await axios
|
||||||
|
.get('DmsTypes/findOne', {
|
||||||
|
filter: {
|
||||||
|
where: { code: 'invoiceIn' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res.data);
|
||||||
|
|
||||||
|
dmsDialog.value.initialForm = {
|
||||||
|
description: selectedRows.value?.[0].supplierName,
|
||||||
|
companyFk: user.value.companyFk,
|
||||||
|
warehouseFk: user.value.warehouseFk,
|
||||||
|
dmsTypeFk: dmsType?.id,
|
||||||
|
};
|
||||||
|
dmsDialog.value.show = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDmsSaved = async (dms, response) => {
|
||||||
|
if (response) {
|
||||||
|
await axios.post('AgencyTerms/createInvoiceIn', {
|
||||||
|
rows: dmsDialog.value.rowsToCreateInvoiceIn,
|
||||||
|
dms: response.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dmsDialog.value.show = false;
|
||||||
|
dmsDialog.value.initialForm = null;
|
||||||
|
dmsDialog.value.rowsToCreateInvoiceIn = [];
|
||||||
|
refreshKey.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
function navigateToRouteSummary(event, row) {
|
||||||
|
router.push({ name: 'RouteSummary', params: { id: row.routeFk } });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="RouteAutonomousList"
|
||||||
|
:label="t('Search autonomous')"
|
||||||
|
:info="t('You can search by autonomous reference')"
|
||||||
|
/>
|
||||||
|
</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">
|
||||||
|
<RouteAutonomousFilter data-key="RouteAutonomousList" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPage class="column items-center">
|
||||||
|
<div class="route-list">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<QCard class="vn-one q-py-sm q-px-lg">
|
||||||
|
<VnLv class="flex">
|
||||||
|
<template #label>
|
||||||
|
<span class="text-h6">{{ t('Total') }}</span>
|
||||||
|
</template>
|
||||||
|
<template #value>
|
||||||
|
<span class="text-h6 q-ml-md">{{ toCurrency(total) }}</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
<VnPaginate
|
||||||
|
:key="refreshKey"
|
||||||
|
data-key="RouteAutonomousList"
|
||||||
|
url="AgencyTerms/filter"
|
||||||
|
:limit="20"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<QTable
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
flat
|
||||||
|
row-key="routeFk"
|
||||||
|
selection="multiple"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-pagination
|
||||||
|
@row-click="navigateToRouteSummary"
|
||||||
|
>
|
||||||
|
<template #body-cell-ID="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<span class="link" @click.stop>
|
||||||
|
{{ props.value }}
|
||||||
|
<RouteDescriptorProxy :id="props.value" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-received="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<span :class="props.value && 'link'" @click.stop>
|
||||||
|
{{ dashIfEmpty(props.value) }}
|
||||||
|
<InvoiceInDescriptorProxy
|
||||||
|
v-if="props.value"
|
||||||
|
:id="props.value"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-autonomous="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<span class="link" @click.stop>
|
||||||
|
{{ props.value }}
|
||||||
|
<SupplierDescriptorProxy
|
||||||
|
v-if="props.row?.supplierFk"
|
||||||
|
:id="props.row?.supplierFk"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-actions="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<div class="table-actions">
|
||||||
|
<QIcon
|
||||||
|
name="preview"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
@click.stop="
|
||||||
|
viewSummary(
|
||||||
|
props?.row?.routeFk,
|
||||||
|
RouteSummary
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Preview') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QPageSticky :offset="[20, 20]" v-if="selectedRows?.length">
|
||||||
|
<QBtn
|
||||||
|
fab
|
||||||
|
icon="vn:invoice-in-create"
|
||||||
|
color="primary"
|
||||||
|
@click="openDmsUploadDialog"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Create invoiceIn') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
<QDialog v-model="dmsDialog.show">
|
||||||
|
<VnDms
|
||||||
|
url="dms/uploadFile"
|
||||||
|
model="AgencyTerms"
|
||||||
|
:form-initial-data="dmsDialog.initialForm"
|
||||||
|
@on-data-saved="onDmsSaved"
|
||||||
|
/>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.route-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search autonomous: Buscar autónomos
|
||||||
|
You can search by autonomous reference: Puedes buscar por referencia del autónomo
|
||||||
|
Create invoiceIn: Crear factura recibida
|
||||||
|
Two autonomous cannot be counted at the same time: Dos autonónomos no pueden ser contabilizados al mismo tiempo
|
||||||
|
Date: Fecha
|
||||||
|
Agency route: Agencia Ruta
|
||||||
|
Agency agreement: Agencia Acuerdo
|
||||||
|
Packages: Bultos
|
||||||
|
Price: Precio
|
||||||
|
Received: Recibida
|
||||||
|
Autonomous: Autónomos
|
||||||
|
Preview: Vista previa
|
||||||
|
</i18n>
|
|
@ -2,9 +2,8 @@
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
import { dashIfEmpty, toHour } from 'src/filters';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import { useValidator } from 'composables/useValidator';
|
import { useValidator } from 'composables/useValidator';
|
||||||
|
@ -16,12 +15,15 @@ import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
||||||
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
||||||
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
|
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
import {useSession} from "composables/useSession";
|
import { useSession } from 'composables/useSession';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { validate } = useValidator();
|
const { validate } = useValidator();
|
||||||
|
const quasar = useQuasar();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ const columns = computed(() => [
|
||||||
label: t('ID'),
|
label: t('ID'),
|
||||||
field: (row) => row.id,
|
field: (row) => row.id,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'worker',
|
name: 'worker',
|
||||||
|
@ -70,7 +72,7 @@ const columns = computed(() => [
|
||||||
label: 'm³',
|
label: 'm³',
|
||||||
field: (row) => dashIfEmpty(row.m3),
|
field: (row) => dashIfEmpty(row.m3),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
@ -113,24 +115,6 @@ const updateRoute = async (route) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateVehicle = (row, vehicle) => {
|
|
||||||
row.vehicleFk = vehicle.id;
|
|
||||||
row.vehiclePlateNumber = vehicle.numberPlate;
|
|
||||||
updateRoute(row);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateAgency = (row, agency) => {
|
|
||||||
row.agencyModeFk = agency.id;
|
|
||||||
row.agencyName = agency.name;
|
|
||||||
updateRoute(row);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateWorker = (row, worker) => {
|
|
||||||
row.workerFk = worker.id;
|
|
||||||
row.workerUserName = worker.name;
|
|
||||||
updateRoute(row);
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmationDialog = ref(false);
|
const confirmationDialog = ref(false);
|
||||||
const startingDate = ref(null);
|
const startingDate = ref(null);
|
||||||
|
|
||||||
|
@ -144,21 +128,21 @@ const cloneRoutes = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const showRouteReport = () => {
|
const showRouteReport = () => {
|
||||||
const ids = selectedRows.value.map(row => row?.id)
|
const ids = selectedRows.value.map((row) => row?.id);
|
||||||
const idString = ids.join(',')
|
const idString = ids.join(',');
|
||||||
let url;
|
let url;
|
||||||
|
|
||||||
if (selectedRows.value.length <= 1) {
|
if (selectedRows.value.length <= 1) {
|
||||||
url = `api/Routes/${idString}/driver-route-pdf?access_token=${session.getToken()}`;
|
url = `api/Routes/${idString}/driver-route-pdf?access_token=${session.getTokenMultimedia()}`;
|
||||||
} else {
|
} else {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
access_token: session.getToken(),
|
access_token: session.getTokenMultimedia(),
|
||||||
id: idString
|
id: idString,
|
||||||
})
|
});
|
||||||
url = `api/Routes/downloadZip?${params.toString()}`;
|
url = `api/Routes/downloadZip?${params.toString()}`;
|
||||||
}
|
}
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
}
|
};
|
||||||
|
|
||||||
const markAsServed = () => {
|
const markAsServed = () => {
|
||||||
selectedRows.value.forEach((row) => {
|
selectedRows.value.forEach((row) => {
|
||||||
|
@ -169,6 +153,20 @@ const markAsServed = () => {
|
||||||
refreshKey.value++;
|
refreshKey.value++;
|
||||||
startingDate.value = null;
|
startingDate.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openTicketsDialog = (id) => {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: RouteListTicketsDialog,
|
||||||
|
componentProps: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => refreshKey.value++);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -287,31 +285,22 @@ const markAsServed = () => {
|
||||||
:rows-per-page-options="[0]"
|
:rows-per-page-options="[0]"
|
||||||
hide-pagination
|
hide-pagination
|
||||||
:pagination="{ sortBy: 'ID', descending: true }"
|
:pagination="{ sortBy: 'ID', descending: true }"
|
||||||
|
:no-data-label="t('globals.noResults')"
|
||||||
>
|
>
|
||||||
<template #body-cell-worker="props">
|
<template #body-cell-worker="{ row }">
|
||||||
<QTd :props="props">
|
<QTd>
|
||||||
{{ props.row?.workerUserName }}
|
|
||||||
<QPopupEdit
|
|
||||||
:model-value="props.row.workerFk"
|
|
||||||
v-slot="scope"
|
|
||||||
buttons
|
|
||||||
@update:model-value="
|
|
||||||
(worker) => updateWorker(props.row, worker)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('Worker')"
|
:label="t('Worker')"
|
||||||
v-model="scope.value"
|
v-model="row.workerFk"
|
||||||
:options="workers"
|
:options="workers"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="nickname"
|
||||||
hide-selected
|
hide-selected
|
||||||
autofocus
|
dense
|
||||||
:emit-value="false"
|
:emit-value="false"
|
||||||
:rules="validate('Route.workerFk')"
|
:rules="validate('Route.workerFk')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
@update:model-value="updateRoute(row)"
|
||||||
@focus="($event) => $event.target.select()"
|
|
||||||
>
|
>
|
||||||
<template #option="{ opt, itemProps }">
|
<template #option="{ opt, itemProps }">
|
||||||
<QItem
|
<QItem
|
||||||
|
@ -329,157 +318,103 @@ const markAsServed = () => {
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelectFilter>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-agency="props">
|
<template #body-cell-agency="{ row }">
|
||||||
<QTd :props="props">
|
<QTd>
|
||||||
{{ props.row?.agencyName }}
|
|
||||||
<QPopupEdit
|
|
||||||
:model-value="props.row.agencyModeFk"
|
|
||||||
v-slot="scope"
|
|
||||||
buttons
|
|
||||||
@update:model-value="
|
|
||||||
(agency) => updateAgency(props.row, agency)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('Agency')"
|
:label="t('Agency')"
|
||||||
v-model="scope.value"
|
v-model="row.agencyModeFk"
|
||||||
:options="agencyList"
|
:options="agencyList"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
hide-selected
|
hide-selected
|
||||||
autofocus
|
dense
|
||||||
:emit-value="false"
|
:emit-value="false"
|
||||||
:rules="validate('route.agencyFk')"
|
:rules="validate('route.agencyFk')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
@update:model-value="updateRoute(row)"
|
||||||
@focus="($event) => $event.target.select()"
|
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-vehicle="props">
|
<template #body-cell-vehicle="{ row }">
|
||||||
<QTd :props="props">
|
<QTd>
|
||||||
{{ props.row?.vehiclePlateNumber }}
|
|
||||||
<QPopupEdit
|
|
||||||
:model-value="props.row.vehicleFk"
|
|
||||||
v-slot="scope"
|
|
||||||
buttons
|
|
||||||
@update:model-value="
|
|
||||||
(vehicle) => updateVehicle(props.row, vehicle)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('Vehicle')"
|
:label="t('Vehicle')"
|
||||||
v-model="scope.value"
|
v-model="row.vehicleFk"
|
||||||
:options="vehicleList"
|
:options="vehicleList"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="numberPlate"
|
option-label="numberPlate"
|
||||||
hide-selected
|
hide-selected
|
||||||
autofocus
|
dense
|
||||||
:emit-value="false"
|
:emit-value="false"
|
||||||
:rules="validate('route.vehicleFk')"
|
:rules="validate('route.vehicleFk')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
@update:model-value="updateRoute(row)"
|
||||||
@focus="($event) => $event.target.select()"
|
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-date="props">
|
<template #body-cell-date="{ row }">
|
||||||
<QTd :props="props">
|
<QTd class="table-input-cell">
|
||||||
{{ toDate(props.row?.created) }}
|
|
||||||
<QPopupEdit
|
|
||||||
v-model="props.row.created"
|
|
||||||
v-slot="scope"
|
|
||||||
@update:model-value="updateRoute(props.row)"
|
|
||||||
buttons
|
|
||||||
>
|
|
||||||
<VnInputDate
|
<VnInputDate
|
||||||
v-model="scope.value"
|
v-model="row.created"
|
||||||
autofocus
|
hide-bottom-space
|
||||||
|
dense
|
||||||
:label="t('Date')"
|
:label="t('Date')"
|
||||||
:rules="validate('route.created')"
|
:rules="validate('route.created')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
@update:model-value="updateRoute(row)"
|
||||||
@focus="($event) => $event.target.select()"
|
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-description="props">
|
<template #body-cell-description="{ row }">
|
||||||
<QTd :props="props">
|
<QTd class="table-input-cell">
|
||||||
{{ props.row?.description }}
|
|
||||||
<QPopupEdit
|
|
||||||
v-model="props.row.description"
|
|
||||||
v-slot="scope"
|
|
||||||
@update:model-value="updateRoute(props.row)"
|
|
||||||
buttons
|
|
||||||
>
|
|
||||||
<VnInput
|
<VnInput
|
||||||
v-model="scope.value"
|
v-model="row.description"
|
||||||
autofocus
|
|
||||||
:label="t('Description')"
|
:label="t('Description')"
|
||||||
:rules="validate('route.description')"
|
:rules="validate('route.description')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
dense
|
||||||
@focus="($event) => $event.target.select()"
|
@update:model-value="updateRoute(row)"
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-started="props">
|
<template #body-cell-started="{ row }">
|
||||||
<QTd :props="props">
|
<QTd class="table-input-cell">
|
||||||
{{ toHour(props.row.started) }}
|
|
||||||
<QPopupEdit
|
|
||||||
v-model="props.row.started"
|
|
||||||
v-slot="scope"
|
|
||||||
buttons
|
|
||||||
@update:model-value="updateRoute(props.row)"
|
|
||||||
>
|
|
||||||
<VnInputTime
|
<VnInputTime
|
||||||
v-model="scope.value"
|
v-model="row.started"
|
||||||
autofocus
|
|
||||||
:label="t('Hour started')"
|
:label="t('Hour started')"
|
||||||
:rules="validate('route.started')"
|
:rules="validate('route.started')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
hide-bottom-space
|
||||||
@focus="($event) => $event.target.select()"
|
dense
|
||||||
|
@update:model-value="updateRoute(row)"
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-finished="props">
|
<template #body-cell-finished="{ row }">
|
||||||
<QTd :props="props">
|
<QTd class="table-input-cell">
|
||||||
{{ toHour(props.row.finished) }}
|
|
||||||
<QPopupEdit
|
|
||||||
v-model="props.row.finished"
|
|
||||||
v-slot="scope"
|
|
||||||
buttons
|
|
||||||
@update:model-value="updateRoute(props.row)"
|
|
||||||
>
|
|
||||||
<VnInputTime
|
<VnInputTime
|
||||||
v-model="scope.value"
|
v-model="row.finished"
|
||||||
autofocus
|
autofocus
|
||||||
:label="t('Hour finished')"
|
:label="t('Hour finished')"
|
||||||
:rules="validate('route.finished')"
|
:rules="validate('route.finished')"
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
@keyup.enter="scope.set"
|
hide-bottom-space
|
||||||
@focus="($event) => $event.target.select()"
|
dense
|
||||||
|
@update:model-value="updateRoute(row)"
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-actions="props">
|
<template #body-cell-actions="props">
|
||||||
<QTd :props="props">
|
<QTd :props="props">
|
||||||
<div class="flex items-center table-actions">
|
<div class="flex items-center no-wrap table-actions">
|
||||||
<QIcon
|
<QIcon
|
||||||
name="vn:ticketAdd"
|
name="vn:ticketAdd"
|
||||||
size="xs"
|
size="xs"
|
||||||
color="primary"
|
color="primary"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
|
@click="openTicketsDialog(props?.row?.id)"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Add ticket') }}</QTooltip>
|
<QTooltip>{{ t('Add ticket') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
|
@ -494,6 +429,20 @@ const markAsServed = () => {
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Preview') }}</QTooltip>
|
<QTooltip>{{ t('Preview') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
|
<RouterLink
|
||||||
|
:to="{
|
||||||
|
name: 'RouteSummary',
|
||||||
|
params: { id: props?.row?.id },
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<QIcon
|
||||||
|
name="vn:eye"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Summary') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
@ -508,8 +457,13 @@ const markAsServed = () => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.table-input-cell {
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
.route-list {
|
.route-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-actions {
|
.table-actions {
|
||||||
|
@ -538,4 +492,5 @@ es:
|
||||||
Download selected routes as PDF: Descargar rutas seleccionadas como PDF
|
Download selected routes as PDF: Descargar rutas seleccionadas como PDF
|
||||||
Add ticket: Añadir tickets
|
Add ticket: Añadir tickets
|
||||||
Preview: Vista previa
|
Preview: Vista previa
|
||||||
|
Summary: Resumen
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<script setup>
|
||||||
|
import VnLog from 'src/components/common/VnLog.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnLog model="Route" url="/RouteLogs"></VnLog>
|
||||||
|
</template>
|
|
@ -0,0 +1,296 @@
|
||||||
|
<script setup>
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { dashIfEmpty, toDateHour } from 'src/filters';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import toCurrency from 'filters/toCurrency';
|
||||||
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
import RoadmapFilter from 'pages/Route/Roadmap/RoadmapFilter.vue';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import {useSummaryDialog} from "composables/useSummaryDialog";
|
||||||
|
import RoadmapSummary from "pages/Route/Roadmap/RoadmapSummary.vue";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const router = useRouter();
|
||||||
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
|
||||||
|
const to = Date.vnNew();
|
||||||
|
to.setDate(to.getDate() + 1);
|
||||||
|
to.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const from = Date.vnNew();
|
||||||
|
from.setDate(from.getDate());
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'roadmap',
|
||||||
|
label: t('Roadmap'),
|
||||||
|
field: (row) => row.name,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ETD',
|
||||||
|
label: t('ETD'),
|
||||||
|
field: (row) => toDateHour(row.etd),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'carrier',
|
||||||
|
label: t('Carrier'),
|
||||||
|
field: (row) => row.supplier?.nickname,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plate',
|
||||||
|
label: t('Plate'),
|
||||||
|
field: (row) => row.tractorPlate,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
label: t('Price'),
|
||||||
|
field: (row) => toCurrency(row.price),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'observations',
|
||||||
|
label: t('Observations'),
|
||||||
|
field: (row) => dashIfEmpty(row.observations),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
const isCloneDialogOpen = ref(false);
|
||||||
|
const etdDate = ref(null);
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
include: { relation: 'supplier', scope: { fields: ['nickname'] } },
|
||||||
|
where: {
|
||||||
|
and: [{ etd: { gte: from } }, { etd: { lte: to } }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const cloneSelection = async () => {
|
||||||
|
await axios.post('Roadmaps/clone', {
|
||||||
|
etd: etdDate.value,
|
||||||
|
ids: selectedRows.value.map((row) => row?.id),
|
||||||
|
});
|
||||||
|
refreshKey.value++;
|
||||||
|
etdDate.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeSelection = async () => {
|
||||||
|
await Promise.all(
|
||||||
|
selectedRows.value.map((roadmap) => {
|
||||||
|
axios.delete(`Roadmaps/${roadmap.id}`);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function confirmRemove() {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Selected roadmaps will be removed'),
|
||||||
|
message: t('Are you sure you want to continue?'),
|
||||||
|
promise: removeSelection,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => refreshKey.value++);
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateToRoadmapSummary(event, row) {
|
||||||
|
router.push({ name: 'RoadmapSummary', params: { id: row.id } })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="RoadmapList"
|
||||||
|
:label="t('Search roadmaps')"
|
||||||
|
:info="t('You can search by roadmap reference')"
|
||||||
|
/>
|
||||||
|
</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">
|
||||||
|
<RoadmapFilter data-key="RoadmapList" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QDialog v-model="isCloneDialogOpen">
|
||||||
|
<QCard style="min-width: 350px">
|
||||||
|
<QCardSection>
|
||||||
|
<p class="text-h6 q-ma-none">
|
||||||
|
{{ t('Select the estimated date of departure (ETD)') }}
|
||||||
|
</p>
|
||||||
|
</QCardSection>
|
||||||
|
|
||||||
|
<QCardSection class="q-pt-none">
|
||||||
|
<VnInputDate :label="t('ETD')" v-model="etdDate" autofocus />
|
||||||
|
</QCardSection>
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
||||||
|
<QBtn color="primary" v-close-popup @click="cloneSelection">
|
||||||
|
{{ t('Clone') }}
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
<QPage class="column items-center">
|
||||||
|
<VnSubToolbar class="bg-vn-dark justify-end">
|
||||||
|
<template #st-actions>
|
||||||
|
<QBtn
|
||||||
|
icon="vn:clone"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="isCloneDialogOpen = true"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
icon="delete"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="confirmRemove"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Delete roadmap(s)') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</template>
|
||||||
|
</VnSubToolbar>
|
||||||
|
<div class="route-list">
|
||||||
|
<VnPaginate
|
||||||
|
:key="refreshKey"
|
||||||
|
data-key="RoadmapList"
|
||||||
|
url="Roadmaps"
|
||||||
|
:limit="20"
|
||||||
|
:filter="filter"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<QTable
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
flat
|
||||||
|
row-key="id"
|
||||||
|
selection="multiple"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-pagination
|
||||||
|
:pagination="{ sortBy: 'ID', descending: true }"
|
||||||
|
@row-click="navigateToRoadmapSummary"
|
||||||
|
>
|
||||||
|
<template #body-cell-carrier="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<span v-if="props.value" class="link" @click.stop>
|
||||||
|
{{ props.value }}
|
||||||
|
<SupplierDescriptorProxy
|
||||||
|
:id="props.row?.supplier?.id"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-actions="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<div class="flex items-center table-actions">
|
||||||
|
<QIcon
|
||||||
|
name="preview"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
@click.stop="viewSummary(props?.row?.id, RoadmapSummary)"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Preview') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<RouterLink :to="{ name: 'RouteRoadmapCreate' }">
|
||||||
|
<QBtn fab icon="add" color="primary" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Create roadmap') }}
|
||||||
|
</QTooltip>
|
||||||
|
</RouterLink>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.route-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search roadmaps: Buscar troncales
|
||||||
|
You can search by roadmap reference: Puedes buscar por referencia de la troncal
|
||||||
|
Delete roadmap(s): Eliminar troncal(es)
|
||||||
|
Selected roadmaps will be removed: Las troncales seleccionadas serán eliminadas
|
||||||
|
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||||
|
The date can't be empty: La fecha no puede estar vacía
|
||||||
|
Clone Selected Routes: Clonar rutas seleccionadas
|
||||||
|
Create roadmap: Crear troncal
|
||||||
|
Roadmap: Troncal
|
||||||
|
Carrier: Transportista
|
||||||
|
Plate: Matrícula
|
||||||
|
Price: Precio
|
||||||
|
Observations: Observaciones
|
||||||
|
Preview: Vista previa
|
||||||
|
</i18n>
|
|
@ -0,0 +1,453 @@
|
||||||
|
<script setup>
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { dashIfEmpty } from 'src/filters';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { QIcon, useQuasar } from 'quasar';
|
||||||
|
import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.vue';
|
||||||
|
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import { openBuscaman } from 'src/utils/buscaman';
|
||||||
|
import SendSmsDialog from 'components/common/SendSmsDialog.vue';
|
||||||
|
import RouteSearchbar from "pages/Route/Card/RouteSearchbar.vue";
|
||||||
|
import {useStateStore} from "stores/useStateStore";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
label: t('Order'),
|
||||||
|
field: (row) => dashIfEmpty(row?.priority),
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'street',
|
||||||
|
label: t('Street'),
|
||||||
|
field: (row) => row?.street,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'city',
|
||||||
|
label: t('City'),
|
||||||
|
field: (row) => row?.city,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pc',
|
||||||
|
label: t('PC'),
|
||||||
|
field: (row) => row?.postalCode,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'client',
|
||||||
|
label: t('Client'),
|
||||||
|
field: (row) => row?.nickname,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warehouse',
|
||||||
|
label: t('Warehouse'),
|
||||||
|
field: (row) => row?.warehouseName,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packages',
|
||||||
|
label: t('Packages'),
|
||||||
|
field: (row) => row?.packages,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'volume',
|
||||||
|
label: 'm³',
|
||||||
|
field: (row) => row?.volume,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packaging',
|
||||||
|
label: t('Packaging'),
|
||||||
|
field: (row) => row?.ipt,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ticket',
|
||||||
|
label: t('Ticket'),
|
||||||
|
field: (row) => row?.id,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'observations',
|
||||||
|
label: '',
|
||||||
|
field: (row) => row?.ticketObservation,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
const confirmationDialog = ref(false);
|
||||||
|
const startingDate = ref(null);
|
||||||
|
const routeEntity = ref(null);
|
||||||
|
const ticketList = ref([]);
|
||||||
|
|
||||||
|
const cloneRoutes = () => {
|
||||||
|
axios.post('Routes/clone', {
|
||||||
|
created: startingDate.value,
|
||||||
|
ids: selectedRows.value.map((row) => row?.id),
|
||||||
|
});
|
||||||
|
refreshKey.value++;
|
||||||
|
startingDate.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deletePriorities = async () => {
|
||||||
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
selectedRows.value.map((ticket) =>
|
||||||
|
axios.patch(`Tickets/${ticket?.id}/`, { priority: null })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
refreshKey.value++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOrderedPriority = async () => {
|
||||||
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
ticketList.value.map((ticket, index) =>
|
||||||
|
axios.patch(`Tickets/${ticket?.id}/`, { priority: index + 1 })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
refreshKey.value++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortRoutes = async () => {
|
||||||
|
await axios.get(`Routes/${route.params?.id}/guessPriority/`);
|
||||||
|
refreshKey.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatePriority = async (ticket, priority = null) => {
|
||||||
|
return axios.patch(`Tickets/${ticket?.id}/`, {
|
||||||
|
priority: priority || ticket?.priority,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setHighestPriority = async (ticket, ticketList) => {
|
||||||
|
const highestPriority = Math.max(...ticketList.map((item) => item.priority)) + 1;
|
||||||
|
if (highestPriority - 1 !== ticket.priority) {
|
||||||
|
const response = await updatePriority(ticket, highestPriority);
|
||||||
|
ticket.priority = response.data.priority;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToBuscaman = async (ticket = null) => {
|
||||||
|
await openBuscaman(
|
||||||
|
routeEntity.value?.vehicleFk,
|
||||||
|
ticket ? [ticket] : selectedRows.value
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openTicketsDialog = () => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: RouteListTicketsDialog,
|
||||||
|
componentProps: {
|
||||||
|
id: route.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => refreshKey.value++);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTicket = async (ticket) => {
|
||||||
|
await axios.patch(`Tickets/${ticket?.id}/`, { routeFk: null });
|
||||||
|
await axios.post(`Routes/${route?.params?.id}/updateVolume`);
|
||||||
|
refreshKey.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmRemove = (ticket) => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Confirm removal from route'),
|
||||||
|
message: t('Are you sure you want to remove this ticket from the route?'),
|
||||||
|
promise: () => removeTicket(ticket),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => refreshKey.value++);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSmsDialog = async () => {
|
||||||
|
const clientsId = [];
|
||||||
|
const clientsPhone = [];
|
||||||
|
|
||||||
|
for (let ticket of selectedRows.value) {
|
||||||
|
clientsId.push(ticket?.clientFk);
|
||||||
|
const { data: client } = await axios.get(`Clients/${ticket?.clientFk}`);
|
||||||
|
clientsPhone.push(client.phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
quasar.dialog({
|
||||||
|
component: SendSmsDialog,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Send SMS to the selected tickets'),
|
||||||
|
url: 'Routes/sendSms',
|
||||||
|
destinationFk: clientsId.toString(),
|
||||||
|
destination: clientsPhone.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<RouteSearchbar />
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (routeEntity = data)"
|
||||||
|
auto-load
|
||||||
|
:url="`Routes/${route.params?.id}`"
|
||||||
|
:filter="{
|
||||||
|
fields: ['vehicleFk'],
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<QDialog v-model="confirmationDialog">
|
||||||
|
<QCard style="min-width: 350px">
|
||||||
|
<QCardSection>
|
||||||
|
<p class="text-h6 q-ma-none">{{ t('Select the starting date') }}</p>
|
||||||
|
</QCardSection>
|
||||||
|
|
||||||
|
<QCardSection class="q-pt-none">
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('Stating date')"
|
||||||
|
v-model="startingDate"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
||||||
|
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
||||||
|
{{ t('Clone') }}
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
<QPage class="column items-center">
|
||||||
|
<QToolbar class="bg-vn-dark justify-end">
|
||||||
|
<div id="st-actions" class="q-pa-sm">
|
||||||
|
<QBtn icon="vn:wand" color="primary" class="q-mr-sm" @click="sortRoutes">
|
||||||
|
<QTooltip>{{ t('Sort routes') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
icon="vn:buscaman"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="goToBuscaman()"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Open buscaman') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
icon="filter_alt"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm filled-icon"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="deletePriorities"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Delete priority') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
icon="format_list_numbered"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
@click="setOrderedPriority"
|
||||||
|
>
|
||||||
|
<QTooltip
|
||||||
|
>{{
|
||||||
|
t('Renumber all tickets in the order you see on the screen')
|
||||||
|
}}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
icon="sms"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm filled-icon"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="openSmsDialog"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Send SMS to all clients') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</QToolbar>
|
||||||
|
<div class="route-list">
|
||||||
|
<VnPaginate
|
||||||
|
:key="refreshKey"
|
||||||
|
data-key="RouteTicketList"
|
||||||
|
url="Routes/getTickets"
|
||||||
|
:filter="{ id: route.params.id }"
|
||||||
|
:order="['priority ASC']"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (ticketList = data)"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<QTable
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
row-key="id"
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
selection="multiple"
|
||||||
|
>
|
||||||
|
<template #body-cell-order="{ row }">
|
||||||
|
<QTd class="order-field">
|
||||||
|
<div class="flex no-wrap items-center">
|
||||||
|
<QIcon
|
||||||
|
name="low_priority"
|
||||||
|
size="xs"
|
||||||
|
class="q-mr-md cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="setHighestPriority(row, rows)"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Assign highest priority') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<VnInput
|
||||||
|
v-model="row.priority"
|
||||||
|
dense
|
||||||
|
@blur="updatePriority(row)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-city="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span
|
||||||
|
class="text-primary cursor-pointer"
|
||||||
|
@click="goToBuscaman(row)"
|
||||||
|
>
|
||||||
|
{{ value }}
|
||||||
|
<QTooltip>{{ t('Open buscaman') }}</QTooltip>
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-client="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span class="text-primary cursor-pointer">
|
||||||
|
{{ value }}
|
||||||
|
<CustomerDescriptorProxy :id="row?.clientFk" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-ticket="{ value, row }">
|
||||||
|
<QTd auto-width class="text-center">
|
||||||
|
<span class="text-primary cursor-pointer">
|
||||||
|
{{ value }}
|
||||||
|
<TicketDescriptorProxy :id="row?.id" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-observations="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<div class="flex items-center no-wrap table-actions">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
color="primary"
|
||||||
|
class="cursor-pointer"
|
||||||
|
size="xs"
|
||||||
|
@click="confirmRemove(row)"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('globals.remove') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon
|
||||||
|
v-if="value"
|
||||||
|
name="vn:notes"
|
||||||
|
color="primary"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ value }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<QBtn fab icon="add" color="primary" @click="openTicketsDialog">
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Add ticket') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.route-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-field {
|
||||||
|
max-width: 140px;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filled-icon {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Order: Orden
|
||||||
|
Street: Dirección fiscal
|
||||||
|
City: Población
|
||||||
|
PC: CP
|
||||||
|
Client: Cliente
|
||||||
|
Warehouse: Almacén
|
||||||
|
Packages: Bultos
|
||||||
|
Packaging: Encajado
|
||||||
|
Confirm removal from route: Quitar de la ruta
|
||||||
|
Are you sure you want to remove this ticket from the route?: ¿Seguro que quieres quitar este ticket de la ruta?
|
||||||
|
Sort routes: Ordenar rutas
|
||||||
|
Open buscaman: Abrir buscaman
|
||||||
|
Delete priority: Borrar orden
|
||||||
|
Renumber all tickets in the order you see on the screen: Renumerar todos los tickets con el orden que ves por pantalla
|
||||||
|
Assign highest priority: Asignar máxima prioridad
|
||||||
|
Send SMS to all clients: Mandar sms a todos los clientes de las rutas
|
||||||
|
Send SMS to the selected tickets: Enviar SMS a los tickets seleccionados
|
||||||
|
Add ticket: Añadir ticket
|
||||||
|
</i18n>
|
|
@ -18,13 +18,16 @@ const quasar = useQuasar();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const bankEntitiesRef = ref(null);
|
||||||
const supplier = ref(null);
|
const supplier = ref(null);
|
||||||
const supplierAccountRef = ref(null);
|
const supplierAccountRef = ref(null);
|
||||||
const wireTransferFk = ref(null);
|
const wireTransferFk = ref(null);
|
||||||
const bankEntitiesOptions = ref([]);
|
const bankEntitiesOptions = ref([]);
|
||||||
|
|
||||||
const onBankEntityCreated = (data) => {
|
const onBankEntityCreated = async (dataSaved, rowData) => {
|
||||||
bankEntitiesOptions.value.push(data);
|
await bankEntitiesRef.value.fetch();
|
||||||
|
rowData.bankEntityFk = dataSaved.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangesSaved = () => {
|
const onChangesSaved = () => {
|
||||||
|
@ -63,6 +66,7 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
ref="bankEntitiesRef"
|
||||||
url="BankEntities"
|
url="BankEntities"
|
||||||
@on-fetch="(data) => (bankEntitiesOptions = data)"
|
@on-fetch="(data) => (bankEntitiesOptions = data)"
|
||||||
auto-load
|
auto-load
|
||||||
|
@ -114,13 +118,16 @@ onMounted(() => {
|
||||||
:label="t('worker.create.bankEntity')"
|
:label="t('worker.create.bankEntity')"
|
||||||
v-model="row.bankEntityFk"
|
v-model="row.bankEntityFk"
|
||||||
:options="bankEntitiesOptions"
|
:options="bankEntitiesOptions"
|
||||||
option-label="name"
|
option-label="bic"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
hide-selected
|
hide-selected
|
||||||
>
|
>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateBankEntityForm
|
<CreateBankEntityForm
|
||||||
@on-data-saved="onBankEntityCreated($event)"
|
@on-data-saved="
|
||||||
|
(_, requestResponse) =>
|
||||||
|
onBankEntityCreated(requestResponse, row)
|
||||||
|
"
|
||||||
:show-entity-field="false"
|
:show-entity-field="false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -83,8 +83,13 @@ const redirectToUpdateView = (addressData) => {
|
||||||
<QPageSticky :offset="[20, 20]">
|
<QPageSticky :offset="[20, 20]">
|
||||||
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('supplier.list.newSupplier') }}
|
{{ t('New address') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
New address: Nueva dirección
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -26,6 +26,7 @@ const workersOptions = ref([]);
|
||||||
:url-update="`Suppliers/${route.params.id}`"
|
:url-update="`Suppliers/${route.params.id}`"
|
||||||
model="supplier"
|
model="supplier"
|
||||||
auto-load
|
auto-load
|
||||||
|
:clear-store-on-unmount="false"
|
||||||
>
|
>
|
||||||
<template #form="{ data, validate }">
|
<template #form="{ data, validate }">
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
|
|
@ -33,6 +33,7 @@ const formatPayDems = (data) => {
|
||||||
:url-update="`Suppliers/${route.params.id}`"
|
:url-update="`Suppliers/${route.params.id}`"
|
||||||
model="supplier"
|
model="supplier"
|
||||||
auto-load
|
auto-load
|
||||||
|
:clear-store-on-unmount="false"
|
||||||
>
|
>
|
||||||
<template #form="{ data, validate }">
|
<template #form="{ data, validate }">
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, nextTick } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
@ -11,6 +11,15 @@ const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const supplierContactRef = ref(null);
|
const supplierContactRef = ref(null);
|
||||||
|
|
||||||
|
const insertRow = () => {
|
||||||
|
supplierContactRef.value.insert();
|
||||||
|
nextTick(() => {
|
||||||
|
const inputs = document.querySelectorAll('[input-name-focusable]');
|
||||||
|
const lastInput = inputs[inputs.length - 1];
|
||||||
|
if (lastInput) lastInput.focus();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (supplierContactRef.value) supplierContactRef.value.reload();
|
if (supplierContactRef.value) supplierContactRef.value.reload();
|
||||||
});
|
});
|
||||||
|
@ -38,6 +47,7 @@ onMounted(() => {
|
||||||
<VnRow class="row q-gutter-md">
|
<VnRow class="row q-gutter-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnInput
|
<VnInput
|
||||||
|
input-name-focusable
|
||||||
:label="t('supplier.contacts.name')"
|
:label="t('supplier.contacts.name')"
|
||||||
v-model="row.name"
|
v-model="row.name"
|
||||||
/>
|
/>
|
||||||
|
@ -92,7 +102,7 @@ onMounted(() => {
|
||||||
size="sm"
|
size="sm"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
color="primary"
|
color="primary"
|
||||||
@click="supplierContactRef.insert()"
|
@click="insertRow()"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('Add contact') }}
|
{{ t('Add contact') }}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import { toDateString } from 'src/filters';
|
import { toDateString } from 'src/filters';
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
import { getUrl } from 'src/composables/getUrl';
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -21,6 +22,7 @@ const $props = defineProps({
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const url = ref();
|
const url = ref();
|
||||||
|
const state = useState();
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -71,6 +73,8 @@ const setData = (entity) => {
|
||||||
data.value = useCardDescription(entity.ref, entity.id);
|
data.value = useCardDescription(entity.ref, entity.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const supplier = computed(() => state.get('supplier'));
|
||||||
|
|
||||||
const getEntryQueryParams = (supplier) => {
|
const getEntryQueryParams = (supplier) => {
|
||||||
if (!supplier) return null;
|
if (!supplier) return null;
|
||||||
|
|
||||||
|
@ -101,7 +105,7 @@ const getEntryQueryParams = (supplier) => {
|
||||||
:subtitle="data.subtitle"
|
:subtitle="data.subtitle"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
data-key="Supplier"
|
data-key="supplier"
|
||||||
>
|
>
|
||||||
<template #header-extra-action>
|
<template #header-extra-action>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
@ -133,10 +137,10 @@ const getEntryQueryParams = (supplier) => {
|
||||||
<VnLv :label="t('supplier.summary.payDay')" :value="entity.payDay" />
|
<VnLv :label="t('supplier.summary.payDay')" :value="entity.payDay" />
|
||||||
<VnLv :label="t('supplier.summary.account')" :value="entity.account" />
|
<VnLv :label="t('supplier.summary.account')" :value="entity.account" />
|
||||||
</template>
|
</template>
|
||||||
<template #icons="{ entity }">
|
<template #icons>
|
||||||
<QCardActions class="q-gutter-x-md">
|
<QCardActions v-if="supplier" class="q-gutter-x-md">
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="!entity.isActive"
|
v-if="!supplier.isActive"
|
||||||
name="vn:disabled"
|
name="vn:disabled"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
@ -144,7 +148,7 @@ const getEntryQueryParams = (supplier) => {
|
||||||
<QTooltip>{{ t('Inactive supplier') }}</QTooltip>
|
<QTooltip>{{ t('Inactive supplier') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="!entity.isSerious"
|
v-if="!supplier.isSerious"
|
||||||
name="vn:supplierfalse"
|
name="vn:supplierfalse"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
@ -167,6 +171,7 @@ const getEntryQueryParams = (supplier) => {
|
||||||
<QTooltip>{{ t('All entries with current supplier') }}</QTooltip>
|
<QTooltip>{{ t('All entries with current supplier') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
v-if="entity.client?.fi"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'CustomerCard',
|
name: 'CustomerCard',
|
||||||
params: { id: entity.client?.id },
|
params: { id: entity.client?.id },
|
||||||
|
|
|
@ -53,6 +53,7 @@ function handleLocation(data, location) {
|
||||||
:url-update="`Suppliers/${route.params.id}/updateFiscalData`"
|
:url-update="`Suppliers/${route.params.id}/updateFiscalData`"
|
||||||
model="supplier"
|
model="supplier"
|
||||||
auto-load
|
auto-load
|
||||||
|
:clear-store-on-unmount="false"
|
||||||
>
|
>
|
||||||
<template #form="{ data, validate }">
|
<template #form="{ data, validate }">
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
|
|
@ -51,25 +51,20 @@ const isAdministrative = computed(() => {
|
||||||
:url="`Suppliers/${entityId}/getSummary`"
|
:url="`Suppliers/${entityId}/getSummary`"
|
||||||
@on-fetch="(data) => setData(data)"
|
@on-fetch="(data) => setData(data)"
|
||||||
>
|
>
|
||||||
<template #header-left>
|
|
||||||
<a v-if="isAdministrative" class="header header-link" :href="supplierUrl">
|
|
||||||
<QIcon name="open_in_new" color="white" size="sm" />
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<span>{{ supplier.name }} - {{ supplier.id }}</span>
|
<span>{{ supplier.name }} - {{ supplier.id }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a
|
<router-link
|
||||||
v-if="isAdministrative"
|
v-if="isAdministrative"
|
||||||
class="header header-link"
|
class="header link"
|
||||||
:href="`#/supplier/${entityId}/basic-data`"
|
:to="{ name: 'SupplierBasicData', params: { id: entityId } }"
|
||||||
>
|
>
|
||||||
{{ t('globals.summary.basicData') }}
|
{{ t('globals.summary.basicData') }}
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</router-link>
|
||||||
<span v-else> {{ t('globals.summary.basicData') }}</span>
|
<span v-else> {{ t('globals.summary.basicData') }}</span>
|
||||||
<VnLv label="Id" :value="supplier.id" />
|
<VnLv label="Id" :value="supplier.id" />
|
||||||
<VnLv label="Alias" :value="supplier.nickname" />
|
<VnLv label="Alias" :value="supplier.nickname" />
|
||||||
|
@ -98,14 +93,14 @@ const isAdministrative = computed(() => {
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a
|
<router-link
|
||||||
v-if="isAdministrative"
|
v-if="isAdministrative"
|
||||||
class="header header-link"
|
class="header link"
|
||||||
:href="`#/supplier/${entityId}/billing-data`"
|
:to="{ name: 'SupplierBillingData', params: { id: entityId } }"
|
||||||
>
|
>
|
||||||
{{ t('supplier.summary.billingData') }}
|
{{ t('supplier.summary.billingData') }}
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</router-link>
|
||||||
<span v-else> {{ t('supplier.summary.billingData') }}</span>
|
<span v-else> {{ t('supplier.summary.billingData') }}</span>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('supplier.summary.payMethod')"
|
:label="t('supplier.summary.payMethod')"
|
||||||
|
@ -121,14 +116,14 @@ const isAdministrative = computed(() => {
|
||||||
<VnLv :label="t('supplier.summary.account')" :value="supplier.account" />
|
<VnLv :label="t('supplier.summary.account')" :value="supplier.account" />
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a
|
<router-link
|
||||||
v-if="isAdministrative"
|
v-if="isAdministrative"
|
||||||
class="header header-link"
|
class="header link"
|
||||||
:href="`#/supplier/${entityId}/fiscal-data`"
|
:to="{ name: 'SupplierFiscalData', params: { id: entityId } }"
|
||||||
>
|
>
|
||||||
{{ t('supplier.summary.fiscalData') }}
|
{{ t('supplier.summary.fiscalData') }}
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</router-link>
|
||||||
<span v-else> {{ t('supplier.summary.fiscalData') }}</span>
|
<span v-else> {{ t('supplier.summary.fiscalData') }}</span>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('supplier.summary.sageTaxType')"
|
:label="t('supplier.summary.sageTaxType')"
|
||||||
|
@ -156,14 +151,14 @@ const isAdministrative = computed(() => {
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a
|
<router-link
|
||||||
v-if="isAdministrative"
|
v-if="isAdministrative"
|
||||||
class="header header-link"
|
class="header link"
|
||||||
:href="`#/supplier/${entityId}/fiscal-data`"
|
:to="{ name: 'SupplierFiscalData', params: { id: entityId } }"
|
||||||
>
|
>
|
||||||
{{ t('supplier.summary.fiscalAddress') }}
|
{{ t('supplier.summary.fiscalAddress') }}
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</router-link>
|
||||||
<span v-else> {{ t('supplier.summary.fiscalAddress') }}</span>
|
<span v-else> {{ t('supplier.summary.fiscalAddress') }}</span>
|
||||||
<VnLv :label="t('supplier.summary.socialName')" :value="supplier.name" />
|
<VnLv :label="t('supplier.summary.socialName')" :value="supplier.name" />
|
||||||
<VnLv :label="t('supplier.summary.taxNumber')" :value="supplier.nif" />
|
<VnLv :label="t('supplier.summary.taxNumber')" :value="supplier.nif" />
|
||||||
|
|
|
@ -42,7 +42,7 @@ const redirectToCreateView = () => {
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<div class="vn-card-list">
|
<div class="vn-card-list">
|
||||||
<VnPaginate data-key="SuppliersList" url="Suppliers/filter" auto-load>
|
<VnPaginate data-key="SuppliersList" url="Suppliers/filter">
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<CardList
|
<CardList
|
||||||
v-for="row of rows"
|
v-for="row of rows"
|
||||||
|
|
|
@ -236,19 +236,9 @@ async function setTravelData(travelData) {
|
||||||
:url="`Travels/${entityId}/getTravel`"
|
:url="`Travels/${entityId}/getTravel`"
|
||||||
@on-fetch="(data) => setTravelData(data)"
|
@on-fetch="(data) => setTravelData(data)"
|
||||||
>
|
>
|
||||||
<template #header-left>
|
|
||||||
<router-link
|
|
||||||
class="header link"
|
|
||||||
:to="{ name: 'TravelSummary', params: { id: entityId } }"
|
|
||||||
>
|
|
||||||
<QIcon name="open_in_new" color="white" size="sm" />
|
|
||||||
<QTooltip>{{ t('travel.pageTitles.summary') }}</QTooltip>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<span>{{ travel.ref }} - {{ travel.id }}</span>
|
<span>{{ travel.ref }} - {{ travel.id }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #header-right>
|
<template #header-right>
|
||||||
<QBtn color="white" dense flat icon="more_vert" round size="md">
|
<QBtn color="white" dense flat icon="more_vert" round size="md">
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
|
|
|
@ -7,8 +7,8 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = session.getToken();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
const counters = ref({
|
const counters = ref({
|
||||||
alquilerBandeja: { count: 0, id: 96001, title: 'CC Bandeja', isTray: true },
|
alquilerBandeja: { count: 0, id: 96001, title: 'CC Bandeja', isTray: true },
|
||||||
|
|
|
@ -22,7 +22,7 @@ const $props = defineProps({
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getToken } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
|
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
|
@ -56,7 +56,7 @@ const filter = {
|
||||||
const sip = computed(() => worker.value?.sip && worker.value.sip.extension);
|
const sip = computed(() => worker.value?.sip && worker.value.sip.extension);
|
||||||
|
|
||||||
function getWorkerAvatar() {
|
function getWorkerAvatar() {
|
||||||
const token = getToken();
|
const token = getTokenMultimedia();
|
||||||
return `/api/Images/user/160x160/${entityId.value}/download?access_token=${token}`;
|
return `/api/Images/user/160x160/${entityId.value}/download?access_token=${token}`;
|
||||||
}
|
}
|
||||||
const data = ref(useCardDescription());
|
const data = ref(useCardDescription());
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { onMounted, ref, computed } from 'vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useRole } from 'src/composables/useRole';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const { hasAny } = useRole();
|
||||||
|
|
||||||
|
const fetchCurrentDeviceRef = ref(null);
|
||||||
|
const deviceProductionsFilter = {
|
||||||
|
fields: ['id', 'serialNumber', 'modelFk'],
|
||||||
|
where: { stateFk: 'idle' },
|
||||||
|
order: 'id',
|
||||||
|
};
|
||||||
|
const deviceProductionsOptions = ref([]);
|
||||||
|
const newPDA = ref({});
|
||||||
|
const currentPDA = ref(null);
|
||||||
|
|
||||||
|
const isAllowedToEdit = computed(() => hasAny(['hr', 'productionAssi']));
|
||||||
|
|
||||||
|
const setCurrentPDA = (data) => {
|
||||||
|
currentPDA.value = data;
|
||||||
|
currentPDA.value.description = `ID: ${currentPDA.value.deviceProductionFk} ${t(
|
||||||
|
'worker.pda.model'
|
||||||
|
)}: ${currentPDA.value.deviceProduction.modelFk} ${t('worker.pda.serialNumber')}: ${
|
||||||
|
currentPDA.value.deviceProduction.serialNumber
|
||||||
|
}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deallocatePDA = async (data) => {
|
||||||
|
try {
|
||||||
|
await axios.post(`Workers/${route.params.id}/deallocatePDA`, {
|
||||||
|
pda: currentPDA.value.deviceProductionFk,
|
||||||
|
});
|
||||||
|
data.pda = null;
|
||||||
|
currentPDA.value = null;
|
||||||
|
await fetchCurrentDeviceRef.value.fetch();
|
||||||
|
notify(t('PDA deallocated'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deallocating PDA');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => await fetchCurrentDeviceRef.value.fetch());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="DeviceProductions"
|
||||||
|
:filter="deviceProductionsFilter"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (deviceProductionsOptions = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
ref="fetchCurrentDeviceRef"
|
||||||
|
url="DeviceProductionUsers"
|
||||||
|
:filter="{
|
||||||
|
where: { userFk: route.params.id },
|
||||||
|
include: { relation: 'deviceProduction' },
|
||||||
|
}"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => setCurrentPDA(data[0])"
|
||||||
|
/>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<FormModel
|
||||||
|
url="DeviceProductionUsers"
|
||||||
|
:url-create="`Workers/${route.params.id}/allocatePDA`"
|
||||||
|
model="DeviceProductionUser"
|
||||||
|
:form-initial-data="newPDA"
|
||||||
|
auto-load
|
||||||
|
:default-buttons="{ save: { label: 'globals.assign', color: 'primary' } }"
|
||||||
|
@on-data-saved="(_, data) => setCurrentPDA(data)"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<QField
|
||||||
|
v-if="currentPDA && currentPDA.description"
|
||||||
|
:label="t('worker.pda.currentPDA')"
|
||||||
|
:model-value="currentPDA.description"
|
||||||
|
:editable="false"
|
||||||
|
class="full-width"
|
||||||
|
>
|
||||||
|
<template #control>
|
||||||
|
<div tabindex="0">
|
||||||
|
{{ currentPDA.description }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="isAllowedToEdit" #append>
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click="deallocatePDA(data)"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('worker.pda.removePDA') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</QField>
|
||||||
|
|
||||||
|
<VnSelectFilter
|
||||||
|
v-else
|
||||||
|
:label="t('worker.pda.newPDA')"
|
||||||
|
v-model="data.pda"
|
||||||
|
:options="deviceProductionsOptions"
|
||||||
|
option-label="serialNumber"
|
||||||
|
option-value="id"
|
||||||
|
hide-selected
|
||||||
|
:disable="!isAllowedToEdit"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>ID: {{ scope.opt?.id }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt?.modelFk }},
|
||||||
|
{{ scope.opt?.serialNumber }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
PDA deallocated: PDA desasignada
|
||||||
|
</i18n>
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'suppliers',
|
title: 'suppliers',
|
||||||
icon: 'vn:supplier',
|
icon: 'vn:supplier',
|
||||||
|
moduleName: 'Supplier',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'SupplierMain' },
|
redirect: { name: 'SupplierMain' },
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'claims',
|
title: 'claims',
|
||||||
icon: 'vn:claims',
|
icon: 'vn:claims',
|
||||||
|
moduleName: 'Claim',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'ClaimMain' },
|
redirect: { name: 'ClaimMain' },
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'customers',
|
title: 'customers',
|
||||||
icon: 'vn:client',
|
icon: 'vn:client',
|
||||||
|
moduleName: 'Customer',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'CustomerMain' },
|
redirect: { name: 'CustomerMain' },
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'entries',
|
title: 'entries',
|
||||||
icon: 'vn:entry',
|
icon: 'vn:entry',
|
||||||
|
moduleName: 'Entry',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'EntryMain' },
|
redirect: { name: 'EntryMain' },
|
||||||
|
|
|
@ -13,6 +13,7 @@ import Travel from './travel';
|
||||||
import Order from './order';
|
import Order from './order';
|
||||||
import Department from './department';
|
import Department from './department';
|
||||||
import Entry from './entry';
|
import Entry from './entry';
|
||||||
|
import roadmap from "./roadmap";
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
Item,
|
Item,
|
||||||
|
@ -30,4 +31,5 @@ export default [
|
||||||
invoiceIn,
|
invoiceIn,
|
||||||
Department,
|
Department,
|
||||||
Entry,
|
Entry,
|
||||||
|
roadmap
|
||||||
];
|
];
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'invoiceIns',
|
title: 'invoiceIns',
|
||||||
icon: 'vn:invoice-in',
|
icon: 'vn:invoice-in',
|
||||||
|
moduleName: 'InvoiceIn',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'InvoiceInMain' },
|
redirect: { name: 'InvoiceInMain' },
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'invoiceOuts',
|
title: 'invoiceOuts',
|
||||||
icon: 'vn:invoice-out',
|
icon: 'vn:invoice-out',
|
||||||
|
moduleName: 'InvoiceOut',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'InvoiceOutMain' },
|
redirect: { name: 'InvoiceOutMain' },
|
||||||
|
@ -43,7 +44,7 @@ export default {
|
||||||
name: 'InvoiceOutNegativeBases',
|
name: 'InvoiceOutNegativeBases',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'negativeBases',
|
title: 'negativeBases',
|
||||||
icon: 'view_list',
|
icon: 'vn:ticket',
|
||||||
},
|
},
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/InvoiceOut/InvoiceOutNegativeBases.vue'),
|
import('src/pages/InvoiceOut/InvoiceOutNegativeBases.vue'),
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'order',
|
title: 'order',
|
||||||
icon: 'vn:basket',
|
icon: 'vn:basket',
|
||||||
|
moduleName: 'Order',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'OrderMain' },
|
redirect: { name: 'OrderMain' },
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { RouterView } from 'vue-router';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
path: '/route/roadmap',
|
||||||
|
name: 'Roadmap',
|
||||||
|
meta: {
|
||||||
|
title: 'roadmap',
|
||||||
|
icon: 'vn:troncales',
|
||||||
|
},
|
||||||
|
component: RouterView,
|
||||||
|
redirect: { name: 'RouteMain' },
|
||||||
|
menus: {
|
||||||
|
card: ['RoadmapBasicData', 'RoadmapStops'],
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'RouteRoadmapCard',
|
||||||
|
path: ':id',
|
||||||
|
component: () => import('src/pages/Route/Roadmap/RoadmapCard.vue'),
|
||||||
|
redirect: { name: 'RoadmapSummary' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'RoadmapSummary',
|
||||||
|
path: 'summary',
|
||||||
|
meta: {
|
||||||
|
title: 'summary',
|
||||||
|
icon: 'open_in_new',
|
||||||
|
},
|
||||||
|
component: () => import('pages/Route/Roadmap/RoadmapSummary.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RoadmapBasicData',
|
||||||
|
path: 'basic-data',
|
||||||
|
meta: {
|
||||||
|
title: 'basicData',
|
||||||
|
icon: 'vn:settings',
|
||||||
|
},
|
||||||
|
component: () => import('pages/Route/Roadmap/RoadmapBasicData.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RoadmapStops',
|
||||||
|
path: 'stops',
|
||||||
|
meta: {
|
||||||
|
title: 'stops',
|
||||||
|
icon: 'vn:lines',
|
||||||
|
},
|
||||||
|
component: () => import('pages/Route/Roadmap/RoadmapStops.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -6,12 +6,13 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'routes',
|
title: 'routes',
|
||||||
icon: 'vn:delivery',
|
icon: 'vn:delivery',
|
||||||
|
moduleName: 'Route',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'RouteMain' },
|
redirect: { name: 'RouteMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['RouteList', 'CmrList'],
|
main: ['RouteList', 'RouteAutonomous', 'RouteRoadmap', 'CmrList'],
|
||||||
card: ['RouteBasicData'],
|
card: ['RouteBasicData', 'RouteTickets', 'RouteLog'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -20,15 +21,6 @@ export default {
|
||||||
component: () => import('src/pages/Route/RouteMain.vue'),
|
component: () => import('src/pages/Route/RouteMain.vue'),
|
||||||
redirect: { name: 'RouteList' },
|
redirect: { name: 'RouteList' },
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
path: 'cmr',
|
|
||||||
name: 'CmrList',
|
|
||||||
meta: {
|
|
||||||
title: 'cmrsList',
|
|
||||||
icon: 'fact_check',
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: 'RouteList',
|
name: 'RouteList',
|
||||||
|
@ -46,6 +38,42 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Route/Card/RouteForm.vue'),
|
component: () => import('src/pages/Route/Card/RouteForm.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'agency-term',
|
||||||
|
name: 'RouteAutonomous',
|
||||||
|
meta: {
|
||||||
|
title: 'autonomous',
|
||||||
|
icon: 'vn:agency-term',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/RouteAutonomous.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'roadmap',
|
||||||
|
name: 'RouteRoadmap',
|
||||||
|
meta: {
|
||||||
|
title: 'RouteRoadmap',
|
||||||
|
icon: 'vn:troncales',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/RouteRoadmap.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'roadmap/create',
|
||||||
|
name: 'RouteRoadmapCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'RouteRoadmapCreate',
|
||||||
|
icon: 'vn:troncales',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/Roadmap/RoadmapCreate.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'cmr',
|
||||||
|
name: 'CmrList',
|
||||||
|
meta: {
|
||||||
|
title: 'cmrsList',
|
||||||
|
icon: 'fact_check',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -72,6 +100,24 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('pages/Route/Card/RouteSummary.vue'),
|
component: () => import('pages/Route/Card/RouteSummary.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tickets',
|
||||||
|
name: 'RouteTickets',
|
||||||
|
meta: {
|
||||||
|
title: 'tickets',
|
||||||
|
icon: 'vn:ticket',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/RouteTickets.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'log',
|
||||||
|
name: 'RouteLog',
|
||||||
|
meta: {
|
||||||
|
title: 'log',
|
||||||
|
icon: 'vn:History',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/RouteLog.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import {RouterView} from "vue-router";
|
import { RouterView } from 'vue-router';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
path: '/shelving',
|
path: '/shelving',
|
||||||
name: 'Shelving',
|
name: 'Shelving',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'shelving',
|
title: 'shelving',
|
||||||
icon: 'vn:inventory'
|
icon: 'vn:inventory',
|
||||||
|
moduleName: 'Shelving',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'ShelvingMain' },
|
redirect: { name: 'ShelvingMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['ShelvingList'],
|
main: ['ShelvingList'],
|
||||||
card: ['ShelvingBasicData', 'ShelvingLog']
|
card: ['ShelvingBasicData', 'ShelvingLog'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -51,8 +52,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'summary',
|
title: 'summary',
|
||||||
},
|
},
|
||||||
component: () =>
|
component: () => import('pages/Shelving/Card/ShelvingSummary.vue'),
|
||||||
import('pages/Shelving/Card/ShelvingSummary.vue'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ShelvingBasicData',
|
name: 'ShelvingBasicData',
|
||||||
|
@ -75,6 +75,5 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'tickets',
|
title: 'tickets',
|
||||||
icon: 'vn:ticket',
|
icon: 'vn:ticket',
|
||||||
|
moduleName: 'Ticket',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'TicketMain' },
|
redirect: { name: 'TicketMain' },
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'travel',
|
title: 'travel',
|
||||||
icon: 'local_airport',
|
icon: 'local_airport',
|
||||||
|
moduleName: 'Travel',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'TravelMain' },
|
redirect: { name: 'TravelMain' },
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'wagons',
|
title: 'wagons',
|
||||||
icon: 'vn:trolley',
|
icon: 'vn:trolley',
|
||||||
|
moduleName: 'Wagon',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'WagonMain' },
|
redirect: { name: 'WagonMain' },
|
||||||
|
|
|
@ -6,12 +6,13 @@ export default {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'workers',
|
title: 'workers',
|
||||||
icon: 'vn:worker',
|
icon: 'vn:worker',
|
||||||
|
moduleName: 'Worker',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'WorkerMain' },
|
redirect: { name: 'WorkerMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['WorkerList', 'WorkerDepartment'],
|
main: ['WorkerList', 'WorkerDepartment'],
|
||||||
card: ['WorkerNotificationsManager'],
|
card: ['WorkerNotificationsManager', 'WorkerPda'],
|
||||||
departmentCard: ['BasicData'],
|
departmentCard: ['BasicData'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@ -75,6 +76,15 @@ export default {
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/Worker/Card/WorkerNotificationsManager.vue'),
|
import('src/pages/Worker/Card/WorkerNotificationsManager.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'WorkerPda',
|
||||||
|
path: 'pda',
|
||||||
|
meta: {
|
||||||
|
title: 'pda',
|
||||||
|
icon: 'phone_android',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Worker/Card/WorkerPda.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -13,6 +13,7 @@ import department from './modules/department';
|
||||||
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';
|
||||||
|
import roadmap from "src/router/modules/roadmap";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
@ -66,6 +67,7 @@ const routes = [
|
||||||
supplier,
|
supplier,
|
||||||
travel,
|
travel,
|
||||||
department,
|
department,
|
||||||
|
roadmap,
|
||||||
entry,
|
entry,
|
||||||
{
|
{
|
||||||
path: '/:catchAll(.*)*',
|
path: '/:catchAll(.*)*',
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue