#6942 improve invoiceIn #220

Merged
jorgep merged 63 commits from 6942-improveInvoceIn into dev 2024-05-29 07:03:46 +00:00
8 changed files with 179 additions and 226 deletions
Showing only changes of commit 5bddc6e04d - Show all commits

View File

@ -11,6 +11,7 @@ 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();
@ -85,28 +86,66 @@ const $props = defineProps({
const emit = defineEmits(['onFetch', 'onDataSaved']);
const componentIsRendered = ref(false);
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);
const hasChanges = ref(!$props.observeFormChanges);
const originalData = ref({});
const formData = computed(() => state.get($props.model));
const formUrl = computed(() => $props.url);
const defaultButtons = computed(() => ({
save: {
color: 'primary',
icon: 'save',
label: 'globals.save',
},
reset: {
color: 'primary',
icon: 'restart_alt',
label: 'globals.reset',
},
...$props.defaultButtons,
}));
onMounted(async () => {
Review

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 = $props.formInitialData;
Review

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.
Review

Se podría poner el comentario en código?

Se podría poner el comentario en código?
nextTick(() => {
componentIsRendered.value = true;
});
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();
if ($props.autoLoad && !$props.formInitialData && $props.url) await fetch();
else if (arrayData.store.data) {
state.set($props.model, arrayData.store.data);
emit('onFetch', state.get($props.model));
}
// 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);
watch(
() => formData.value,
(val) => {
hasChanges.value = !isResetting.value && val;
Review

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.
Review

Se podría poner el comentario en código?

Se podría poner el comentario en código?
Review

yo lo veo bien sin comentarios. lo que diga @jgallego

yo lo veo bien sin comentarios. lo que diga @jgallego
Review

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(
() => arrayData.store.data,
(val) => updateAndEmit(val, 'onFetch')
);
watch(formUrl, async () => {
Review

Agrupado con el resto de hooks.

Agrupado con el resto de hooks.
originalData.value = null;
reset();
fetch();
});
onBeforeRouteLeave((to, from, next) => {
Review

Agrupado con el resto de hooks

Agrupado con el resto de hooks
if (hasChanges.value && $props.observeFormChanges)
quasar.dialog({
@ -129,49 +168,14 @@ onUnmounted(() => {
if ($props.clearStoreOnUnmount) state.unset($props.model);
});
const isLoading = ref(false);
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
const isResetting = ref(false);
const hasChanges = ref(!$props.observeFormChanges);
const originalData = ref({});
const formData = computed(() => state.get($props.model));
const formUrl = computed(() => $props.url);
const defaultButtons = computed(() => ({
save: {
color: 'primary',
icon: 'save',
label: 'globals.save',
},
reset: {
color: 'primary',
icon: 'restart_alt',
label: 'globals.reset',
},
...$props.defaultButtons,
}));
const startFormWatcher = () => {
watch(
() => formData.value,
(val) => {
hasChanges.value = !isResetting.value && val;
isResetting.value = false;
},
{ deep: true }
);
};
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 = {};
@ -179,31 +183,30 @@ 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 {
Review

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.
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
const method = $props.urlCreate ? 'post' : 'patch';
const url =
$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));
updateAndEmit(response?.data, 'onDataSaved');
hasChanges.value = false;
} catch (err) {
console.error(err);
notify('errors.writeRequest', 'negative');
} finally {
isLoading.value = false;
}
isLoading.value = false;
}
async function saveAndGo() {
@ -212,10 +215,7 @@ async function saveAndGo() {
}
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;
@ -237,17 +237,15 @@ function filter(value, update, filterOptions) {
);
Review

Esta parte se repetía varias veces

Esta parte se repetía varias veces
}
watch(formUrl, async () => {
originalData.value = null;
reset();
fetch();
});
function updateAndEmit(val, evt) {
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">

View File

@ -49,12 +49,13 @@ const { store } = arrayData;
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
const isLoading = ref(false);
defineExpose({
getData,
});
defineExpose({ getData });
onBeforeMount(async () => {
await getData();
watch($props, async () => await getData());
watch(
() => [$props.url, $props.filter],
async () => await getData()
);
});
Review

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
Review

Se podría poner el comentario en código?

Se podría poner el comentario en código?
async function getData() {

View File

@ -24,6 +24,7 @@ globals:
create: Create
edit: Edit
save: Save
saveAndContinue: Save and continue
remove: Remove
reset: Reset
close: Close

View File

@ -24,6 +24,7 @@ globals:
create: Crear
edit: Modificar
save: Guardar
saveAndContinue: Guardar y continuar
remove: Eliminar
reset: Restaurar
close: Cerrar

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
@ -14,15 +14,13 @@ import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'src/components/common/VnInput.vue';
const quasar = useQuasar();
const { currentRoute } = useRouter();
const { t } = useI18n();
const dms = ref({});
const editDownloadDisabled = ref(false);
const arrayData = useArrayData('InvoiceIn');
const invoiceIn = computed(() => arrayData.store.data);
const invoiceIn = computed(() => useArrayData('InvoiceIn').store.data);
jorgep marked this conversation as resolved Outdated

Usar useRoute().meta.moduleName

Usar useRoute().meta.moduleName
const userConfig = ref(null);
const invoiceId = currentRoute.value.params.id;
const invoiceId = computed(() => +useRoute().params.id);
const expenses = ref([]);
const currencies = ref([]);
@ -180,13 +178,7 @@ async function upsert() {
auto-load
@on-fetch="(data) => (sageWithholdings = data)"
/>
<FormModel
v-if="invoiceIn"
:url="`InvoiceIns/${invoiceId}`"
model="InvoiceIn"
:go-to="`/invoice-in/${invoiceId}/vat`"
auto-load
>
<FormModel model="InvoiceIn" :go-to="`/invoice-in/${invoiceId}/vat`" auto-load>
<template #form="{ data }">
<VnRow>
<VnSelect

View File

@ -6,7 +6,6 @@ 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';
@ -20,9 +19,7 @@ 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, default: null },
});
const $props = defineProps({ id: { type: Number, default: null } });
const { push, currentRoute } = useRouter();
@ -33,7 +30,6 @@ const { openReport, sendEmail } = usePrintService();
const { store } = useArrayData('InvoiceIn');
jorgep marked this conversation as resolved Outdated

useRoute().meta.moduleName

useRoute().meta.moduleName
const invoiceIn = computed(() => store.data);
const isBooked = ref();
const cardDescriptorRef = ref();
const correctionDialogRef = ref();
const entityId = computed(() => $props.id || +currentRoute.value.params.id);
@ -92,11 +88,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 } };
@ -177,16 +169,9 @@ async function setInvoiceCorrection(id) {
);
}
async function setData(entity) {
data.value = useCardDescription(entity.supplierRef, entity.id);
isBooked.value = entity.isBooked;
const { totalDueDay } = await getTotals();
totalAmount.value = totalDueDay;
}
async function getTotals() {
async function setTotals() {
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
return data;
totalAmount.value = data.totalDueDay;
}
function openDialog() {
@ -303,16 +288,17 @@ const createInvoiceInCorrection = async () => {
auto-load
/>
<CardDescriptor
v-if="invoiceIn"
ref="cardDescriptorRef"
module="InvoiceIn"
:url="`InvoiceIns/${entityId}`"
:filter="filter"
:title="data.title"
:subtitle="data.subtitle"
@on-fetch="setData"
data-key="invoiceInData"
:title="invoiceIn.supplierRef"
:subtitle="invoiceIn.id"
data-key="InvoiceIn"
@on-fetch="setTotals"
>
<template #menu="{ entity }">
<template #menu>
<InvoiceInToBook>
<template #content="{ book }">
<QItem
@ -378,17 +364,20 @@ const createInvoiceInCorrection = async () => {
<QItemSection>{{ t('Create rectificative invoice') }}...</QItemSection>
</QItem>
<QItem
v-if="entity.dmsFk"
v-if="invoiceIn.dmsFk"
v-ripple
clickable
@click="downloadFile(entity.dmsFk)"
@click="downloadFile(invoiceIn.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)" />
<template #body>
<VnLv :label="t('invoiceIn.card.issued')" :value="toDate(invoiceIn.issued)" />
<VnLv
:label="t('invoiceIn.summary.booked')"
:value="toDate(invoiceIn.booked)"
/>
<VnLv :label="t('invoiceIn.card.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoiceIn.summary.supplier')">
<template #value>
@ -399,13 +388,13 @@ const createInvoiceInCorrection = async () => {
</template>
</VnLv>
</template>
<template #actions="{ entity }">
<template #action>

Este campo en ningún caso me ha sacado valor

Este campo en ningún caso me ha sacado valor

Esto pasa en salix también en local. Creo que lo comente con Carlos Andrés, es por las fixtures.

Esto pasa en salix también en local. Creo que lo comente con Carlos Andrés, es por las fixtures.

si voy a la seccion http://localhost:9000/#/invoice-in/1/basic-data y cambio el valor de Fecha contable, no actualiza el descriptor, si hago F5 ya lo muestra, deberia hacerlo al apretar guardar en basic-data

si voy a la seccion http://localhost:9000/#/invoice-in/1/basic-data y cambio el valor de Fecha contable, no actualiza el descriptor, si hago F5 ya lo muestra, deberia hacerlo al apretar guardar en basic-data
<QCardActions>
<QBtn
size="md"
icon="vn:supplier"
color="primary"
:to="routes.getSupplier(entity.supplierFk)"
:to="routes.getSupplier(invoiceIn.supplierFk)"
>
<QTooltip>{{ t('invoiceIn.list.supplier') }}</QTooltip>
</QBtn>
@ -413,7 +402,7 @@ const createInvoiceInCorrection = async () => {
size="md"
icon="vn:entry"
color="primary"
:to="routes.getEntry(entity.entryFk)"
:to="routes.getEntry(invoiceIn.entryFk)"
>
<QTooltip>{{ t('Entry') }}</QTooltip>
</QBtn>
@ -421,7 +410,7 @@ const createInvoiceInCorrection = async () => {
size="md"
icon="vn:ticket"
color="primary"
:to="routes.getTickets(entity.supplierFk)"
:to="routes.getTickets(invoiceIn.supplierFk)"
>
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
</QBtn>

View File

@ -11,7 +11,7 @@ import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorP
import InvoiceIntoBook from '../InvoiceInToBook.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
const props = defineProps({ id: { type: Number, default: 0 } });
const props = defineProps({ id: { type: [Number, String], default: 0 } });
const { t } = useI18n();
const entityId = computed(() => props.id || useRoute().params.id);
@ -20,7 +20,6 @@ const invoiceIn = computed(() => useArrayData('InvoiceIn').store.data);
const invoiceInUrl = ref();
const amountsNotMatch = ref(null);
const intrastatTotals = ref({ amount: 0, net: 0, stems: 0 });
const isBooked = ref();
const vatColumns = ref([
{
@ -156,37 +155,26 @@ onMounted(async () => {
invoiceInUrl.value = `${await getUrl('')}invoiceIn/${entityId.value}/`;
});
function getAmountNotMatch(totals) {
return (
totals.totalDueDay != totals.totalTaxableBase &&
totals.totalDueDay != totals.totalVat
);
}
function getTaxTotal(tax) {
return tax.reduce(
(acc, cur) => acc + taxRate(cur.taxableBase, cur.taxTypeSage?.rate),
0
);
}
const init = (data) => {
if (!data) return;
isBooked.value = data.isBooked;
amountsNotMatch.value = getAmountNotMatch(data.totals);
const { totals, invoiceInIntrastat } = data;
amountsNotMatch.value =
totals.totalDueDay != totals.totalTaxableBase &&
totals.totalDueDay != totals.totalVat;
if (data.invoiceInIntrastat.length) {
data.invoiceInIntrastat.forEach((val) => {
intrastatTotals.value.amount += val.amount;
intrastatTotals.value.net += val.net;
intrastatTotals.value.stems += val.stems;
});
}
invoiceInIntrastat.forEach((val) => {
intrastatTotals.value.amount += val.amount;
intrastatTotals.value.net += val.net;
intrastatTotals.value.stems += val.stems;
});
};
const taxRate = (taxableBase = 0, rate = 0) => (rate / 100) * taxableBase;
const getTotalTax = (tax) =>
tax.reduce((acc, cur) => acc + taxRate(cur.taxableBase, cur.taxTypeSage?.rate), 0);
const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</script>
@ -199,7 +187,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<template #header="{ entity }">
<div>{{ entity.id }} - {{ entity.supplier?.name }}</div>
</template>
<template #header-right v-if="!isBooked">
<template #header-right v-if="!invoiceIn?.isBooked">
<InvoiceIntoBook>
<template #content="{ book }">
<QBtn
@ -288,11 +276,9 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
:label="t('invoiceIn.summary.company')"
:value="entity.company?.code"
/>
<QCheckbox
v-if="invoiceIn"
<VnLv
:label="t('invoiceIn.summary.booked')"
v-model="invoiceIn.isBooked"
:disable="true"
:value="invoiceIn.isBooked"
/>
</QCard>
<QCard class="vn-one">
@ -352,7 +338,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QTd>{{ toCurrency(entity.totals.totalTaxableBase) }}</QTd>
<QTd></QTd>
<QTd></QTd>
<QTd>{{ toCurrency(getTaxTotal(entity.invoiceInTax)) }}</QTd>
<QTd>{{ toCurrency(getTotalTax(entity.invoiceInTax)) }}</QTd>
<QTd></QTd>
</QTr>
</template>

View File

@ -4,33 +4,17 @@ import { useI18n } from 'vue-i18n';
import VnSelect from 'components/common/VnSelect.vue';
Review

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
Review

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>