#6942 improve invoiceIn #220
|
@ -1,6 +1,7 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
|
@ -10,6 +11,7 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
|
|||
import SkeletonTable from 'components/ui/SkeletonTable.vue';
|
||||
import { tMobile } from 'src/composables/tMobile';
|
||||
|
||||
const { push } = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
|
@ -60,6 +62,11 @@ const $props = defineProps({
|
|||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
goTo: {
|
||||
|
||||
type: String,
|
||||
default: '',
|
||||
description: 'It is used for redirect on click "save and continue"',
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = ref(false);
|
||||
|
@ -128,6 +135,11 @@ async function onSubmit() {
|
|||
await saveChanges($props.saveFn ? formData.value : null);
|
||||
}
|
||||
|
||||
async function onSumbitAndGo() {
|
||||
await onSubmit();
|
||||
push({ path: $props.goTo });
|
||||
}
|
||||
|
||||
async function saveChanges(data) {
|
||||
if ($props.saveFn) {
|
||||
$props.saveFn(data, getChanges);
|
||||
|
@ -310,7 +322,40 @@ watch(formUrl, async () => {
|
|||
:title="t('globals.reset')"
|
||||
v-if="$props.defaultReset"
|
||||
/>
|
||||
<QBtnDropdown
|
||||
jorgep
commented
Botón desplegable con la op. de guardar y continuar o solo guardar. Botón desplegable con la op. de guardar y continuar o solo guardar.
|
||||
v-if="$props.goTo && $props.defaultSave"
|
||||
@click="onSumbitAndGo"
|
||||
:label="tMobile('globals.saveAndContinue')"
|
||||
:title="t('globals.saveAndContinue')"
|
||||
:disable="!hasChanges"
|
||||
color="primary"
|
||||
icon="save"
|
||||
split
|
||||
>
|
||||
<QList>
|
||||
<QItem
|
||||
color="primary"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="onSubmit"
|
||||
:title="t('globals.save')"
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
<QIcon
|
||||
name="save"
|
||||
color="white"
|
||||
class="q-mr-sm"
|
||||
size="sm"
|
||||
/>
|
||||
{{ t('globals.save').toUpperCase() }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QBtnDropdown>
|
||||
<QBtn
|
||||
v-else-if="!$props.goTo && $props.defaultSave"
|
||||
:label="tMobile('globals.save')"
|
||||
ref="saveButtonRef"
|
||||
color="primary"
|
||||
|
@ -318,7 +363,6 @@ watch(formUrl, async () => {
|
|||
@click="onSubmit"
|
||||
:disable="!hasChanges"
|
||||
:title="t('globals.save')"
|
||||
v-if="$props.defaultSave"
|
||||
/>
|
||||
<slot name="moreAfterActions" />
|
||||
</QBtnGroup>
|
||||
|
|
|
@ -24,7 +24,7 @@ const $props = defineProps({
|
|||
default: '',
|
||||
},
|
||||
limit: {
|
||||
type: String,
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
jorgep
commented
Elimino el warning de consola. Elimino el warning de consola.
|
||||
params: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue';
|
||||
import { onBeforeRouteLeave } from 'vue-router';
|
||||
import { onBeforeRouteLeave, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
@ -11,7 +11,9 @@ import useNotify from 'src/composables/useNotify.js';
|
|||
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
||||
import VnConfirm from './ui/VnConfirm.vue';
|
||||
import { tMobile } from 'src/composables/tMobile';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
|
||||
const { push } = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const state = useState();
|
||||
const stateStore = useStateStore();
|
||||
|
@ -74,55 +76,17 @@ const $props = defineProps({
|
|||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
goTo: {
|
||||
jorgep
commented
Lo mismo que en crudModel Lo mismo que en crudModel
|
||||
type: String,
|
||||
default: '',
|
||||
description: 'It is used for redirect on click "save and continue"',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||
|
||||
const componentIsRendered = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
originalData.value = $props.formInitialData;
|
||||
nextTick(() => {
|
||||
componentIsRendered.value = true;
|
||||
});
|
||||
|
||||
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
|
||||
state.set($props.model, $props.formInitialData);
|
||||
if ($props.autoLoad && !$props.formInitialData) {
|
||||
await fetch();
|
||||
}
|
||||
|
||||
// Si así se desea disparamos el watcher del form después de 100ms, asi darle tiempo de que se haya cargado la data inicial
|
||||
// para evitar que detecte cambios cuando es data inicial default
|
||||
if ($props.observeFormChanges) {
|
||||
setTimeout(() => {
|
||||
startFormWatcher();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
if (hasChanges.value && $props.observeFormChanges)
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('Unsaved changes will be lost'),
|
||||
message: t('Are you sure exit without saving?'),
|
||||
promise: () => next(),
|
||||
},
|
||||
});
|
||||
else next();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 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 arrayData = useArrayData($props.model);
|
||||
const isLoading = ref(false);
|
||||
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
|
||||
const isResetting = ref(false);
|
||||
|
@ -143,29 +107,72 @@ const defaultButtons = computed(() => ({
|
|||
},
|
||||
...$props.defaultButtons,
|
||||
}));
|
||||
const startFormWatcher = () => {
|
||||
|
||||
onMounted(async () => {
|
||||
jorgep
commented
Lo he pasado aquí para que todos los hooks estén agrupados. Lo he pasado aquí para que todos los hooks estén agrupados.
|
||||
originalData.value = JSON.parse(JSON.stringify($props.formInitialData ?? {}));
|
||||
jorgep
commented
Hay que hacerlo así para que se haga una copia, si no, se hace una referencia al mismo objeto. Hay que hacerlo así para que se haga una copia, si no, se hace una referencia al mismo objeto.
jsegarra
commented
Se podría poner el comentario en código? Se podría poner el comentario en código?
|
||||
|
||||
nextTick(() => (componentIsRendered.value = true));
|
||||
|
||||
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
|
||||
state.set($props.model, $props.formInitialData);
|
||||
|
||||
if ($props.autoLoad && !$props.formInitialData && $props.url) await fetch();
|
||||
else if (arrayData.store.data) updateAndEmit(arrayData.store.data, 'onFetch');
|
||||
|
||||
if ($props.observeFormChanges) {
|
||||
watch(
|
||||
() => formData.value,
|
||||
(newVal, oldVal) => {
|
||||
if (!oldVal) return;
|
||||
hasChanges.value =
|
||||
!isResetting.value &&
|
||||
JSON.stringify(newVal) !== JSON.stringify(originalData.value);
|
||||
jorgep
commented
Para ver si hay cambios comparamos el obj actual con el original. Con la lógica anterior, no funcionaba bien si se usa una store, ya que al emitir este valor se 'actualiza'. Así se hace la comprobación correcta. Para ver si hay cambios comparamos el obj actual con el original. Con la lógica anterior, no funcionaba bien si se usa una store, ya que al emitir este valor se 'actualiza'. Así se hace la comprobación correcta.
jsegarra
commented
Se podría poner el comentario en código? Se podría poner el comentario en código?
jorgep
commented
yo lo veo bien sin comentarios. lo que diga @jgallego yo lo veo bien sin comentarios. lo que diga @jgallego
jgallego
commented
Yo no pondría comentarios, pero si en otros sitios está implementado distinto yo lo cambiaría para que quien venga detrás coja siempre una buena implementación Yo no pondría comentarios, pero si en otros sitios está implementado distinto yo lo cambiaría para que quien venga detrás coja siempre una buena implementación
|
||||
isResetting.value = false;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (!$props.url)
|
||||
watch(
|
||||
() => formData.value,
|
||||
(val) => {
|
||||
hasChanges.value = !isResetting.value && val;
|
||||
isResetting.value = false;
|
||||
},
|
||||
{ deep: true }
|
||||
() => arrayData.store.data,
|
||||
(val) => updateAndEmit(val, 'onFetch')
|
||||
);
|
||||
};
|
||||
|
||||
watch(formUrl, async () => {
|
||||
jorgep
commented
Agrupado con el resto de hooks. Agrupado con el resto de hooks.
|
||||
originalData.value = null;
|
||||
reset();
|
||||
await fetch();
|
||||
});
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
jorgep
commented
Agrupado con el resto de hooks Agrupado con el resto de hooks
|
||||
if (hasChanges.value && $props.observeFormChanges)
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('Unsaved changes will be lost'),
|
||||
message: t('Are you sure exit without saving?'),
|
||||
promise: () => next(),
|
||||
},
|
||||
});
|
||||
else next();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 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) return state.set($props.model, originalData.value);
|
||||
if ($props.clearStoreOnUnmount) state.unset($props.model);
|
||||
});
|
||||
|
||||
async function fetch() {
|
||||
try {
|
||||
let { data } = await axios.get($props.url, {
|
||||
params: { filter: JSON.stringify($props.filter) },
|
||||
});
|
||||
|
||||
if (Array.isArray(data)) data = data[0] ?? {};
|
||||
|
||||
state.set($props.model, data);
|
||||
originalData.value = data && JSON.parse(JSON.stringify(data));
|
||||
|
||||
emit('onFetch', state.get($props.model));
|
||||
updateAndEmit(data, 'onFetch');
|
||||
} catch (error) {
|
||||
state.set($props.model, {});
|
||||
originalData.value = {};
|
||||
|
@ -173,38 +180,39 @@ async function fetch() {
|
|||
}
|
||||
|
||||
async function save() {
|
||||
if ($props.observeFormChanges && !hasChanges.value) {
|
||||
notify('globals.noChanges', 'negative');
|
||||
return;
|
||||
}
|
||||
isLoading.value = true;
|
||||
if ($props.observeFormChanges && !hasChanges.value)
|
||||
return notify('globals.noChanges', 'negative');
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||
const method = $props.urlCreate ? 'post' : 'patch';
|
||||
const url =
|
||||
jorgep
commented
En mi opinión queda mucho más legible así, aunque solo se use 1 vez. En mi opinión queda mucho más legible así, aunque solo se use 1 vez.
|
||||
$props.urlCreate || $props.urlUpdate || $props.url || arrayData.store.url;
|
||||
let response;
|
||||
|
||||
if ($props.saveFn) response = await $props.saveFn(body);
|
||||
else
|
||||
response = await axios[$props.urlCreate ? 'post' : 'patch'](
|
||||
$props.urlCreate || $props.urlUpdate || $props.url,
|
||||
body
|
||||
);
|
||||
else response = await axios[method](url, body);
|
||||
|
||||
if ($props.urlCreate) notify('globals.dataCreated', 'positive');
|
||||
|
||||
emit('onDataSaved', formData.value, response?.data);
|
||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||
hasChanges.value = false;
|
||||
isLoading.value = false;
|
||||
|
||||
updateAndEmit(response?.data, 'onDataSaved');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
notify('errors.writeRequest', 'negative');
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
async function saveAndGo() {
|
||||
await save();
|
||||
push({ path: $props.goTo });
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.set($props.model, originalData.value);
|
||||
originalData.value = JSON.parse(JSON.stringify(originalData.value));
|
||||
|
||||
emit('onFetch', state.get($props.model));
|
||||
updateAndEmit(originalData.value, 'onFetch');
|
||||
if ($props.observeFormChanges) {
|
||||
hasChanges.value = false;
|
||||
isResetting.value = true;
|
||||
|
@ -226,17 +234,15 @@ function filter(value, update, filterOptions) {
|
|||
);
|
||||
}
|
||||
|
||||
watch(formUrl, async () => {
|
||||
originalData.value = null;
|
||||
reset();
|
||||
fetch();
|
||||
});
|
||||
function updateAndEmit(val, evt) {
|
||||
jorgep
commented
Esta parte se repetía varias veces Esta parte se repetía varias veces
|
||||
state.set($props.model, val);
|
||||
originalData.value = val && JSON.parse(JSON.stringify(val));
|
||||
if (!$props.url) arrayData.store.data = val;
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
isLoading,
|
||||
hasChanges,
|
||||
});
|
||||
emit(evt, state.get($props.model));
|
||||
}
|
||||
|
||||
defineExpose({ save, isLoading, hasChanges });
|
||||
</script>
|
||||
<template>
|
||||
<div class="column items-center full-width">
|
||||
|
@ -273,10 +279,42 @@ defineExpose({
|
|||
:disable="!hasChanges"
|
||||
:title="t(defaultButtons.reset.label)"
|
||||
/>
|
||||
<QBtnDropdown
|
||||
jorgep
commented
Lo mismo que crudModel Lo mismo que crudModel
|
||||
v-if="$props.goTo"
|
||||
@click="saveAndGo"
|
||||
:label="tMobile('globals.saveAndContinue')"
|
||||
:title="t('globals.saveAndContinue')"
|
||||
:disable="!hasChanges"
|
||||
color="primary"
|
||||
icon="save"
|
||||
split
|
||||
>
|
||||
<QList>
|
||||
<QItem
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="save"
|
||||
:title="t('globals.save')"
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
<QIcon
|
||||
name="save"
|
||||
color="white"
|
||||
class="q-mr-sm"
|
||||
size="sm"
|
||||
/>
|
||||
{{ t('globals.save').toUpperCase() }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QBtnDropdown>
|
||||
<QBtn
|
||||
:label="tMobile(defaultButtons.save.label)"
|
||||
:color="defaultButtons.save.color"
|
||||
:icon="defaultButtons.save.icon"
|
||||
v-else
|
||||
:label="tMobile('globals.save')"
|
||||
color="primary"
|
||||
icon="save"
|
||||
@click="save"
|
||||
:disable="!hasChanges"
|
||||
:title="t(defaultButtons.save.label)"
|
||||
|
|
|
@ -44,7 +44,7 @@ onBeforeMount(async () => {
|
|||
if (props.baseUrl) {
|
||||
onBeforeRouteUpdate(async (to, from) => {
|
||||
jorgep
commented
Al no haber cambiado de ruta, se estaba poniendo el mismo id actual. Al no haber cambiado de ruta, se estaba poniendo el mismo id actual.
|
||||
if (to.params.id !== from.params.id) {
|
||||
arrayData.store.url = `${props.baseUrl}/${route.params.id}`;
|
||||
arrayData.store.url = `${props.baseUrl}/${to.params.id}`;
|
||||
await arrayData.fetch({ append: false });
|
||||
}
|
||||
});
|
||||
|
|
|
@ -78,6 +78,7 @@ async function save() {
|
|||
const body = mapperDms(dms.value);
|
||||
const response = await axios.post(getUrl(), body[0], body[1]);
|
||||
emit('onDataSaved', body[1].params, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
function defaultData() {
|
||||
|
|
|
@ -622,21 +622,6 @@ setLogTree();
|
|||
</QList>
|
||||
</div>
|
||||
</div>
|
||||
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<QBtn
|
||||
flat
|
||||
@click.stop="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<QTooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QDrawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300">
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<QList dense>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<script setup>
|
||||
const $props = defineProps({
|
||||
defineProps({
|
||||
url: { type: String, default: null },
|
||||
text: { type: String, default: null },
|
||||
icon: { type: String, default: 'open_in_new' },
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="titleBox">
|
||||
<div :class="$q.screen.gt.md ? 'q-pb-lg' : 'q-pb-md'">
|
||||
<div class="header-link">
|
||||
<a :href="$props.url" :class="$props.url ? 'link' : 'color-vn-text'">
|
||||
{{ $props.text }}
|
||||
<QIcon v-if="url" :name="$props.icon" />
|
||||
<a :href="url" :class="url ? 'link' : 'color-vn-text'">
|
||||
{{ text }}
|
||||
<QIcon v-if="url" :name="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,7 +19,4 @@ const $props = defineProps({
|
|||
a {
|
||||
font-size: large;
|
||||
}
|
||||
.titleBox {
|
||||
padding-bottom: 2%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -5,6 +5,7 @@ import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
|||
import { useArrayData } from 'composables/useArrayData';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const $props = defineProps({
|
||||
url: {
|
||||
|
@ -15,21 +16,21 @@ const $props = defineProps({
|
|||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
module: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
subtitle: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
default: null,
|
||||
},
|
||||
dataKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: null,
|
||||
},
|
||||
module: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
summary: {
|
||||
type: Object,
|
||||
|
@ -40,21 +41,27 @@ const $props = defineProps({
|
|||
const state = useState();
|
||||
const { t } = useI18n();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const arrayData = useArrayData($props.dataKey || $props.module, {
|
||||
url: $props.url,
|
||||
filter: $props.filter,
|
||||
skip: 0,
|
||||
});
|
||||
const { store } = arrayData;
|
||||
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
|
||||
let arrayData;
|
||||
let store;
|
||||
let entity;
|
||||
const isLoading = ref(false);
|
||||
|
||||
defineExpose({
|
||||
getData,
|
||||
});
|
||||
defineExpose({ getData });
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await getData();
|
||||
watch($props, async () => await getData());
|
||||
arrayData = useArrayData($props.dataKey, {
|
||||
url: $props.url,
|
||||
filter: $props.filter,
|
||||
skip: 0,
|
||||
});
|
||||
store = arrayData.store;
|
||||
entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
|
||||
// It enables to load data only once if the module is the same as the dataKey
|
||||
if ($props.dataKey !== useRoute().meta.moduleName) await getData();
|
||||
jorgep
commented
Permite cargar los datos solo una vez si el módulo es el mismo que dataKey Permite cargar los datos solo una vez si el módulo es el mismo que dataKey
jsegarra
commented
Se podría poner el comentario en código? Se podría poner el comentario en código?
|
||||
watch(
|
||||
() => [$props.url, $props.filter],
|
||||
jorgep
commented
Solo interesa ver cambios en estas 2 props. Solo interesa ver cambios en estas 2 props.
|
||||
async () => await getData()
|
||||
);
|
||||
});
|
||||
|
||||
async function getData() {
|
||||
|
@ -132,7 +139,7 @@ const emit = defineEmits(['onFetch']);
|
|||
<QItemLabel header class="ellipsis text-h5" :lines="1">
|
||||
<div class="title">
|
||||
<span v-if="$props.title" :title="$props.title">
|
||||
{{ $props.title }}
|
||||
{{ entity[title] ?? $props.title }}
|
||||
</span>
|
||||
<slot v-else name="description" :entity="entity">
|
||||
<span :title="entity.name">
|
||||
|
|
|
@ -24,6 +24,7 @@ globals:
|
|||
create: Create
|
||||
edit: Edit
|
||||
save: Save
|
||||
saveAndContinue: Save and continue
|
||||
remove: Remove
|
||||
reset: Reset
|
||||
close: Close
|
||||
|
|
|
@ -24,6 +24,7 @@ globals:
|
|||
create: Crear
|
||||
edit: Modificar
|
||||
save: Guardar
|
||||
saveAndContinue: Guardar y continuar
|
||||
remove: Eliminar
|
||||
reset: Restaurar
|
||||
close: Cerrar
|
||||
|
|
|
@ -9,18 +9,21 @@ import { downloadFile } from 'src/composables/downloadFile';
|
|||
import FormModel from 'components/FormModel.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnRow from 'src/components/ui/VnRow.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const dms = ref({});
|
||||
const route = useRoute();
|
||||
const editDownloadDisabled = ref(false);
|
||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
Usar useRoute().meta.moduleName Usar useRoute().meta.moduleName
|
||||
const arrayData = useArrayData('InvoiceIn');
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
const invoiceIn = computed(() => useArrayData(route.meta.moduleName).store.data);
|
||||
const userConfig = ref(null);
|
||||
const invoiceId = computed(() => +route.params.id);
|
||||
|
||||
const expenses = ref([]);
|
||||
const currencies = ref([]);
|
||||
const currenciesRef = ref();
|
||||
const companies = ref([]);
|
||||
|
@ -31,14 +34,11 @@ const warehouses = ref([]);
|
|||
const warehousesRef = ref();
|
||||
const allowTypesRef = ref();
|
||||
const allowedContentTypes = ref([]);
|
||||
const sageWithholdings = ref([]);
|
||||
const inputFileRef = ref();
|
||||
const editDmsRef = ref();
|
||||
const createDmsRef = ref();
|
||||
|
||||
const requiredFieldRule = (val) => val || t('globals.requiredField');
|
||||
const dateMask = '####-##-##';
|
||||
const fillMask = '_';
|
||||
|
||||
async function checkFileExists(dmsId) {
|
||||
if (!dmsId) return;
|
||||
try {
|
||||
|
@ -173,11 +173,17 @@ async function upsert() {
|
|||
@on-fetch="(data) => (userConfig = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData url="Expenses" auto-load @on-fetch="(data) => (expenses = data)" />
|
||||
<FetchData
|
||||
url="SageWithholdings"
|
||||
auto-load
|
||||
@on-fetch="(data) => (sageWithholdings = data)"
|
||||
/>
|
||||
<FormModel
|
||||
v-if="invoiceIn"
|
||||
:url="`InvoiceIns/${route.params.id}`"
|
||||
model="invoiceIn"
|
||||
:auto-load="true"
|
||||
model="InvoiceIn"
|
||||
:go-to="`/invoice-in/${invoiceId}/vat`"
|
||||
auto-load
|
||||
:url-update="`InvoiceIns/${invoiceId}/updateInvoiceIn`"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow>
|
||||
|
@ -201,7 +207,7 @@ async function upsert() {
|
|||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<QInput
|
||||
<VnInput
|
||||
clearable
|
||||
clear-icon="close"
|
||||
:label="t('Supplier ref')"
|
||||
|
@ -209,69 +215,29 @@ async function upsert() {
|
|||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QInput
|
||||
:label="t('Expedition date')"
|
||||
v-model="data.issued"
|
||||
:mask="dateMask"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer" :fill-mask="fillMask">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<QDate v-model="data.issued">
|
||||
<div class="row items-center justify-end">
|
||||
<QBtn
|
||||
v-close-popup
|
||||
label="Close"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</QDate>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
<QInput
|
||||
<VnInputDate :label="t('Expedition date')" v-model="data.issued" />
|
||||
<VnInputDate
|
||||
:label="t('Operation date')"
|
||||
v-model="data.operated"
|
||||
:mask="dateMask"
|
||||
:fill-mask="fillMask"
|
||||
autofocus
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<QDate v-model="data.operated" :mask="dateMask">
|
||||
<div class="row items-center justify-end">
|
||||
<QBtn
|
||||
v-close-popup
|
||||
label="Close"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</QDate>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QInput
|
||||
<VnSelect
|
||||
:label="t('Undeductible VAT')"
|
||||
v-model="data.deductibleExpenseFk"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
/>
|
||||
<QInput
|
||||
:options="expenses"
|
||||
option-value="id"
|
||||
option-label="id"
|
||||
:filter-options="['id', 'name']"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnInput
|
||||
:label="t('Document')"
|
||||
v-model="data.dmsFk"
|
||||
clearable
|
||||
|
@ -316,67 +282,11 @@ async function upsert() {
|
|||
<QTooltip>{{ t('Create document') }}</QTooltip>
|
||||
</QBtn>
|
||||
</template>
|
||||
</QInput>
|
||||
</VnInput>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QInput
|
||||
:label="t('Entry date')"
|
||||
v-model="data.bookEntried"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
:mask="dateMask"
|
||||
:fill-mask="fillMask"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<QDate v-model="data.bookEntried" :mask="dateMask">
|
||||
<div class="row items-center justify-end">
|
||||
<QBtn
|
||||
v-close-popup
|
||||
label="Close"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</QDate>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
<QInput
|
||||
:label="t('Accounted date')"
|
||||
v-model="data.booked"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
:mask="dateMask"
|
||||
:fill-mask="fillMask"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<QDate v-model="data.booked" :mask="maskDate">
|
||||
<div class="row items-center justify-end">
|
||||
<QBtn
|
||||
v-close-popup
|
||||
label="Close"
|
||||
color="primary"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
</QDate>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
<VnInputDate :label="t('Entry date')" v-model="data.bookEntried" />
|
||||
<VnInputDate :label="t('Accounted date')" v-model="data.booked" />
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
|
@ -386,6 +296,7 @@ async function upsert() {
|
|||
option-value="id"
|
||||
option-label="code"
|
||||
/>
|
||||
|
||||
<VnSelect
|
||||
v-if="companiesRef"
|
||||
:label="t('Company')"
|
||||
|
@ -395,7 +306,15 @@ async function upsert() {
|
|||
option-label="code"
|
||||
/>
|
||||
</VnRow>
|
||||
<QCheckbox :label="t('invoiceIn.summary.booked')" v-model="data.isBooked" />
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('invoiceIn.summary.sage')"
|
||||
v-model="data.withholdingSageFk"
|
||||
:options="sageWithholdings"
|
||||
option-value="id"
|
||||
option-label="withholding"
|
||||
/>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
<QDialog ref="editDmsRef">
|
||||
|
@ -411,7 +330,7 @@ async function upsert() {
|
|||
</QCardSection>
|
||||
<QCardSection class="q-py-none">
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInput
|
||||
class="full-width q-pa-xs"
|
||||
:label="t('Reference')"
|
||||
v-model="dms.reference"
|
||||
|
@ -420,45 +339,45 @@ async function upsert() {
|
|||
/>
|
||||
<VnSelect
|
||||
class="full-width q-pa-xs"
|
||||
:label="`${t('Company')}*`"
|
||||
:label="t('Company')"
|
||||
v-model="dms.companyId"
|
||||
:options="companies"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
class="full-width q-pa-xs"
|
||||
:label="`${t('Warehouse')}*`"
|
||||
:label="t('Warehouse')"
|
||||
v-model="dms.warehouseId"
|
||||
:options="warehouses"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
<VnSelect
|
||||
class="full-width q-pa-xs"
|
||||
:label="`${t('Type')}*`"
|
||||
:label="t('Type')"
|
||||
v-model="dms.dmsTypeId"
|
||||
:options="dmsTypes"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
class="full-width q-pa-xs"
|
||||
<VnInput
|
||||
:label="t('Description')"
|
||||
v-model="dms.description"
|
||||
:required="true"
|
||||
type="textarea"
|
||||
class="full-width q-pa-xs"
|
||||
size="lg"
|
||||
autogrow
|
||||
:label="`${t('Description')}*`"
|
||||
v-model="dms.description"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
:rules="[(val) => val.length || t('Required field')]"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
|
@ -522,7 +441,7 @@ async function upsert() {
|
|||
</QCardSection>
|
||||
<QCardSection class="q-pb-none">
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInput
|
||||
class="full-width q-pa-xs"
|
||||
:label="t('Reference')"
|
||||
v-model="dms.reference"
|
||||
|
@ -534,7 +453,7 @@ async function upsert() {
|
|||
:options="companies"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
|
@ -545,7 +464,7 @@ async function upsert() {
|
|||
:options="warehouses"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
<VnSelect
|
||||
class="full-width q-pa-xs"
|
||||
|
@ -554,11 +473,11 @@ async function upsert() {
|
|||
:options="dmsTypes"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInput
|
||||
class="full-width q-pa-xs"
|
||||
type="textarea"
|
||||
size="lg"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { useCapitalize } from 'src/composables/useCapitalize';
|
||||
|
@ -8,12 +8,11 @@ import CrudModel from 'src/components/CrudModel.vue';
|
|||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { push, currentRoute } = useRouter();
|
||||
const { t } = useI18n();
|
||||
|
||||
const invoiceId = route.params.id;
|
||||
const arrayData = useArrayData('InvoiceIn');
|
||||
const invoiceId = +currentRoute.value.params.id;
|
||||
const arrayData = useArrayData(currentRoute.value.meta.moduleName);
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
const invoiceInCorrectionRef = ref();
|
||||
const filter = {
|
||||
|
@ -74,7 +73,7 @@ const rowsSelected = ref([]);
|
|||
|
||||
const requiredFieldRule = (val) => val || t('globals.requiredField');
|
||||
|
||||
const onSave = (data) => data.deletes && router.push(`/invoice-in/${invoiceId}/summary`);
|
||||
const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<script setup>
|
||||
import { ref, reactive, computed, onBeforeMount, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ref, reactive, computed, onBeforeMount } from 'vue';
|
||||
import { useRouter, onBeforeRouteLeave } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { toCurrency, toDate } from 'src/filters';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
import { downloadFile } from 'src/composables/downloadFile';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
|
@ -17,27 +16,23 @@ import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
|||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import { useCapitalize } from 'src/composables/useCapitalize';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import InvoiceInToBook from '../InvoiceInToBook.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
const $props = defineProps({ id: { type: Number, default: null } });
|
||||
|
||||
const { push, currentRoute } = useRouter();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { hasAny } = useRole();
|
||||
const { t } = useI18n();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
const arrayData = useArrayData('InvoiceIn');
|
||||
const { store } = useArrayData(currentRoute.value.meta.moduleName);
|
||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
useRoute().meta.moduleName useRoute().meta.moduleName
|
||||
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
const invoiceIn = computed(() => store.data);
|
||||
const cardDescriptorRef = ref();
|
||||
const correctionDialogRef = ref();
|
||||
const entityId = computed(() => $props.id || +route.params.id);
|
||||
const entityId = computed(() => $props.id || +currentRoute.value.params.id);
|
||||
const totalAmount = ref();
|
||||
const currentAction = ref();
|
||||
const config = ref();
|
||||
|
@ -45,28 +40,21 @@ const cplusRectificationTypes = ref([]);
|
|||
const siiTypeInvoiceOuts = ref([]);
|
||||
const invoiceCorrectionTypes = ref([]);
|
||||
const actions = {
|
||||
jorgep marked this conversation as resolved
jsegarra
commented
Hay 3 acciones y el texto es el mismo, menos el verbo de la acción. Se puede hacer una traducción con parámetro Hay 3 acciones y el texto es el mismo, menos el verbo de la acción.
Se puede hacer una traducción con parámetro
|
||||
book: {
|
||||
title: 'Are you sure you want to book this invoice?',
|
||||
cb: checkToBook,
|
||||
action: toBook,
|
||||
unbook: {
|
||||
title: t('assertAction', { action: t('unbook') }),
|
||||
action: toUnbook,
|
||||
},
|
||||
delete: {
|
||||
title: 'Are you sure you want to delete this invoice?',
|
||||
title: t('assertAction', { action: t('delete') }),
|
||||
action: deleteInvoice,
|
||||
},
|
||||
clone: {
|
||||
title: 'Are you sure you want to clone this invoice?',
|
||||
title: t('assertAction', { action: t('clone') }),
|
||||
action: cloneInvoice,
|
||||
},
|
||||
showPdf: {
|
||||
cb: showPdfInvoice,
|
||||
},
|
||||
sendPdf: {
|
||||
cb: sendPdfInvoiceConfirmation,
|
||||
},
|
||||
correct: {
|
||||
cb: () => correctionDialogRef.value.show(),
|
||||
},
|
||||
showPdf: { cb: showPdfInvoice },
|
||||
sendPdf: { cb: sendPdfInvoiceConfirmation },
|
||||
correct: { cb: () => correctionDialogRef.value.show() },
|
||||
};
|
||||
const filter = {
|
||||
include: [
|
||||
|
@ -94,11 +82,7 @@ const filter = {
|
|||
},
|
||||
],
|
||||
};
|
||||
const data = ref(useCardDescription());
|
||||
const invoiceInCorrection = reactive({
|
||||
correcting: [],
|
||||
corrected: null,
|
||||
});
|
||||
const invoiceInCorrection = reactive({ correcting: [], corrected: null });
|
||||
const routes = reactive({
|
||||
getSupplier: (id) => {
|
||||
return { name: 'SupplierCard', params: { id } };
|
||||
|
@ -139,16 +123,17 @@ const correctionFormData = reactive({
|
|||
});
|
||||
const isNotFilled = computed(() => Object.values(correctionFormData).includes(null));
|
||||
|
||||
onBeforeMount(async () => await setInvoiceCorrection(entityId.value));
|
||||
onBeforeMount(async () => {
|
||||
await setInvoiceCorrection(entityId.value);
|
||||
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
|
||||
totalAmount.value = data.totalDueDay;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
async (newId) => {
|
||||
invoiceInCorrection.correcting.length = 0;
|
||||
invoiceInCorrection.corrected = null;
|
||||
if (newId) await setInvoiceCorrection(entityId.value);
|
||||
}
|
||||
);
|
||||
onBeforeRouteLeave(async (to, from) => {
|
||||
invoiceInCorrection.correcting.length = 0;
|
||||
invoiceInCorrection.corrected = null;
|
||||
if (to.params.id !== from.params.id) await setInvoiceCorrection(entityId.value);
|
||||
});
|
||||
|
||||
async function setInvoiceCorrection(id) {
|
||||
const [{ data: correctingData }, { data: correctedData }] = await Promise.all([
|
||||
|
@ -179,17 +164,6 @@ async function setInvoiceCorrection(id) {
|
|||
);
|
||||
}
|
||||
|
||||
async function setData(entity) {
|
||||
data.value = useCardDescription(entity.supplierRef, entity.id);
|
||||
const { totalDueDay } = await getTotals();
|
||||
totalAmount.value = totalDueDay;
|
||||
}
|
||||
|
||||
async function getTotals() {
|
||||
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
|
||||
return data;
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
|
@ -200,38 +174,17 @@ function openDialog() {
|
|||
});
|
||||
}
|
||||
|
||||
async function checkToBook() {
|
||||
let directBooking = true;
|
||||
async function toUnbook() {
|
||||
const { data } = await axios.post(`InvoiceIns/${entityId.value}/toUnbook`);
|
||||
const { isLinked, bookEntry, accountingEntries } = data;
|
||||
|
||||
const totals = await getTotals();
|
||||
const taxableBaseNotEqualDueDay = totals.totalDueDay != totals.totalTaxableBase;
|
||||
const vatNotEqualDueDay = totals.totalDueDay != totals.totalVat;
|
||||
const type = isLinked ? 'warning' : 'positive';
|
||||
const message = isLinked
|
||||
? t('isLinked', { bookEntry, accountingEntries })
|
||||
: t('isNotLinked', { bookEntry });
|
||||
|
||||
if (taxableBaseNotEqualDueDay && vatNotEqualDueDay) directBooking = false;
|
||||
|
||||
const { data: dueDaysCount } = await axios.get('InvoiceInDueDays/count', {
|
||||
where: {
|
||||
invoiceInFk: entityId.value,
|
||||
dueDated: { gte: Date.vnNew() },
|
||||
},
|
||||
});
|
||||
|
||||
if (dueDaysCount) directBooking = false;
|
||||
|
||||
if (!directBooking) openDialog();
|
||||
else toBook();
|
||||
}
|
||||
|
||||
async function toBook() {
|
||||
await axios.post(`InvoiceIns/${entityId.value}/toBook`);
|
||||
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('globals.dataSaved'),
|
||||
});
|
||||
|
||||
await cardDescriptorRef.value.getData();
|
||||
setTimeout(() => location.reload(), 500);
|
||||
quasar.notify({ type, message });
|
||||
if (!isLinked) store.data.isBooked = false;
|
||||
}
|
||||
|
||||
async function deleteInvoice() {
|
||||
|
@ -240,7 +193,7 @@ async function deleteInvoice() {
|
|||
type: 'positive',
|
||||
message: t('Invoice deleted'),
|
||||
});
|
||||
router.push({ path: '/invoice-in' });
|
||||
push({ path: '/invoice-in' });
|
||||
}
|
||||
|
||||
async function cloneInvoice() {
|
||||
|
@ -249,11 +202,9 @@ async function cloneInvoice() {
|
|||
type: 'positive',
|
||||
message: t('Invoice cloned'),
|
||||
});
|
||||
router.push({ path: `/invoice-in/${data.id}/summary` });
|
||||
push({ path: `/invoice-in/${data.id}/summary` });
|
||||
}
|
||||
|
||||
const requiredFieldRule = (val) => val || t('globals.requiredField');
|
||||
|
||||
const isAdministrative = () => hasAny(['administrative']);
|
||||
|
||||
const isAgricultural = () =>
|
||||
|
@ -299,10 +250,9 @@ const createInvoiceInCorrection = async () => {
|
|||
'InvoiceIns/corrective',
|
||||
Object.assign(correctionFormData, { id: entityId.value })
|
||||
);
|
||||
router.push({ path: `/invoice-in/${correctingId}/summary` });
|
||||
push({ path: `/invoice-in/${correctingId}/summary` });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="InvoiceInConfigs"
|
||||
|
@ -329,21 +279,33 @@ const createInvoiceInCorrection = async () => {
|
|||
<CardDescriptor
|
||||
ref="cardDescriptorRef"
|
||||
module="InvoiceIn"
|
||||
data-key="InvoiceIn"
|
||||
:url="`InvoiceIns/${entityId}`"
|
||||
:filter="filter"
|
||||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
@on-fetch="setData"
|
||||
data-key="invoiceInData"
|
||||
title="supplierRef"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
<InvoiceInToBook>
|
||||
<template #content="{ book }">
|
||||
<QItem
|
||||
v-if="!entity?.isBooked && isAdministrative()"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="book(entityId)"
|
||||
>
|
||||
<QItemSection>{{ t('To book') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</InvoiceInToBook>
|
||||
<QItem
|
||||
v-if="!entity.isBooked && isAdministrative()"
|
||||
v-if="entity?.isBooked && isAdministrative()"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('book')"
|
||||
@click="triggerMenu('unbook')"
|
||||
>
|
||||
<QItemSection>{{ t('To book') }}</QItemSection>
|
||||
<QItemSection>
|
||||
{{ t('To unbook') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="isAdministrative()"
|
||||
|
@ -395,25 +357,24 @@ const createInvoiceInCorrection = async () => {
|
|||
>
|
||||
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="entity.dmsFk"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="downloadFile(entity.dmsFk)"
|
||||
>
|
||||
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('invoiceIn.card.issued')" :value="toDate(entity.issued)" />
|
||||
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
|
||||
<VnLv :label="t('invoiceIn.card.amount')" :value="toCurrency(totalAmount)" />
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.supplier')"
|
||||
:value="entity.supplier?.nickname"
|
||||
:label="t('invoiceIn.card.amount')"
|
||||
:value="toCurrency(totalAmount, entity.currency?.code)"
|
||||
/>
|
||||
<VnLv :label="t('invoiceIn.summary.supplier')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ entity?.supplier?.nickname }}
|
||||
<SupplierDescriptorProxy :id="entity?.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
</template>
|
||||
<template #actions="{ entity }">
|
||||
<template #action="{ entity }">
|
||||
<QCardActions>
|
||||
<QBtn
|
||||
size="md"
|
||||
|
@ -486,7 +447,7 @@ const createInvoiceInCorrection = async () => {
|
|||
:options="siiTypeInvoiceOuts"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
|
@ -496,7 +457,7 @@ const createInvoiceInCorrection = async () => {
|
|||
:options="cplusRectificationTypes"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
<VnSelect
|
||||
:label="`${useCapitalize(t('globals.reason'))}*`"
|
||||
|
@ -504,7 +465,7 @@ const createInvoiceInCorrection = async () => {
|
|||
:options="invoiceCorrectionTypes"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
:rules="[requiredFieldRule]"
|
||||
:required="true"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
@ -544,11 +505,18 @@ const createInvoiceInCorrection = async () => {
|
|||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries
|
||||
isLinked: The entry {bookEntry} has been linked to Sage. Please contact administration for further information
|
||||
assertAction: Are you sure you want to {action} this invoice?
|
||||
es:
|
||||
book: asentar
|
||||
unbook: desasentar
|
||||
delete: eliminar
|
||||
clone: clonar
|
||||
To book: Contabilizar
|
||||
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||
To unbook: Descontabilizar
|
||||
Delete invoice: Eliminar factura
|
||||
Are you sure you want to delete this invoice?: Estas seguro de querer eliminar esta factura?
|
||||
Invoice deleted: Factura eliminada
|
||||
Clone invoice: Clonar factura
|
||||
Invoice cloned: Factura clonada
|
||||
|
@ -560,4 +528,7 @@ es:
|
|||
Rectificative invoice: Factura rectificativa
|
||||
Original invoice: Factura origen
|
||||
Entry: entrada
|
||||
isNotLinked: Se ha eliminado el asiento nº {bookEntry} con {accountingEntries} apuntes
|
||||
isLinked: El asiento {bookEntry} fue enlazado a Sage, por favor contacta con administración
|
||||
assertAction: Estas seguro de querer {action} esta factura?
|
||||
</i18n>
|
||||
|
|
|
@ -9,24 +9,21 @@ import CrudModel from 'src/components/CrudModel.vue';
|
|||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import { toCurrency } from 'src/filters';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const arrayData = useArrayData('InvoiceIn');
|
||||
const arrayData = useArrayData(route.meta.moduleName);
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
|
||||
const rowsSelected = ref([]);
|
||||
const banks = ref([]);
|
||||
const invoiceInFormRef = ref();
|
||||
const invoiceId = route.params.id;
|
||||
const invoiceId = +route.params.id;
|
||||
|
||||
const placeholder = 'yyyy/mm/dd';
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
invoiceInFk: invoiceId,
|
||||
},
|
||||
};
|
||||
const filter = { where: { invoiceInFk: invoiceId } };
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
|
@ -73,6 +70,7 @@ async function insert() {
|
|||
await axios.post('/InvoiceInDueDays/new', { id: +invoiceId });
|
||||
await invoiceInFormRef.value.reload();
|
||||
}
|
||||
const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount, 0);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -184,6 +182,19 @@ async function insert() {
|
|||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd />
|
||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
Divisa no es seleccionable? Quiero decir estña el campo pero sin acción Divisa no es seleccionable? Quiero decir estña el campo pero sin acción
jorgep
commented
Si la divisa es euros no se puede editar ese campo Si la divisa es euros no se puede editar ese campo
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
jorgep marked this conversation as resolved
jgallego
commented
http://localhost:9000/#/invoice-in/4/due-day al entrar en esta seccion me dice Access Denied en ingles con developer, cuando en realidad esta seccion la deben de poder usar el rol administrative. http://localhost:9000/#/invoice-in/4/due-day al entrar en esta seccion me dice Access Denied en ingles con developer, cuando en realidad esta seccion la deben de poder usar el rol administrative.
jorgep
commented
A mi no me da ningún fallo . Compruebalo de nuevo y me dices. A mi no me da ningún fallo . Compruebalo de nuevo y me dices.
jsegarra
commented
No me da fallo. No me da fallo.
Pregunta: la fecha y el placeholder está como yyyy/mm/dd, es correcto?
jorgep
commented
@jsegarra Ahora se usa VnInputDate. @jsegarra Ahora se usa VnInputDate.
|
||||
{{
|
||||
toCurrency(getTotalAmount(rows), invoiceIn.currency.code)
|
||||
}}
|
||||
</QTd>
|
||||
<QTd />
|
||||
</QTr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||
<QCard>
|
||||
|
@ -294,7 +305,11 @@ async function insert() {
|
|||
<QBtn color="primary" icon="add" size="lg" round @click="insert" />
|
||||
</QPageSticky>
|
||||
</template>
|
||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
Que hace la función de insert? Que hace la función de insert?
Restaurar la tabla? Eso ya tiene un botón
jorgep
commented
Llama al back InvoiceInDueDays/new (llama a invoiceIn_calculate) y recarga el formulario... Llama al back InvoiceInDueDays/new (llama a invoiceIn_calculate) y recarga el formulario...
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
background-color: var(--vn-light-gray);
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
es:
|
||||
Date: Fecha
|
||||
|
|
|
@ -6,26 +6,21 @@ import { toCurrency } from 'src/filters';
|
|||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const route = useRoute();
|
||||
const currency = computed(
|
||||
() => useArrayData(route.meta.moduleName).store.data?.currency?.code
|
||||
);
|
||||
const invoceInIntrastat = ref([]);
|
||||
const amountTotal = computed(() => getTotal('amount'));
|
||||
const netTotal = computed(() => getTotal('net'));
|
||||
const stemsTotal = computed(() => getTotal('stems'));
|
||||
const rowsSelected = ref([]);
|
||||
const countries = ref([]);
|
||||
const intrastats = ref([]);
|
||||
const invoiceInFormRef = ref();
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
invoiceInFk: route.params.id,
|
||||
},
|
||||
};
|
||||
|
||||
const invoiceInId = computed(() => +route.params.id);
|
||||
const filter = { where: { invoiceInFk: invoiceInId.value } };
|
||||
const columns = computed(() => [
|
||||
{
|
||||
name: 'code',
|
||||
|
@ -77,13 +72,8 @@ const columns = computed(() => [
|
|||
},
|
||||
]);
|
||||
jorgep
commented
Si se escribe la coma, te devuelve NaN Si se escribe la coma, te devuelve NaN
|
||||
|
||||
function getTotal(type) {
|
||||
if (!invoceInIntrastat.value.length) return 0.0;
|
||||
return invoceInIntrastat.value.reduce(
|
||||
(total, intrastat) => total + intrastat[type],
|
||||
0.0
|
||||
);
|
||||
}
|
||||
const getTotal = (data, key) =>
|
||||
data.reduce((acc, cur) => acc + +String(cur[key]).replace(',', '.'), 0);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -99,30 +89,12 @@ function getTotal(type) {
|
|||
@on-fetch="(data) => (intrastats = data)"
|
||||
/>
|
||||
<div class="invoiceIn-intrastat">
|
||||
<QCard v-if="invoceInIntrastat.length" class="full-width q-mb-md q-pa-sm">
|
||||
<QItem class="justify-end">
|
||||
<div>
|
||||
<QItemLabel>
|
||||
<VnLv
|
||||
:label="t('Total amount')"
|
||||
:value="toCurrency(amountTotal)"
|
||||
/>
|
||||
</QItemLabel>
|
||||
<QItemLabel>
|
||||
<VnLv :label="t('Total net')" :value="netTotal" />
|
||||
</QItemLabel>
|
||||
<QItemLabel>
|
||||
<VnLv :label="t('Total stems')" :value="stemsTotal" />
|
||||
</QItemLabel>
|
||||
</div>
|
||||
</QItem>
|
||||
</QCard>
|
||||
<CrudModel
|
||||
ref="invoiceInFormRef"
|
||||
jorgep
commented
espera a que se carguen los datos de arrayData, ya que hace falta saber el currency espera a que se carguen los datos de arrayData, ya que hace falta saber el currency
|
||||
data-key="InvoiceInIntrastats"
|
||||
url="InvoiceInIntrastats"
|
||||
auto-load
|
||||
:data-required="{ invoiceInFk: route.params.id }"
|
||||
:auto-load="!currency"
|
||||
:data-required="{ invoiceInFk: invoiceInId }"
|
||||
:filter="filter"
|
||||
v-model:selected="rowsSelected"
|
||||
@on-fetch="(data) => (invoceInIntrastat = data)"
|
||||
|
@ -172,6 +144,22 @@ function getTotal(type) {
|
|||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(getTotal(rows, 'amount'), currency) }}
|
||||
</QTd>
|
||||
<QTd>
|
||||
{{ getTotal(rows, 'net') }}
|
||||
</QTd>
|
||||
<QTd>
|
||||
{{ getTotal(rows, 'stems') }}
|
||||
</QTd>
|
||||
<QTd />
|
||||
</QTr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||
<QCard>
|
||||
|
|
|
@ -3,32 +3,24 @@ import { onMounted, ref, computed } from 'vue';
|
|||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { toCurrency, toDate } from 'src/filters';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { getUrl } from 'src/composables/getUrl';
|
||||
import CardSummary from 'components/ui/CardSummary.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import InvoiceIntoBook from '../InvoiceInToBook.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
|
||||
onMounted(async () => {
|
||||
salixUrl.value = await getUrl('');
|
||||
invoiceInUrl.value = salixUrl.value + `invoiceIn/${entityId.value}/`;
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const props = defineProps({ id: { type: [Number, String], default: 0 } });
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const salixUrl = ref();
|
||||
const entityId = computed(() => props.id || +route.params.id);
|
||||
const invoiceIn = computed(() => useArrayData(route.meta.moduleName).store.data);
|
||||
const currency = computed(() => invoiceIn.value?.currency?.code);
|
||||
const invoiceInUrl = ref();
|
||||
const amountsNotMatch = ref(null);
|
||||
const intrastatTotals = ref({});
|
||||
const intrastatTotals = ref({ amount: 0, net: 0, stems: 0 });
|
||||
|
||||
const vatColumns = ref([
|
||||
{
|
||||
|
@ -42,14 +34,16 @@ const vatColumns = ref([
|
|||
name: 'landed',
|
||||
label: 'invoiceIn.summary.taxableBase',
|
||||
field: (row) => row.taxableBase,
|
||||
format: (value) => toCurrency(value),
|
||||
format: (value) => toCurrency(value, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'vat',
|
||||
label: 'invoiceIn.summary.sageVat',
|
||||
field: (row) => row.taxTypeSage?.vat,
|
||||
field: (row) => {
|
||||
if (row.taxTypeSage) return `#${row.taxTypeSage.id} : ${row.taxTypeSage.vat}`;
|
||||
},
|
||||
format: (value) => value,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
|
@ -57,7 +51,10 @@ const vatColumns = ref([
|
|||
{
|
||||
name: 'transaction',
|
||||
label: 'invoiceIn.summary.sageTransaction',
|
||||
field: (row) => row.transactionTypeSage?.transaction,
|
||||
field: (row) => {
|
||||
if (row.transactionTypeSage)
|
||||
return `#${row.transactionTypeSage.id} : ${row.transactionTypeSage?.transaction}`;
|
||||
},
|
||||
format: (value) => value,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
|
@ -66,9 +63,9 @@ const vatColumns = ref([
|
|||
name: 'rate',
|
||||
label: 'invoiceIn.summary.rate',
|
||||
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
|
||||
format: (value) => toCurrency(value),
|
||||
format: (value) => toCurrency(value, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'currency',
|
||||
|
@ -99,7 +96,7 @@ const dueDayColumns = ref([
|
|||
name: 'amount',
|
||||
label: 'invoiceIn.summary.amount',
|
||||
field: (row) => row.amount,
|
||||
format: (value) => toCurrency(value),
|
||||
format: (value) => toCurrency(value, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -122,13 +119,15 @@ const intrastatColumns = ref([
|
|||
},
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
style: 'width: 10px',
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
label: 'invoiceIn.summary.amount',
|
||||
field: (row) => toCurrency(row.amount),
|
||||
field: (row) => toCurrency(row.amount, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
style: 'width: 10px',
|
||||
},
|
||||
{
|
||||
name: 'net',
|
||||
|
@ -155,58 +154,55 @@ const intrastatColumns = ref([
|
|||
},
|
||||
]);
|
||||
|
||||
function getAmountNotMatch(totals) {
|
||||
return (
|
||||
onMounted(async () => {
|
||||
invoiceInUrl.value = `${await getUrl('')}invoiceIn/${entityId.value}/`;
|
||||
});
|
||||
|
||||
const init = (data) => {
|
||||
if (!data) return;
|
||||
|
||||
const { totals, invoiceInIntrastat } = data;
|
||||
amountsNotMatch.value =
|
||||
totals.totalDueDay != totals.totalTaxableBase &&
|
||||
totals.totalDueDay != totals.totalVat
|
||||
);
|
||||
}
|
||||
totals.totalDueDay != totals.totalVat;
|
||||
|
||||
function getIntrastatTotals(intrastat) {
|
||||
const totals = {
|
||||
amount: intrastat.reduce((acc, cur) => acc + cur.amount, 0),
|
||||
net: intrastat.reduce((acc, cur) => acc + cur.net, 0),
|
||||
stems: intrastat.reduce((acc, cur) => acc + cur.stems, 0),
|
||||
};
|
||||
invoiceInIntrastat.forEach((val) => {
|
||||
intrastatTotals.value.amount += val.amount;
|
||||
intrastatTotals.value.net += val.net;
|
||||
intrastatTotals.value.stems += val.stems;
|
||||
});
|
||||
};
|
||||
|
||||
return totals;
|
||||
}
|
||||
const taxRate = (taxableBase = 0, rate = 0) => (rate / 100) * taxableBase;
|
||||
|
||||
function getTaxTotal(tax) {
|
||||
return tax.reduce(
|
||||
(acc, cur) => acc + taxRate(cur.taxableBase, cur.taxTypeSage?.rate),
|
||||
0
|
||||
);
|
||||
}
|
||||
const getTotalTax = (tax) =>
|
||||
tax.reduce((acc, cur) => acc + taxRate(cur.taxableBase, cur.taxTypeSage?.rate), 0);
|
||||
|
||||
function setData(entity) {
|
||||
if (!entity) return false;
|
||||
|
||||
amountsNotMatch.value = getAmountNotMatch(entity.totals);
|
||||
|
||||
if (entity.invoiceInIntrastat.length)
|
||||
intrastatTotals.value = { ...getIntrastatTotals(entity.invoiceInIntrastat) };
|
||||
}
|
||||
|
||||
function taxRate(taxableBase = 0, rate = 0) {
|
||||
return (rate / 100) * taxableBase;
|
||||
}
|
||||
|
||||
function getLink(param) {
|
||||
return `#/invoice-in/${entityId.value}/${param}`;
|
||||
}
|
||||
const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardSummary
|
||||
data-key="InvoiceInSummary"
|
||||
:url="`InvoiceIns/${entityId}/summary`"
|
||||
@on-fetch="(data) => setData(data)"
|
||||
@on-fetch="(data) => init(data)"
|
||||
>
|
||||
<template #header="{ entity: invoiceIn }">
|
||||
<div>{{ invoiceIn.id }} - {{ invoiceIn.supplier?.name }}</div>
|
||||
<template #header="{ entity }">
|
||||
<div>{{ entity.id }} - {{ entity.supplier?.name }}</div>
|
||||
</template>
|
||||
<template #body="{ entity: invoiceIn }">
|
||||
<template #header-right v-if="!invoiceIn?.isBooked">
|
||||
<InvoiceIntoBook>
|
||||
<template #content="{ book }">
|
||||
<QBtn
|
||||
:label="t('To book')"
|
||||
color="orange-11"
|
||||
text-color="black"
|
||||
@click="book(entityId)"
|
||||
/>
|
||||
</template>
|
||||
</InvoiceIntoBook>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<!--Basic Data-->
|
||||
<QCard class="vn-one">
|
||||
<QCardSection class="q-pa-none">
|
||||
|
@ -217,19 +213,26 @@ function getLink(param) {
|
|||
</QCardSection>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.supplier')"
|
||||
:value="invoiceIn.supplier?.name"
|
||||
/>
|
||||
:value="entity.supplier?.name"
|
||||
>
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ entity.supplier?.name }}
|
||||
<SupplierDescriptorProxy :id="entity.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.supplierRef')"
|
||||
:value="invoiceIn.supplierRef"
|
||||
:value="entity.supplierRef"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.currency')"
|
||||
:value="invoiceIn.currency?.code"
|
||||
:value="entity.currency?.code"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.docNumber')"
|
||||
:value="`${invoiceIn.serial}/${invoiceIn.serialNumber}`"
|
||||
:value="`${entity.serial}/${entity.serialNumber}`"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
|
@ -242,19 +245,19 @@ function getLink(param) {
|
|||
<VnLv
|
||||
:ellipsis-value="false"
|
||||
:label="t('invoiceIn.summary.issued')"
|
||||
:value="toDate(invoiceIn.issued)"
|
||||
:value="toDate(entity.issued)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.operated')"
|
||||
:value="toDate(invoiceIn.operated)"
|
||||
:value="toDate(entity.operated)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.bookEntried')"
|
||||
:value="toDate(invoiceIn.bookEntried)"
|
||||
:value="toDate(entity.bookEntried)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.bookedDate')"
|
||||
:value="toDate(invoiceIn.booked)"
|
||||
:value="toDate(entity.booked)"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
|
@ -266,20 +269,19 @@ function getLink(param) {
|
|||
</QCardSection>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.sage')"
|
||||
:value="invoiceIn.sageWithholding?.withholding"
|
||||
:value="entity.sageWithholding?.withholding"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.vat')"
|
||||
:value="invoiceIn.expenseDeductible?.name"
|
||||
:value="entity.expenseDeductible?.name"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.company')"
|
||||
:value="invoiceIn.company?.code"
|
||||
:value="entity.company?.code"
|
||||
/>
|
||||
<QCheckbox
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.booked')"
|
||||
v-model="invoiceIn.isBooked"
|
||||
:disable="true"
|
||||
:value="invoiceIn?.isBooked"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
|
@ -290,58 +292,62 @@ function getLink(param) {
|
|||
/>
|
||||
</QCardSection>
|
||||
<QCardSection class="q-pa-none">
|
||||
<div class="bordered q-px-sm q-mx-auto">
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.taxableBase')"
|
||||
:value="toCurrency(invoiceIn.totals.totalTaxableBase)"
|
||||
/>
|
||||
<VnLv
|
||||
label="Total"
|
||||
:value="toCurrency(invoiceIn.totals.totalVat)"
|
||||
/>
|
||||
<VnLv :label="t('invoiceIn.summary.dueTotal')">
|
||||
<template #value>
|
||||
<QChip
|
||||
dense
|
||||
class="q-pa-xs"
|
||||
:color="amountsNotMatch ? 'negative' : 'transparent'"
|
||||
:title="
|
||||
amountsNotMatch
|
||||
? t('invoiceIn.summary.noMatch')
|
||||
: t('invoiceIn.summary.dueTotal')
|
||||
"
|
||||
>
|
||||
{{ toCurrency(invoiceIn.totals.totalDueDay) }}
|
||||
</QChip>
|
||||
</template>
|
||||
</VnLv>
|
||||
</div>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.taxableBase')"
|
||||
:value="toCurrency(entity.totals.totalTaxableBase, currency)"
|
||||
/>
|
||||
<VnLv
|
||||
label="Total"
|
||||
:value="toCurrency(entity.totals.totalVat, currency)"
|
||||
/>
|
||||
<VnLv :label="t('invoiceIn.summary.dueTotal')">
|
||||
<template #value>
|
||||
<QChip
|
||||
dense
|
||||
class="q-pa-xs"
|
||||
:color="amountsNotMatch ? 'negative' : 'transparent'"
|
||||
:title="
|
||||
amountsNotMatch
|
||||
? t('invoiceIn.summary.noMatch')
|
||||
: t('invoiceIn.summary.dueTotal')
|
||||
"
|
||||
>
|
||||
jorgep marked this conversation as resolved
Outdated
jgallego
commented
lo pongo aqui pero no es esta linea es decir, añadir la clave primaria lo pongo aqui pero no es esta linea
en la seccion iva del summary en los campos sage vat y sage transaccion quieren en la columna sage vat que aparezca:
8 : H.P. IVA 21% CEE
20 : Adquisic.intracomunitarias de bienes y serv.corr.
es decir, añadir la clave primaria
|
||||
{{ toCurrency(entity.totals.totalDueDay, currency) }}
|
||||
</QChip>
|
||||
</template>
|
||||
</VnLv>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
<!--Vat-->
|
||||
<QCard v-if="invoiceIn.invoiceInTax.length">
|
||||
<QCard v-if="entity.invoiceInTax.length" class="vat">
|
||||
<VnTitle :url="getLink('vat')" :text="t('invoiceIn.card.vat')" />
|
||||
<QTable
|
||||
:columns="vatColumns"
|
||||
:rows="invoiceIn.invoiceInTax"
|
||||
:rows="entity.invoiceInTax"
|
||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
Warning en la 338, 366 y 394 Warning en la 338, 366 y 394
|
||||
flat
|
||||
hide-pagination
|
||||
>
|
||||
<template #header="props">
|
||||
<QTr :props="props" class="bg">
|
||||
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<template #header="vatProps">
|
||||
<QTr :props="vatProps" class="bg">
|
||||
<QTh
|
||||
v-for="col in vatProps.cols"
|
||||
:key="col.name"
|
||||
:props="vatProps"
|
||||
>
|
||||
{{ t(col.label) }}
|
||||
</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
<QTd>{{ toCurrency(invoiceIn.totals.totalTaxableBase) }}</QTd>
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd>{{
|
||||
toCurrency(getTaxTotal(invoiceIn.invoiceInTax))
|
||||
toCurrency(entity.totals.totalTaxableBase, currency)
|
||||
}}</QTd>
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd class="text-center">{{
|
||||
toCurrency(getTotalTax(entity.invoiceInTax, currency))
|
||||
}}</QTd>
|
||||
<QTd></QTd>
|
||||
</QTr>
|
||||
|
@ -349,17 +355,17 @@ function getLink(param) {
|
|||
</QTable>
|
||||
</QCard>
|
||||
<!--Due Day-->
|
||||
<QCard v-if="invoiceIn.invoiceInDueDay.length">
|
||||
<QCard v-if="entity.invoiceInDueDay.length" class="due-day">
|
||||
<VnTitle :url="getLink('due-day')" :text="t('invoiceIn.card.dueDay')" />
|
||||
<QTable
|
||||
class="full-width"
|
||||
:columns="dueDayColumns"
|
||||
:rows="invoiceIn.invoiceInDueDay"
|
||||
flat
|
||||
>
|
||||
<template #header="props">
|
||||
<QTr :props="props" class="bg">
|
||||
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<QTable :columns="dueDayColumns" :rows="entity.invoiceInDueDay" flat>
|
||||
<template #header="dueDayProps">
|
||||
<QTr :props="dueDayProps" class="bg">
|
||||
<QTh
|
||||
table-header-style="max-width:50%"
|
||||
v-for="col in dueDayProps.cols"
|
||||
:key="col.name"
|
||||
:props="dueDayProps"
|
||||
>
|
||||
{{ t(col.label) }}
|
||||
</QTh>
|
||||
</QTr>
|
||||
|
@ -368,26 +374,32 @@ function getLink(param) {
|
|||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd>{{ toCurrency(invoiceIn.totals.totalDueDay) }}</QTd>
|
||||
<QTd>
|
||||
{{ toCurrency(entity.totals.totalDueDay, currency) }}
|
||||
</QTd>
|
||||
<QTd></QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QCard>
|
||||
<!--Intrastat-->
|
||||
<QCard v-if="invoiceIn.invoiceInIntrastat.length">
|
||||
<QCard v-if="entity.invoiceInIntrastat.length">
|
||||
<VnTitle
|
||||
:url="getLink('intrastat')"
|
||||
:text="t('invoiceIn.card.intrastat')"
|
||||
/>
|
||||
<QTable
|
||||
:columns="intrastatColumns"
|
||||
:rows="invoiceIn.invoiceInIntrastat"
|
||||
:rows="entity.invoiceInIntrastat"
|
||||
flat
|
||||
>
|
||||
<template #header="props">
|
||||
<QTr :props="props" class="bg">
|
||||
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<template #header="intrastatProps">
|
||||
<QTr :props="intrastatProps" class="bg">
|
||||
<QTh
|
||||
v-for="col in intrastatProps.cols"
|
||||
:key="col.name"
|
||||
:props="intrastatProps"
|
||||
>
|
||||
{{ t(col.label) }}
|
||||
</QTh>
|
||||
</QTr>
|
||||
|
@ -395,7 +407,7 @@ function getLink(param) {
|
|||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
<QTd>{{ toCurrency(intrastatTotals.amount) }}</QTd>
|
||||
<QTd>{{ toCurrency(intrastatTotals.amount, currency) }}</QTd>
|
||||
<QTd>{{ intrastatTotals.net }}</QTd>
|
||||
<QTd>{{ intrastatTotals.stems }}</QTd>
|
||||
<QTd></QTd>
|
||||
|
@ -410,13 +422,28 @@ function getLink(param) {
|
|||
.bg {
|
||||
background-color: var(--vn-accent-color);
|
||||
}
|
||||
.bordered {
|
||||
border: 1px solid var(--vn-text-color);
|
||||
max-width: 18em;
|
||||
@media (min-width: $breakpoint-md) {
|
||||
.summaryBody {
|
||||
.vat {
|
||||
flex: 65%;
|
||||
}
|
||||
|
||||
.due-day {
|
||||
flex: 30%;
|
||||
}
|
||||
.vat,
|
||||
.due-day {
|
||||
.q-table th {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
es:
|
||||
Search invoice: Buscar factura recibida
|
||||
You can search by invoice reference: Puedes buscar por referencia de la factura
|
||||
Totals: Totales
|
||||
To book: Contabilizar
|
||||
</i18n>
|
||||
|
|
|
@ -11,13 +11,14 @@ import VnSelect from 'src/components/common/VnSelect.vue';
|
|||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRoute();
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const arrayData = useArrayData('InvoiceIn');
|
||||
const arrayData = useArrayData(router.meta.moduleName);
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
|
||||
const invoiceId = +router.params.id;
|
||||
const currency = computed(() => invoiceIn.value?.currency?.code);
|
||||
const expenses = ref([]);
|
||||
const sageTaxTypes = ref([]);
|
||||
const sageTransactionTypes = ref([]);
|
||||
|
@ -55,7 +56,7 @@ const columns = computed(() => [
|
|||
{
|
||||
name: 'taxablebase',
|
||||
label: t('Taxable base'),
|
||||
field: (row) => toCurrency(row.taxableBase),
|
||||
field: (row) => toCurrency(row.taxableBase, currency.value),
|
||||
model: 'taxableBase',
|
||||
sortable: true,
|
||||
tabIndex: 2,
|
||||
|
@ -68,7 +69,7 @@ const columns = computed(() => [
|
|||
options: sageTaxTypes.value,
|
||||
model: 'taxTypeSageFk',
|
||||
optionValue: 'id',
|
||||
optionLabel: 'vat',
|
||||
optionLabel: 'id',
|
||||
sortable: true,
|
||||
tabindex: 3,
|
||||
align: 'left',
|
||||
|
@ -80,7 +81,7 @@ const columns = computed(() => [
|
|||
options: sageTransactionTypes.value,
|
||||
model: 'transactionTypeSageFk',
|
||||
optionValue: 'id',
|
||||
optionLabel: 'transaction',
|
||||
optionLabel: 'id',
|
||||
sortable: true,
|
||||
tabIndex: 4,
|
||||
align: 'left',
|
||||
|
@ -90,7 +91,7 @@ const columns = computed(() => [
|
|||
label: t('Rate'),
|
||||
sortable: true,
|
||||
tabIndex: 5,
|
||||
field: (row) => toCurrency(taxRate(row, row.taxTypeSageFk)),
|
||||
field: (row) => toCurrency(taxRate(row, row.taxTypeSageFk), currency.value),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
|
@ -114,7 +115,7 @@ const filter = {
|
|||
'transactionTypeSageFk',
|
||||
],
|
||||
where: {
|
||||
invoiceInFk: route.params.id,
|
||||
invoiceInFk: invoiceId,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -161,6 +162,9 @@ async function addExpense() {
|
|||
});
|
||||
}
|
||||
}
|
||||
const getTotalTaxableBase = (rows) =>
|
||||
rows.reduce((acc, { taxableBase }) => acc + +taxableBase, 0);
|
||||
const getTotalRate = (rows) => rows.reduce((acc, cur) => acc + +taxRate(cur), 0);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -181,9 +185,10 @@ async function addExpense() {
|
|||
data-key="InvoiceInTaxes"
|
||||
url="InvoiceInTaxes"
|
||||
:filter="filter"
|
||||
:data-required="{ invoiceInFk: route.params.id }"
|
||||
:data-required="{ invoiceInFk: invoiceId }"
|
||||
auto-load
|
||||
v-model:selected="rowsSelected"
|
||||
:go-to="`/invoice-in/${invoiceId}/due-day`"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QTable
|
||||
|
@ -191,7 +196,7 @@ async function addExpense() {
|
|||
selection="multiple"
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
row-key="$index"
|
||||
row-key="$index"
|
||||
:grid="$q.screen.lt.sm"
|
||||
>
|
||||
<template #body-cell-expense="{ row, col }">
|
||||
|
@ -303,6 +308,19 @@ async function addExpense() {
|
|||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(getTotalTaxableBase(rows), currency) }}
|
||||
</QTd>
|
||||
<QTd />
|
||||
jorgep marked this conversation as resolved
jgallego
commented
En esta seccion en los desplegables Sage iva, transactionTypeSageFk no quieren ver la descripcion solo la clave primaria de la tabla. si despliegan veran el valor En esta seccion en los desplegables Sage iva, transactionTypeSageFk no quieren ver la descripcion solo la clave primaria de la tabla. si despliegan veran el valor
|
||||
<QTd />
|
||||
<QTd> {{ toCurrency(getTotalRate(rows), currency) }}</QTd>
|
||||
<QTd />
|
||||
</QTr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||
<QCard bordered flat class="q-my-xs">
|
||||
|
@ -391,7 +409,7 @@ async function addExpense() {
|
|||
</VnSelect>
|
||||
</QItem>
|
||||
<QItem>
|
||||
{{ toCurrency(taxRate(props.row)) }}
|
||||
{{ toCurrency(taxRate(props.row), currency) }}
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
|
@ -458,6 +476,10 @@ async function addExpense() {
|
|||
</QPageSticky>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
background-color: var(--vn-light-gray);
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-xs) {
|
||||
.q-dialog {
|
||||
.q-card {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
const state = useState();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const stateStore = useStateStore();
|
||||
|
||||
const user = state.getUser();
|
||||
const newInvoiceIn = reactive({
|
||||
supplierFk: +route.query?.supplierFk || null,
|
||||
supplierRef: null,
|
||||
companyFk: user.value.companyFk || null,
|
||||
issued: Date.vnNew(),
|
||||
});
|
||||
const suppliersOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
|
||||
const redirectToInvoiceInBasicData = ({ id }) => {
|
||||
router.push({ name: 'InvoiceInBasicData', params: { id } });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
order="nickname"
|
||||
@on-fetch="(data) => (suppliersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="companiesRef"
|
||||
url="Companies"
|
||||
:filter="{ fields: ['id', 'code'] }"
|
||||
order="code"
|
||||
@on-fetch="(data) => (companiesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
custom-route-redirect-name="InvoiceInSummary"
|
||||
data-key="InvoiceInSummary"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
<QPage>
|
||||
<VnSubToolbar />
|
||||
<FormModel
|
||||
url-create="InvoiceIns"
|
||||
model="InvoiceIn"
|
||||
:form-initial-data="newInvoiceIn"
|
||||
@on-data-saved="redirectToInvoiceInBasicData"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('Supplier')"
|
||||
v-model="data.supplierFk"
|
||||
:options="suppliersOptions"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
hide-selected
|
||||
:required="true"
|
||||
:rules="validate('entry.supplierFk')"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
#{{ scope.opt?.id }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnInput
|
||||
:label="t('invoiceIn.summary.supplierRef')"
|
||||
v-model="data.supplierRef"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('Company')"
|
||||
v-model="data.companyFk"
|
||||
:options="companiesOptions"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
map-options
|
||||
hide-selected
|
||||
:required="true"
|
||||
:rules="validate('invoiceIn.companyFk')"
|
||||
/>
|
||||
<VnInputDate
|
||||
:label="t('invoiceIn.summary.issued')"
|
||||
v-model="data.issued"
|
||||
/>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Supplier: Proveedor
|
||||
Travel: Envío
|
||||
Company: Empresa
|
||||
</i18n>
|
|
@ -4,33 +4,17 @@ import { useI18n } from 'vue-i18n';
|
|||
|
||||
import VnSelect from 'components/common/VnSelect.vue';
|
||||
jorgep
commented
Se han reordenado los campos en base a como están en Salix. Se han reordenado los campos en base a como están en Salix.
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
defineProps({ dataKey: { type: String, required: true } });
|
||||
const suppliers = ref([]);
|
||||
const suppliersRef = ref();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
ref="suppliersRef"
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
order="nickname"
|
||||
limit="30"
|
||||
@on-fetch="(data) => (suppliers = data)"
|
||||
auto-load
|
||||
/>
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<VnFilterPanel :data-key="dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
|
@ -38,22 +22,6 @@ const suppliersRef = ref();
|
|||
</div>
|
||||
</template>
|
||||
<template #body="{ params, searchFn }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.supplierFk')"
|
||||
v-model="params.supplierFk"
|
||||
:options="suppliers"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
@input-value="suppliersRef.fetch()"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
|
@ -68,21 +36,11 @@ const suppliersRef = ref();
|
|||
</VnInput>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('From')" v-model="params.from" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.serial')"
|
||||
v-model="params.serial"
|
||||
:label="t('params.fi')"
|
||||
v-model="params.fi"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
>
|
||||
|
@ -92,11 +50,61 @@ const suppliersRef = ref();
|
|||
</VnInput>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
jorgep
commented
Se corrige el filtrado de suppliers. Se corrige el filtrado de suppliers.
|
||||
v-model="params.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:label="t('params.supplierFk')"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
:options="suppliers"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.account')"
|
||||
v-model="params.account"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
>
|
||||
<template #prepend>
|
||||
<QIcon name="person" size="sm" />
|
||||
</template>
|
||||
</VnInput>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCurrency v-model="params.amount" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('From')" v-model="params.from" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('Issued')"
|
||||
v-model="params.issued"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-mb-md">
|
||||
<QItemSection>
|
||||
<QCheckbox
|
||||
|
@ -111,8 +119,8 @@ const suppliersRef = ref();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.fi')"
|
||||
v-model="params.fi"
|
||||
:label="t('params.serialNumber')"
|
||||
v-model="params.serialNumber"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
>
|
||||
|
@ -125,8 +133,8 @@ const suppliersRef = ref();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.serialNumber')"
|
||||
v-model="params.serialNumber"
|
||||
:label="t('params.serial')"
|
||||
v-model="params.serial"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
>
|
||||
|
@ -150,29 +158,6 @@ const suppliersRef = ref();
|
|||
</VnInput>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.account')"
|
||||
v-model="params.account"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
>
|
||||
<template #prepend>
|
||||
<QIcon name="person" size="sm" />
|
||||
</template>
|
||||
</VnInput>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('Issued')"
|
||||
v-model="params.issued"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QExpansionItem>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
|
|
|
@ -13,6 +13,7 @@ import InvoiceInFilter from './InvoiceInFilter.vue';
|
|||
import { getUrl } from 'src/composables/getUrl';
|
||||
import InvoiceInSummary from './Card/InvoiceInSummary.vue';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const router = useRouter();
|
||||
|
@ -85,7 +86,15 @@ function navigate(id) {
|
|||
<VnLv
|
||||
:label="t('invoiceIn.list.supplier')"
|
||||
:value="row.supplierName"
|
||||
/>
|
||||
@click.stop
|
||||
>
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ row.supplierName }}
|
||||
<SupplierDescriptorProxy :id="row.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.serialNumber')"
|
||||
:value="row.serialNumber"
|
||||
|
@ -137,13 +146,7 @@ function navigate(id) {
|
|||
</div>
|
||||
</QPage>
|
||||
<QPageSticky position="bottom-right" :offset="[20, 20]">
|
||||
<QBtn
|
||||
color="primary"
|
||||
icon="add"
|
||||
size="lg"
|
||||
round
|
||||
:href="`${url}invoice-in/create`"
|
||||
/>
|
||||
<QBtn color="primary" icon="add" size="lg" round :href="`/#/invoice-in/create`" />
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
const { notify, dialog } = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
defineExpose({ checkToBook });
|
||||
|
||||
const { store } = useArrayData(useRoute().meta.moduleName);
|
||||
|
||||
async function checkToBook(id) {
|
||||
let directBooking = true;
|
||||
|
||||
const { data: totals } = await axios.get(`InvoiceIns/${id}/getTotals`);
|
||||
const taxableBaseNotEqualDueDay = totals.totalDueDay != totals.totalTaxableBase;
|
||||
const vatNotEqualDueDay = totals.totalDueDay != totals.totalVat;
|
||||
|
||||
if (taxableBaseNotEqualDueDay && vatNotEqualDueDay) directBooking = false;
|
||||
|
||||
const { data: dueDaysCount } = await axios.get('InvoiceInDueDays/count', {
|
||||
where: {
|
||||
invoiceInFk: id,
|
||||
dueDated: { gte: Date.vnNew() },
|
||||
},
|
||||
});
|
||||
|
||||
if (dueDaysCount) directBooking = false;
|
||||
|
||||
if (directBooking) return toBook(id);
|
||||
|
||||
dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: { title: t('Are you sure you want to book this invoice?') },
|
||||
}).onOk(async () => await toBook(id));
|
||||
}
|
||||
|
||||
async function toBook(id) {
|
||||
let type = 'positive';
|
||||
let message = t('globals.dataSaved');
|
||||
|
||||
try {
|
||||
await axios.post(`InvoiceIns/${id}/toBook`);
|
||||
store.data.isBooked = true;
|
||||
} catch (e) {
|
||||
type = 'negative';
|
||||
message = t('It was not able to book the invoice');
|
||||
} finally {
|
||||
notify({ type, message });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<slot name="content" :book="checkToBook" />
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Are you sure you want to book this invoice?: ¿Estás seguro de querer asentar esta factura?
|
||||
It was not able to book the invoice: No se pudo contabilizar la factura
|
||||
</i18n>
|
|
@ -183,7 +183,7 @@ const getEntryQueryParams = (supplier) => {
|
|||
<QTooltip>{{ t('Go to client') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:href="`${url}invoice-in/create?supplierFk=${entity.id}`"
|
||||
:href="`#/invoice-in/create?supplierFk=${entity.id}`"
|
||||
jorgep marked this conversation as resolved
jgallego
commented
Esta en la ruta actual? Esta en la ruta actual?
jorgep
commented
Sí, antes redirigia a Salix. Sí, antes redirigia a Salix.
|
||||
size="md"
|
||||
icon="vn:invoice-in-create"
|
||||
color="primary"
|
||||
|
|
|
@ -27,7 +27,7 @@ const save = async (data) => {
|
|||
const lockerId = data.id ?? originaLockerId.value;
|
||||
const workerFk = lockerId == originaLockerId.value ? null : entityId.value;
|
||||
|
||||
await axios.patch(`Lockers/${lockerId}`, { workerFk });
|
||||
return axios.patch(`Lockers/${lockerId}`, { workerFk });
|
||||
};
|
||||
|
||||
const init = async (data) => {
|
||||
|
|
|
@ -37,6 +37,15 @@ export default {
|
|||
},
|
||||
component: () => import('src/pages/InvoiceIn/InvoiceInList.vue'),
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'InvoiceInCreare',
|
||||
meta: {
|
||||
title: 'invoiceInCreate',
|
||||
icon: 'create',
|
||||
},
|
||||
component: () => import('src/pages/InvoiceIn/InvoiceInCreate.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
describe('InvoiceInBasicData', () => {
|
||||
const formInputs = '.q-form > .q-card input';
|
||||
const firstFormSelect = '.q-card > .vn-row:nth-child(1) > .q-select';
|
||||
const appendBtns = '.q-form label button';
|
||||
const dialogAppendBtns = '.q-dialog label button';
|
||||
const documentBtns = '.q-form .q-field button';
|
||||
const dialogInputs = '.q-dialog input';
|
||||
const dialogActionBtns = '.q-card__actions button';
|
||||
|
||||
|
@ -15,8 +14,7 @@ describe('InvoiceInBasicData', () => {
|
|||
it('should edit the provideer and supplier ref', () => {
|
||||
cy.selectOption(firstFormSelect, 'Bros');
|
||||
cy.get('[title="Reset"]').click();
|
||||
cy.get(appendBtns).eq(0).click();
|
||||
cy.get(formInputs).eq(1).type(4739);
|
||||
cy.get(formInputs).eq(1).type('{selectall}4739');
|
||||
cy.saveCard();
|
||||
|
||||
cy.get(`${firstFormSelect} input`).invoke('val').should('eq', 'Plants nick');
|
||||
|
@ -27,22 +25,20 @@ describe('InvoiceInBasicData', () => {
|
|||
const firtsInput = 'Ticket:65';
|
||||
const secondInput = "I don't know what posting here!";
|
||||
|
||||
cy.get(appendBtns).eq(3).click();
|
||||
cy.get(dialogAppendBtns).eq(0).click();
|
||||
cy.get(dialogInputs).eq(0).type(firtsInput);
|
||||
cy.get(dialogAppendBtns).eq(1).click();
|
||||
cy.get('textarea').type(secondInput);
|
||||
cy.get(documentBtns).eq(1).click();
|
||||
cy.get(dialogInputs).eq(0).type(`{selectall}${firtsInput}`);
|
||||
cy.get('textarea').type(`{selectall}${secondInput}`);
|
||||
cy.get(dialogActionBtns).eq(1).click();
|
||||
|
||||
cy.get(appendBtns).eq(3).click();
|
||||
|
||||
cy.get(documentBtns).eq(1).click();
|
||||
cy.get(dialogInputs).eq(0).invoke('val').should('eq', firtsInput);
|
||||
cy.get('textarea').invoke('val').should('eq', secondInput);
|
||||
});
|
||||
|
||||
it('should throw an error creating a new dms if a file is not attached', () => {
|
||||
cy.get(appendBtns).eq(2).click();
|
||||
cy.get(appendBtns).eq(1).click();
|
||||
cy.get(formInputs).eq(5).click();
|
||||
cy.get(formInputs).eq(5).type('{selectall}{backspace}');
|
||||
cy.get(documentBtns).eq(0).click();
|
||||
cy.get(dialogActionBtns).eq(1).click();
|
||||
cy.get('.q-notification__message').should(
|
||||
'have.text',
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
describe('InvoiceInDescriptor', () => {
|
||||
const dialogBtns = '.q-card__actions button';
|
||||
const firstDescritorOpt = '.q-menu > .q-list > :nth-child(1) > .q-item__section';
|
||||
const isBookedField =
|
||||
'.q-card:nth-child(3) .vn-label-value:nth-child(5) > .value > span';
|
||||
|
||||
it('should booking and unbooking the invoice properly', () => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/invoice-in/1/summary?limit=10`);
|
||||
|
||||
cy.openLeftMenu();
|
||||
cy.openActionsDescriptor();
|
||||
cy.get(firstDescritorOpt).click();
|
||||
cy.get(dialogBtns).eq(1).click();
|
||||
cy.get('.fullscreen').first().click();
|
||||
cy.get(isBookedField).should('have.attr', 'title', 'true');
|
||||
|
||||
cy.openLeftMenu();
|
||||
cy.openActionsDescriptor();
|
||||
cy.get(firstDescritorOpt).click();
|
||||
cy.get(dialogBtns).eq(1).click();
|
||||
cy.get(isBookedField).should('have.attr', 'title', 'false');
|
||||
});
|
||||
});
|
|
@ -18,7 +18,7 @@ describe('InvoiceInVat', () => {
|
|||
cy.saveCard();
|
||||
cy.visit(`/#/invoice-in/1/vat`);
|
||||
|
||||
cy.getValue(firstLineVat).should('equal', 'H.P. IVA 21% CEE');
|
||||
cy.getValue(firstLineVat).should('equal', '8');
|
||||
jorgep
commented
Se está comprobando el valor del input, no el "label" Se está comprobando el valor del input, no el "label"
|
||||
});
|
||||
|
||||
it('should add a new row', () => {
|
||||
|
|
|
@ -9,15 +9,15 @@ describe('VnLog', () => {
|
|||
cy.visit(`/#/claim/${1}/log`);
|
||||
cy.openRightMenu();
|
||||
});
|
||||
|
||||
it('should filter by insert actions', () => {
|
||||
// Se tiene que cambiar el Accept-Language a 'en', ya hay una tarea para eso #7189.
|
||||
xit('should filter by insert actions', () => {
|
||||
cy.checkOption(':nth-child(7) > .q-checkbox');
|
||||
cy.get('.q-page').click();
|
||||
cy.validateContent(chips[0], 'Document');
|
||||
cy.validateContent(chips[1], 'Beginning');
|
||||
});
|
||||
|
||||
it('should filter by entity', () => {
|
||||
xit('should filter by entity', () => {
|
||||
cy.selectOption('.q-drawer--right .q-item > .q-select', 'Claim');
|
||||
cy.get('.q-page').click();
|
||||
cy.validateContent(chips[0], 'Claim');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('WorkerList', () => {
|
||||
describe('WorkerLocker', () => {
|
||||
const workerId = 1109;
|
||||
const lockerCode = '2F';
|
||||
const input = '.q-card input';
|
||||
|
|
|
@ -92,8 +92,13 @@ Cypress.Commands.add('checkOption', (selector) => {
|
|||
|
||||
// Global buttons
|
||||
Cypress.Commands.add('saveCard', () => {
|
||||
jorgep
commented
Se adapta para que te deje guardar en caso de haber un desplegable Se adapta para que te deje guardar en caso de haber un desplegable
|
||||
const dropdownArrow = '.q-btn-dropdown__arrow-container > .q-btn__content > .q-icon';
|
||||
cy.get('#st-actions').then(($el) => {
|
||||
if ($el.find(dropdownArrow).length) cy.get(dropdownArrow).click();
|
||||
});
|
||||
cy.get('[title="Save"]').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('resetCard', () => {
|
||||
cy.get('[title="Reset"]').click();
|
||||
});
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import { vi, describe, expect, it, beforeAll } from 'vitest';
|
||||
jorgep
commented
Ya se comprueba en e2e Ya se comprueba en e2e
|
||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||
import InvoiceInBasicData from 'src/pages/InvoiceIn/Card/InvoiceInBasicData.vue';
|
||||
|
||||
describe('InvoiceInBasicData', () => {
|
||||
let vm;
|
||||
|
||||
beforeAll(() => {
|
||||
vm = createWrapper(InvoiceInBasicData, {
|
||||
global: {
|
||||
stubs: [],
|
||||
mocks: {
|
||||
fetch: vi.fn(),
|
||||
},
|
||||
},
|
||||
}).vm;
|
||||
});
|
||||
|
||||
describe('upsert()', () => {
|
||||
it('should throw an error when data is empty', async () => {
|
||||
vi.spyOn(axios, 'post').mockResolvedValue({ data: [] });
|
||||
vi.spyOn(vm.quasar, 'notify');
|
||||
|
||||
await vm.upsert();
|
||||
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: `The company can't be empty`,
|
||||
type: 'negative',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -19,13 +19,13 @@ describe('InvoiceInIntrastat', () => {
|
|||
|
||||
describe('getTotal()', () => {
|
||||
it('should correctly handle the sum', () => {
|
||||
vm.invoceInIntrastat = [
|
||||
const invoceInIntrastat = [
|
||||
jorgep
commented
Ahora a la fn hay que pasarle el array. Así los valoraes se actualizan de forma reactiva. Ahora a la fn hay que pasarle el array. Así los valoraes se actualizan de forma reactiva.
|
||||
{ amount: 10, stems: 162 },
|
||||
{ amount: 20, stems: 21 },
|
||||
];
|
||||
|
||||
const totalAmount = vm.getTotal('amount');
|
||||
const totalStems = vm.getTotal('stems');
|
||||
const totalAmount = vm.getTotal(invoceInIntrastat, 'amount');
|
||||
const totalStems = vm.getTotal(invoceInIntrastat, 'stems');
|
||||
|
||||
expect(totalAmount).toBe(10 + 20);
|
||||
expect(totalStems).toBe(162 + 21);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { vi, describe, expect, it, beforeAll } from 'vitest';
|
||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||
import { createWrapper } from 'app/test/vitest/helper';
|
||||
import InvoiceInVat from 'src/pages/InvoiceIn/Card/InvoiceInVat.vue';
|
||||
|
||||
describe('InvoiceInVat', () => {
|
||||
|
@ -16,41 +16,6 @@ describe('InvoiceInVat', () => {
|
|||
}).vm;
|
||||
});
|
||||
|
||||
describe('addExpense()', () => {
|
||||
jorgep
commented
ya se comprueba en e2e ya se comprueba en e2e
|
||||
beforeAll(() => {
|
||||
vi.spyOn(axios, 'post').mockResolvedValue({ data: [] });
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
|
||||
vi.spyOn(vm.quasar, 'notify');
|
||||
});
|
||||
|
||||
it('should throw an error when the code property is undefined', async () => {
|
||||
await vm.addExpense();
|
||||
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: `The code can't be empty`,
|
||||
type: 'negative',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly handle expense addition', async () => {
|
||||
vm.newExpense = {
|
||||
code: 123,
|
||||
isWithheld: false,
|
||||
description: 'Descripción del gasto',
|
||||
};
|
||||
|
||||
await vm.addExpense();
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Data saved',
|
||||
type: 'positive',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('taxRate()', () => {
|
||||
it('should correctly compute the tax rate', () => {
|
||||
const invoiceInTax = { taxableBase: 100, taxTypeSageFk: 1 };
|
||||
|
|
|
@ -24,6 +24,7 @@ vi.mock('vue-router', () => ({
|
|||
params: {
|
||||
id: 1,
|
||||
},
|
||||
meta: { moduleName: 'mockName' },
|
||||
jorgep
commented
Lo pongo en ambos sitios ya de paso. Lo pongo en ambos sitios ya de paso.
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
@ -31,6 +32,7 @@ vi.mock('vue-router', () => ({
|
|||
matched: [],
|
||||
query: {},
|
||||
params: {},
|
||||
meta: { moduleName: 'mockName' },
|
||||
}),
|
||||
}));
|
||||
|
||||
|
|
url a la que redirigir al hacer click en 'save and continue'