#6942 improve invoiceIn #220
|
@ -96,8 +96,7 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (!hasChanges.value) next();
|
if (hasChanges.value) {
|
||||||
|
|
||||||
quasar.dialog({
|
quasar.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
@ -106,6 +105,7 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
promise: () => next(),
|
promise: () => next(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else next();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|||||||
|
|
|
@ -698,6 +698,7 @@ export default {
|
||||||
intrastat: 'Intrastat',
|
intrastat: 'Intrastat',
|
||||||
corrective: 'Corrective',
|
corrective: 'Corrective',
|
||||||
log: 'Logs',
|
log: 'Logs',
|
||||||
|
create: 'Create',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
ref: 'Reference',
|
ref: 'Reference',
|
||||||
|
|
|
@ -756,6 +756,7 @@ export default {
|
||||||
intrastat: 'Intrastat',
|
intrastat: 'Intrastat',
|
||||||
corrective: 'Rectificativa',
|
corrective: 'Rectificativa',
|
||||||
log: 'Registros de auditoría',
|
log: 'Registros de auditoría',
|
||||||
|
create: 'Crear',
|
||||||
jorgep marked this conversation as resolved
Outdated
jgallego
commented
~~Sage transación~~
Sage transacción
|
|||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
ref: 'Referencia',
|
ref: 'Referencia',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -10,9 +10,12 @@ import FormModel from 'components/FormModel.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import VnRow from '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 quasar = useQuasar();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const dms = ref({});
|
const dms = ref({});
|
||||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
Usar useRoute().meta.moduleName Usar useRoute().meta.moduleName
|
|||||||
|
@ -32,14 +35,11 @@ const warehouses = ref([]);
|
||||||
const warehousesRef = ref();
|
const warehousesRef = ref();
|
||||||
const allowTypesRef = ref();
|
const allowTypesRef = ref();
|
||||||
const allowedContentTypes = ref([]);
|
const allowedContentTypes = ref([]);
|
||||||
|
const sageWithholdings = ref([]);
|
||||||
const inputFileRef = ref();
|
const inputFileRef = ref();
|
||||||
const editDmsRef = ref();
|
const editDmsRef = ref();
|
||||||
const createDmsRef = ref();
|
const createDmsRef = ref();
|
||||||
|
|
||||||
const requiredFieldRule = (val) => val || t('globals.requiredField');
|
|
||||||
const dateMask = '####-##-##';
|
|
||||||
const fillMask = '_';
|
|
||||||
|
|
||||||
async function checkFileExists(dmsId) {
|
async function checkFileExists(dmsId) {
|
||||||
if (!dmsId) return;
|
if (!dmsId) return;
|
||||||
try {
|
try {
|
||||||
|
@ -175,11 +175,19 @@ async function upsert() {
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FetchData url="Expenses" auto-load @on-fetch="(data) => (expenses = data)" />
|
<FetchData url="Expenses" auto-load @on-fetch="(data) => (expenses = data)" />
|
||||||
|
<FetchData
|
||||||
|
url="SageWithholdings"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (sageWithholdings = data)"
|
||||||
|
/>
|
||||||
<FormModel
|
<FormModel
|
||||||
v-if="invoiceIn"
|
v-if="invoiceIn"
|
||||||
:url="`InvoiceIns/${route.params.id}`"
|
:url="`InvoiceIns/${route.params.id}`"
|
||||||
model="invoiceIn"
|
model="invoiceIn"
|
||||||
:auto-load="true"
|
auto-load
|
||||||
|
@on-data-saved="
|
||||||
|
router.push({ name: 'InvoiceInVat', params: { id: route.params.id } })
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #form="{ data }">
|
<template #form="{ data }">
|
||||||
<VnRow>
|
<VnRow>
|
||||||
|
@ -203,7 +211,7 @@ async function upsert() {
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelectFilter>
|
||||||
<QInput
|
<VnInput
|
||||||
clearable
|
clearable
|
||||||
clear-icon="close"
|
clear-icon="close"
|
||||||
:label="t('Supplier ref')"
|
:label="t('Supplier ref')"
|
||||||
|
@ -211,60 +219,12 @@ async function upsert() {
|
||||||
/>
|
/>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<QInput
|
<VnInputDate :label="t('Expedition date')" v-model="data.issued" />
|
||||||
:label="t('Expedition date')"
|
<VnInputDate
|
||||||
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
|
|
||||||
:label="t('Operation date')"
|
:label="t('Operation date')"
|
||||||
v-model="data.operated"
|
v-model="data.operated"
|
||||||
:mask="dateMask"
|
|
||||||
:fill-mask="fillMask"
|
|
||||||
autofocus
|
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>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
|
@ -281,8 +241,7 @@ async function upsert() {
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelectFilter>
|
||||||
|
<VnInput
|
||||||
<QInput
|
|
||||||
:label="t('Document')"
|
:label="t('Document')"
|
||||||
v-model="data.dmsFk"
|
v-model="data.dmsFk"
|
||||||
clearable
|
clearable
|
||||||
|
@ -327,68 +286,11 @@ async function upsert() {
|
||||||
<QTooltip>{{ t('Create document') }}</QTooltip>
|
<QTooltip>{{ t('Create document') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</template>
|
</template>
|
||||||
</QInput>
|
</VnInput>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<QInput
|
<VnInputDate :label="t('Entry date')" v-model="data.bookEntried" />
|
||||||
:label="t('Entry date')"
|
<VnInputDate :label="t('Accounted date')" v-model="data.booked" />
|
||||||
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>
|
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
|
@ -409,10 +311,19 @@ async function upsert() {
|
||||||
/>
|
/>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('invoiceIn.summary.sage')"
|
||||||
|
v-model="data.withholdingSageFk"
|
||||||
|
:options="sageWithholdings"
|
||||||
|
option-value="id"
|
||||||
|
option-label="withholding"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
:label="t('invoiceIn.summary.booked')"
|
:label="t('invoiceIn.summary.booked')"
|
||||||
v-model="data.isBooked"
|
v-model="data.isBooked"
|
||||||
jgallego
commented
Cuidado porque aquí si intento descontabilizar desde el descriptor me dice que no puedo pero si lo hago desmarcando el checkbox sí. Cuidado porque aquí si intento descontabilizar desde el descriptor me dice que no puedo pero si lo hago desmarcando el checkbox sí.
Quita visualmente el check y que no se pueda modificar directamente
http://localhost:9000/#/invoice-in/2/basic-data
jsegarra
commented
Vaya, que mal, había probado el registro 1 y 4 y puedo contabilizar y descontabiliza, en bucle sin fin. Vaya, que mal, había probado el registro 1 y 4 y puedo contabilizar y descontabiliza, en bucle sin fin.
Pero si uso tu registro 2, me sale "El asiento fue enlazado a Sage, por favor contacta con administración"
jorgep
commented
Es correcto. > Vaya, que mal, había probado el registro 1 y 4 y puedo contabilizar y descontabiliza, en bucle sin fin.
> Pero si uso tu registro 2, me sale "El asiento fue enlazado a Sage, por favor contacta con administración"
Es correcto.
jorgep
commented
hecho. ¿Habría que modificar algún permiso o con quitarlo de basic-data es suficiente? @jgallego > Cuidado porque aquí si intento descontabilizar desde el descriptor me dice que no puedo pero si lo hago desmarcando el checkbox sí.
> Quita visualmente el check y que no se pueda modificar directamente
> http://localhost:9000/#/invoice-in/2/basic-data
hecho. ¿Habría que modificar algún permiso o con quitarlo de basic-data es suficiente? @jgallego
jgallego
commented
quitarlo no es suficiente, hay que quitar permisos, ese campo solo debe poder ser modificado via back quitarlo no es suficiente, hay que quitar permisos, ese campo solo debe poder ser modificado via back
|
|||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
</template>
|
</template>
|
||||||
</FormModel>
|
</FormModel>
|
||||||
|
@ -429,7 +340,7 @@ async function upsert() {
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-py-none">
|
<QCardSection class="q-py-none">
|
||||||
<QItem>
|
<QItem>
|
||||||
<QInput
|
<VnInput
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
:label="t('Reference')"
|
:label="t('Reference')"
|
||||||
v-model="dms.reference"
|
v-model="dms.reference"
|
||||||
|
@ -438,45 +349,45 @@ async function upsert() {
|
||||||
/>
|
/>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
:label="`${t('Company')}*`"
|
:label="t('Company')"
|
||||||
v-model="dms.companyId"
|
v-model="dms.companyId"
|
||||||
:options="companies"
|
:options="companies"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="code"
|
option-label="code"
|
||||||
:rules="[requiredFieldRule]"
|
:required="true"
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
:label="`${t('Warehouse')}*`"
|
:label="t('Warehouse')"
|
||||||
v-model="dms.warehouseId"
|
v-model="dms.warehouseId"
|
||||||
:options="warehouses"
|
:options="warehouses"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
:rules="[requiredFieldRule]"
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
:label="`${t('Type')}*`"
|
:label="t('Type')"
|
||||||
v-model="dms.dmsTypeId"
|
v-model="dms.dmsTypeId"
|
||||||
:options="dmsTypes"
|
:options="dmsTypes"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
:rules="[requiredFieldRule]"
|
:required="true"
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
<QInput
|
<VnInput
|
||||||
class="full-width q-pa-xs"
|
:label="t('Description')"
|
||||||
|
v-model="dms.description"
|
||||||
|
:required="true"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
|
class="full-width q-pa-xs"
|
||||||
size="lg"
|
size="lg"
|
||||||
autogrow
|
autogrow
|
||||||
:label="`${t('Description')}*`"
|
|
||||||
v-model="dms.description"
|
|
||||||
clearable
|
clearable
|
||||||
clear-icon="close"
|
clear-icon="close"
|
||||||
:rules="[(val) => val.length || t('Required field')]"
|
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
|
@ -540,7 +451,7 @@ async function upsert() {
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pb-none">
|
<QCardSection class="q-pb-none">
|
||||||
<QItem>
|
<QItem>
|
||||||
<QInput
|
<VnInput
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
:label="t('Reference')"
|
:label="t('Reference')"
|
||||||
v-model="dms.reference"
|
v-model="dms.reference"
|
||||||
|
@ -552,7 +463,7 @@ async function upsert() {
|
||||||
:options="companies"
|
:options="companies"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="code"
|
option-label="code"
|
||||||
:rules="[requiredFieldRule]"
|
:required="true"
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
|
@ -563,7 +474,7 @@ async function upsert() {
|
||||||
:options="warehouses"
|
:options="warehouses"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
:rules="[requiredFieldRule]"
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
|
@ -572,11 +483,11 @@ async function upsert() {
|
||||||
:options="dmsTypes"
|
:options="dmsTypes"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
:rules="[requiredFieldRule]"
|
:required="true"
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
<QInput
|
<VnInput
|
||||||
class="full-width q-pa-xs"
|
class="full-width q-pa-xs"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
|
|
@ -28,15 +28,10 @@ const filter = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{ relation: 'invoiceInDueDay' },
|
||||||
relation: 'invoiceInDueDay',
|
{ relation: 'company' },
|
||||||
},
|
{ relation: 'currency' },
|
||||||
{
|
{ relation: 'sageWithholding' },
|
||||||
relation: 'company',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relation: 'currency',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const arrayData = useArrayData('InvoiceIn', {
|
const arrayData = useArrayData('InvoiceIn', {
|
||||||
|
@ -47,7 +42,7 @@ const arrayData = useArrayData('InvoiceIn', {
|
||||||
onMounted(async () => await arrayData.fetch({ append: false }));
|
onMounted(async () => await arrayData.fetch({ append: false }));
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
async (newId, oldId) => {
|
async (newId) => {
|
||||||
if (newId) {
|
if (newId) {
|
||||||
arrayData.store.url = `InvoiceIns/${newId}`;
|
arrayData.store.url = `InvoiceIns/${newId}`;
|
||||||
await arrayData.fetch({ append: false });
|
await arrayData.fetch({ append: false });
|
||||||
|
|
|
@ -17,6 +17,7 @@ import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
import { useCapitalize } from 'src/composables/useCapitalize';
|
import { useCapitalize } from 'src/composables/useCapitalize';
|
||||||
|
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -408,10 +409,14 @@ const createInvoiceInCorrection = async () => {
|
||||||
<VnLv :label="t('invoiceIn.card.issued')" :value="toDate(entity.issued)" />
|
<VnLv :label="t('invoiceIn.card.issued')" :value="toDate(entity.issued)" />
|
||||||
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
|
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
|
||||||
<VnLv :label="t('invoiceIn.card.amount')" :value="toCurrency(totalAmount)" />
|
<VnLv :label="t('invoiceIn.card.amount')" :value="toCurrency(totalAmount)" />
|
||||||
<VnLv
|
<VnLv :label="t('invoiceIn.summary.supplier')">
|
||||||
:label="t('invoiceIn.summary.supplier')"
|
<template #value>
|
||||||
:value="entity.supplier?.nickname"
|
<span class="link">
|
||||||
/>
|
{{ invoiceIn?.supplier?.nickname }}
|
||||||
|
<SupplierDescriptorProxy :id="invoiceIn?.supplierFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ entity }">
|
<template #actions="{ entity }">
|
||||||
<QCardActions>
|
<QCardActions>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import CrudModel from 'src/components/CrudModel.vue';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||||
|
import { toCurrency } from 'src/filters';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -73,6 +74,7 @@ async function insert() {
|
||||||
await axios.post('/InvoiceInDueDays/new ', { id: +invoiceId });
|
await axios.post('/InvoiceInDueDays/new ', { id: +invoiceId });
|
||||||
await invoiceInFormRef.value.reload();
|
await invoiceInFormRef.value.reload();
|
||||||
}
|
}
|
||||||
|
const getTotalAmount = (rows) => rows.reduce((acc, cur) => acc + cur.amount, 0);
|
||||||
jorgep marked this conversation as resolved
Outdated
jsegarra
commented
Esta es mi propuesta para el error const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount, 0); Esta es mi propuesta para el error
const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount, 0);
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData url="Banks" auto-load limit="30" @on-fetch="(data) => (banks = data)" />
|
<FetchData url="Banks" auto-load limit="30" @on-fetch="(data) => (banks = data)" />
|
||||||
|
@ -180,6 +182,17 @@ async function insert() {
|
||||||
/>
|
/>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</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)) }}
|
||||||
|
</QTd>
|
||||||
|
<QTd />
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
<template #item="props">
|
<template #item="props">
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
<QCard>
|
<QCard>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { toCurrency, toDate } from 'src/filters';
|
||||||
import { getUrl } from 'src/composables/getUrl';
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
salixUrl.value = await getUrl('');
|
salixUrl.value = await getUrl('');
|
||||||
|
@ -217,7 +218,14 @@ function getLink(param) {
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceIn.summary.supplier')"
|
:label="t('invoiceIn.summary.supplier')"
|
||||||
:value="invoiceIn.supplier?.name"
|
:value="invoiceIn.supplier?.name"
|
||||||
/>
|
>
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ invoiceIn.supplier?.name }}
|
||||||
|
<SupplierDescriptorProxy :id="invoiceIn.supplierFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceIn.summary.supplierRef')"
|
:label="t('invoiceIn.summary.supplierRef')"
|
||||||
:value="invoiceIn.supplierRef"
|
:value="invoiceIn.supplierRef"
|
||||||
|
@ -282,21 +290,14 @@ function getLink(param) {
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<QCardSection class="q-pa-none">
|
<QCardSection class="q-pa-none">
|
||||||
<a class="header" :href="getLink('basic-data')">
|
<span class="header">{{ t('Totals') }}</span>
|
||||||
{{ t('invoiceIn.pageTitles.basicData') }}
|
|
||||||
<QIcon name="open_in_new" color="primary" />
|
|
||||||
</a>
|
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pa-none">
|
<QCardSection class="q-pa-none">
|
||||||
<div class="bordered q-px-sm q-mx-auto">
|
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceIn.summary.taxableBase')"
|
:label="t('invoiceIn.summary.taxableBase')"
|
||||||
:value="toCurrency(invoiceIn.totals.totalTaxableBase)"
|
:value="toCurrency(invoiceIn.totals.totalTaxableBase)"
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv label="Total" :value="toCurrency(invoiceIn.totals.totalVat)" />
|
||||||
label="Total"
|
|
||||||
:value="toCurrency(invoiceIn.totals.totalVat)"
|
|
||||||
/>
|
|
||||||
<VnLv :label="t('invoiceIn.summary.dueTotal')">
|
<VnLv :label="t('invoiceIn.summary.dueTotal')">
|
||||||
<template #value>
|
<template #value>
|
||||||
<QChip
|
<QChip
|
||||||
|
@ -313,11 +314,10 @@ function getLink(param) {
|
||||||
</QChip>
|
</QChip>
|
||||||
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
|
|||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
</div>
|
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
</QCard>
|
</QCard>
|
||||||
<!--Vat-->
|
<!--Vat-->
|
||||||
<QCard v-if="invoiceIn.invoiceInTax.length">
|
<QCard v-if="invoiceIn.invoiceInTax.length" class="vn-two">
|
||||||
<a class="header" :href="getLink('vat')">
|
<a class="header" :href="getLink('vat')">
|
||||||
{{ t('invoiceIn.card.vat') }}
|
{{ t('invoiceIn.card.vat') }}
|
||||||
<QIcon name="open_in_new" color="primary" />
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
@ -350,7 +350,7 @@ function getLink(param) {
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
<!--Due Day-->
|
<!--Due Day-->
|
||||||
<QCard v-if="invoiceIn.invoiceInDueDay.length">
|
<QCard v-if="invoiceIn.invoiceInDueDay.length" class="vn-one">
|
||||||
<a class="header" :href="getLink('due-day')">
|
<a class="header" :href="getLink('due-day')">
|
||||||
{{ t('invoiceIn.card.dueDay') }}
|
{{ t('invoiceIn.card.dueDay') }}
|
||||||
<QIcon name="open_in_new" color="primary" />
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
@ -416,13 +416,16 @@ function getLink(param) {
|
||||||
.bg {
|
.bg {
|
||||||
background-color: var(--vn-light-gray);
|
background-color: var(--vn-light-gray);
|
||||||
}
|
}
|
||||||
.bordered {
|
|
||||||
border: 1px solid var(--vn-text);
|
@media (max-width: $breakpoint-md) {
|
||||||
max-width: 18em;
|
.cardSummary .summaryBody > .q-card.vn-two {
|
||||||
|
flex: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Search invoice: Buscar factura recibida
|
Search invoice: Buscar factura recibida
|
||||||
You can search by invoice reference: Puedes buscar por referencia de la factura
|
You can search by invoice reference: Puedes buscar por referencia de la factura
|
||||||
|
Totals: Totales
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -12,6 +12,7 @@ import CrudModel from 'src/components/CrudModel.vue';
|
||||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
@ -154,6 +155,7 @@ async function addExpense() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const getTotalTaxableBase = (rows) => rows.reduce((acc, cur) => acc + cur.taxableBase, 0);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
@ -177,6 +179,9 @@ async function addExpense() {
|
||||||
:data-required="{ invoiceInFk: route.params.id }"
|
:data-required="{ invoiceInFk: route.params.id }"
|
||||||
auto-load
|
auto-load
|
||||||
v-model:selected="rowsSelected"
|
v-model:selected="rowsSelected"
|
||||||
|
@save-changes="
|
||||||
|
router.push({ name: 'InvoiceInDueDay', params: { id: route.params.id } })
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<QTable
|
<QTable
|
||||||
|
@ -299,6 +304,19 @@ async function addExpense() {
|
||||||
/>
|
/>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
<template #bottom-row>
|
||||||
jorgep marked this conversation as resolved
Outdated
jgallego
commented
En el summary el pie tiene un color de fondo que es un gris mas claro y ayuda a ver los totales. Copialo En el summary el pie tiene un color de fondo que es un gris mas claro y ayuda a ver los totales. Copialo
jorgep
commented
|
|||||||
|
<QTr class="bg">
|
||||||
|
<QTd />
|
||||||
|
<QTd />
|
||||||
|
<QTd>
|
||||||
|
{{ toCurrency(getTotalTaxableBase(rows)) }}
|
||||||
|
</QTd>
|
||||||
|
<QTd />
|
||||||
|
<QTd />
|
||||||
|
<QTd />
|
||||||
|
<QTd />
|
||||||
|
</QTr>
|
||||||
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
|
|||||||
|
</template>
|
||||||
<template #item="props">
|
<template #item="props">
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
<QCard bordered flat class="q-my-xs">
|
<QCard bordered flat class="q-my-xs">
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
<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 VnSelectFilter from 'src/components/common/VnSelectFilter.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="Invoice"
|
||||||
|
:form-initial-data="newInvoiceIn"
|
||||||
|
@on-data-saved="redirectToInvoiceInBasicData"
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate }">
|
||||||
|
<VnRow>
|
||||||
|
<VnSelectFilter
|
||||||
|
: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>
|
||||||
|
</VnSelectFilter>
|
||||||
|
<VnInput
|
||||||
|
:label="t('invoiceIn.summary.supplierRef')"
|
||||||
|
v-model="data.supplierRef"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelectFilter
|
||||||
|
: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>
|
|
@ -13,6 +13,7 @@ import InvoiceInFilter from './InvoiceInFilter.vue';
|
||||||
import { getUrl } from 'src/composables/getUrl';
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
import InvoiceInSummary from './Card/InvoiceInSummary.vue';
|
import InvoiceInSummary from './Card/InvoiceInSummary.vue';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -85,7 +86,15 @@ function navigate(id) {
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceIn.list.supplier')"
|
:label="t('invoiceIn.list.supplier')"
|
||||||
:value="row.supplierName"
|
:value="row.supplierName"
|
||||||
/>
|
@click.stop
|
||||||
|
>
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ row.supplierName }}
|
||||||
|
<SupplierDescriptorProxy :id="row.supplierFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceIn.list.serialNumber')"
|
:label="t('invoiceIn.list.serialNumber')"
|
||||||
:value="row.serialNumber"
|
:value="row.serialNumber"
|
||||||
|
@ -138,13 +147,7 @@ function navigate(id) {
|
||||||
</div>
|
</div>
|
||||||
</QPage>
|
</QPage>
|
||||||
<QPageSticky position="bottom-right" :offset="[20, 20]">
|
<QPageSticky position="bottom-right" :offset="[20, 20]">
|
||||||
<QBtn
|
<QBtn color="primary" icon="add" size="lg" round :href="`/#/invoice-in/create`" />
|
||||||
color="primary"
|
|
||||||
icon="add"
|
|
||||||
size="lg"
|
|
||||||
round
|
|
||||||
:href="`${url}invoice-in/create`"
|
|
||||||
/>
|
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,7 @@ const getEntryQueryParams = (supplier) => {
|
||||||
<QTooltip>{{ t('Go to client') }}</QTooltip>
|
<QTooltip>{{ t('Go to client') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn
|
<QBtn
|
||||||
:href="`${url}invoice-in/create?supplierFk=${entity.id}`"
|
:href="`#/invoice-in/create?supplierFk=${entity.id}`"
|
||||||
size="md"
|
size="md"
|
||||||
icon="vn:invoice-in-create"
|
icon="vn:invoice-in-create"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
|
@ -36,6 +36,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/InvoiceIn/InvoiceInList.vue'),
|
component: () => import('src/pages/InvoiceIn/InvoiceInList.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'InvoiceInCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'create',
|
||||||
|
icon: 'vn:settings',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/InvoiceIn/InvoiceInCreate.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Lo he pasado aquí para que todos los hooks estén agrupados.