#6899 end invoiceOut migration with VnTable #513

Merged
jon merged 18 commits from 6899_InvoiceOut_End into dev 2024-07-08 09:34:44 +00:00
14 changed files with 527 additions and 401 deletions
Showing only changes of commit cdbbd576e3 - Show all commits

View File

@ -46,22 +46,6 @@ const onDataSaved = async (formData, requestResponse) => {
@on-fetch="(data) => (taxAreasOptions = data)"
auto-load
/>
<FetchData
url="Tickets"
:filter="{
fields: ['id', 'nickname'],
where: { refFk: null },
order: 'shipped DESC',
}"
@on-fetch="(data) => (ticketsOptions = data)"
auto-load
/>
<FetchData
url="Clients"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (clientsOptions = data)"
auto-load
/>
<FormModelPopup
ref="formModelPopupRef"
:title="t('Create manual invoice')"
@ -84,6 +68,10 @@ const onDataSaved = async (formData, requestResponse) => {
option-value="id"
v-model="data.ticketFk"
@update:model-value="data.clientFk = null"
url="Tickets"
:where="{ refFk: null }"
:fields="['id', 'nickname']"
:filter-options="{ order: 'shipped DESC' }"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -105,6 +93,9 @@ const onDataSaved = async (formData, requestResponse) => {
option-value="id"
v-model="data.clientFk"
@update:model-value="data.ticketFk = null"
url="Clients"
:fields="['id', 'name']"
:filter-options="{ order: 'name ASC' }"
/>
<VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
</VnRow>
@ -116,7 +107,6 @@ const onDataSaved = async (formData, requestResponse) => {
option-label="description"
option-value="code"
v-model="data.serial"
:required="true"
/>
<VnSelect
:label="t('Area')"
@ -125,7 +115,6 @@ const onDataSaved = async (formData, requestResponse) => {
option-label="code"
option-value="code"
v-model="data.taxArea"
:required="true"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">

View File

@ -8,7 +8,7 @@ import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue';
import VnSelect from 'components/common/VnSelect.vue';
import FormPopup from './FormPopup.vue';
import { useDialogPluginComponent } from 'quasar';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
@ -18,7 +18,7 @@ const $props = defineProps({
default: () => {},
},
});
const { dialogRef } = useDialogPluginComponent();
const quasar = useQuasar();
const { t } = useI18n();
const router = useRouter();
@ -70,14 +70,11 @@ const makeInvoice = async () => {
});
});
if (!response) {
console.log('entra cuando no checkbox');
return;
}
}
console.log('params: ', params);
const { data } = await axios.post('InvoiceOuts/transferInvoice', params);
console.log('data: ', data);
notify(t('Transferred invoice'), 'positive');
const id = data?.[0];
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });
@ -119,90 +116,92 @@ const makeInvoice = async () => {
@on-fetch="(data) => (invoiceCorrectionTypesOptions = data)"
auto-load
/>
<FormPopup
<QDialog ref="dialogRef">
<FormPopup
jsegarra marked this conversation as resolved
Review

Has probado a usar FormPopupDialog?

Has probado a usar FormPopupDialog?
@on-submit="makeInvoice()"
:title="t('Transfer invoice')"
:custom-submit-button-label="t('Transfer client')"
:default-cancel-button="false"
>
>
<template #form-inputs>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
:label="t('Client')"
:options="clientsOptions"
hide-selected
option-label="name"
option-value="id"
v-model="transferInvoiceParams.newClientFk"
:required="true"
url="Clients"
:fields="['id', 'name', 'hasToInvoiceByAddress']"
auto-load
>
<template #option="scope">
<QItem
v-bind="scope.itemProps"
@click="selectedClient(scope.opt)"
>
<QItemSection>
<QItemLabel>
#{{ scope.opt?.id }} - {{ scope.opt?.name }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnSelect
:label="t('Rectificative type')"
:options="rectificativeTypeOptions"
hide-selected
option-label="description"
option-value="id"
v-model="transferInvoiceParams.cplusRectificationTypeFk"
:required="true"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
:label="t('Class')"
:options="siiTypeInvoiceOutsOptions"
hide-selected
option-label="description"
option-value="id"
v-model="transferInvoiceParams.siiTypeInvoiceOutFk"
:required="true"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.code }} -
{{ scope.opt?.description }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnSelect
:label="t('Type')"
:options="invoiceCorrectionTypesOptions"
hide-selected
option-label="description"
option-value="id"
v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
:required="true"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div>
<QCheckbox :label="t('Bill destination client')" v-model="checked" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip>{{ t('transferInvoiceInfo') }}</QTooltip>
</QIcon>
</div>
</VnRow>
</template>
</FormPopup>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
:label="t('Client')"
:options="clientsOptions"
hide-selected
option-label="name"
option-value="id"
v-model="transferInvoiceParams.newClientFk"
:required="true"
url="Clients"
:fields="['id', 'name', 'hasToInvoiceByAddress']"
auto-load
>
<template #option="scope">
<QItem
v-bind="scope.itemProps"
@click="selectedClient(scope.opt)"
>
<QItemSection>
<QItemLabel>
#{{ scope.opt?.id }} - {{ scope.opt?.name }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnSelect
:label="t('Rectificative type')"
:options="rectificativeTypeOptions"
hide-selected
option-label="description"
option-value="id"
v-model="transferInvoiceParams.cplusRectificationTypeFk"
:required="true"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
:label="t('Class')"
:options="siiTypeInvoiceOutsOptions"
hide-selected
option-label="description"
option-value="id"
v-model="transferInvoiceParams.siiTypeInvoiceOutFk"
:required="true"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.code }} -
{{ scope.opt?.description }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnSelect
:label="t('Type')"
:options="invoiceCorrectionTypesOptions"
hide-selected
option-label="description"
option-value="id"
v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
:required="true"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div>
<QCheckbox :label="t('Bill destination client')" v-model="checked" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip>{{ t('transferInvoiceInfo') }}</QTooltip>
</QIcon>
</div>
</VnRow>
</template>
</FormPopup>
</QDialog>
</template>
<i18n>

View File

@ -59,6 +59,10 @@ const $props = defineProps({
type: Boolean,
default: false,
},
hasSubtoolbar: {
type: Boolean,
default: true,
},
});
const { t } = useI18n();
const stateStore = useStateStore();
@ -177,9 +181,12 @@ function columnName(col) {
function getColAlign(col) {
return 'text-' + (col.align ?? 'left')
}
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
defineExpose({
reload,
redirect: redirectFn,
selected
});
</script>
<template>
@ -225,11 +232,14 @@ defineExpose({
:search-url="searchUrl"
:disable-infinite-scroll="mode == TABLE_MODE"
@save-changes="reload"
:has-subtoolbar="isEditable"
:has-subtoolbar="$attrs['hasSubtoolbar'] ?? isEditable"
>
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
</template>
<template #body="{ rows }">
<QTable
v-bind="$attrs['QTable']"
v-bind="$attrs['q-table']"
class="vnTable"
:columns="splittedColumns.columns"
:rows="rows"
@ -246,6 +256,7 @@ defineExpose({
CrudModelRef.vnPaginateRef.paginate()
"
@row-click="(_, row) => rowClickFunction(row)"
@update:selected="$emit('update:selected', $event)"
>
<template #top-left>
<slot name="top-left"></slot>
@ -477,6 +488,7 @@ defineExpose({
default="input"
v-model="data[column.name]"
:show-label="true"
component-prop="columnCreate"
/>
<slot name="more-create-dialog" :data="data" />
</div>

View File

@ -46,6 +46,7 @@ let arrayData;
let store;
let entity;
const isLoading = ref(false);
const menuRef = ref({});
defineExpose({ getData });
@ -127,9 +128,9 @@ const emit = defineEmits(['onFetch']);
<QTooltip>
{{ t('components.cardDescriptor.moreOptions') }}
</QTooltip>
<QMenu>
<QMenu ref="menuRef">
<QList>
<slot name="menu" :entity="entity" />
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
</QList>
</QMenu>
</QBtn>

View File

@ -63,8 +63,8 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
@on-fetch="setData"
data-key="invoiceOutData"
>
<template #menu="{ entity }">
<InvoiceOutDescriptorMenu :invoice-out-data="entity" />
<template #menu="{ entity, menuRef }">
<InvoiceOutDescriptorMenu :invoice-out-data="entity" :menu-ref="menuRef" />
</template>
<template #body="{ entity }">
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />

View File

@ -1,5 +1,5 @@
<script setup>
import { ref } from 'vue';
import { ref, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
@ -11,13 +11,20 @@ import useNotify from 'src/composables/useNotify';
import { useSession } from 'src/composables/useSession';
import { usePrintService } from 'composables/usePrintService';
import { useVnConfirm } from 'composables/useVnConfirm';
import { getUrl } from 'src/composables/getUrl';
import axios from 'axios';
onBeforeMount(async () => (salixUrl.value = await getUrl('')));
const $props = defineProps({
invoiceOutData: {
type: Object,
default: () => {},
},
menuRef: {
type: Object,
default: () => {},
},
});
const { notify } = useNotify();
@ -28,8 +35,7 @@ const { t } = useI18n();
const { openReport, sendEmail } = usePrintService();
const { openConfirmationModal } = useVnConfirm();
const quasar = useQuasar();
const transferInvoiceDialogRef = ref();
const salixUrl = ref();
const invoiceFormType = ref('pdf');
const defaultEmailAddress = ref($props.invoiceOutData.client?.email);
@ -115,6 +121,7 @@ const refundInvoice = async (withWarehouse) => {
try {
const params = { ref: $props.invoiceOutData.ref, withWarehouse: withWarehouse };
const { data } = await axios.post('InvoiceOuts/refund', params);
location.href = `${salixUrl.value}ticket/${data[0].id}/sale`;
jon marked this conversation as resolved Outdated

podemos probar a usar window.origin en vez de salixURL?

podemos probar a usar window.origin en vez de salixURL?
notify(
t('refundInvoiceSuccessMessage', {
refundTicket: data[0].id,
@ -125,11 +132,21 @@ const refundInvoice = async (withWarehouse) => {
console.error('Error generating invoice out pdf', err);
}
};
const showTransferInvoiceForm = async () => {
$props.menuRef.hide();
jsegarra marked this conversation as resolved
Review

Ufff... me suena que esto ya pasó una vez y lo corregimos

Ufff... me suena que esto ya pasó una vez y lo corregimos
quasar.dialog({
component: TransferInvoiceForm,
componentProps: {
invoiceOutData: $props.invoiceOutData,
},
})
}
</script>
<template>
<QItem v-ripple clickable @click="transferInvoiceDialogRef.show()">
<QItemSection>{{ t('Transfer invoice to...') }}</QItemSection>
<QItem v-ripple clickable>
<QItemSection @click="showTransferInvoiceForm()">{{ t('Transfer invoice to...') }}</QItemSection>
</QItem>
<QItem v-ripple clickable>
<QItemSection>{{ t('Show invoice...') }}</QItemSection>
@ -222,10 +239,6 @@ const refundInvoice = async (withWarehouse) => {
{{ t('Create a single ticket with all the content of the current invoice') }}
</QTooltip>
</QItem>
<QDialog ref="transferInvoiceDialogRef">
<TransferInvoiceForm :invoice-out-data="invoiceOutData" />
</QDialog>
</template>
<i18n>

View File

@ -3,7 +3,7 @@ import { onMounted, ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { toCurrency, toDate } from 'src/filters';
import { toCurrency, toDate, toPercentage } from 'src/filters';
jon marked this conversation as resolved
Review

Hay una traducción que no va bien

Hay una traducción que no va bien
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { getUrl } from 'src/composables/getUrl';
@ -57,12 +57,14 @@ const taxColumns = ref([
name: 'quantity',
label: 'invoiceOut.summary.rate',
field: (row) => row.rate,
format: (value) => toPercentage(value / 100),
sortable: true,
},
{
name: 'invoiceOuted',
label: 'invoiceOut.summary.fee',
field: (row) => row.vat,
format: (value) => toCurrency(value),
sortable: true,
},
]);
@ -164,7 +166,7 @@ const ticketsColumns = ref([
</template>
<template #body-cell-item="{ value }">
<QTd>
<QBtn flat color="primary">
<QBtn flat class="link">
{{ value }}
<TicketDescriptorProxy :id="value" />
</QBtn>
@ -172,7 +174,7 @@ const ticketsColumns = ref([
</template>
<template #body-cell-quantity="{ value, row }">
<QTd>
<QBtn class="no-uppercase" flat color="primary" dense>
<QBtn class="no-uppercase link" flat dense>
{{ value }}
<CustomerDescriptorProxy :id="row.id" />
</QBtn>

View File

@ -41,7 +41,7 @@ function setWorkers(data) {
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<template #body="{ params }">
<QItem>
<QItemSection>
<VnInput
@ -93,7 +93,6 @@ function setWorkers(data) {
<QItemSection>
<QCheckbox
:label="t('Has PDF')"
@update:model-value="searchFn()"
toggle-indeterminate
v-model="params.hasPdf"
/>

View File

@ -30,7 +30,7 @@ const selectedCustomerId = ref(null);
const tableColumnComponents = {
clientId: {
component: QBtn,
props: { flat: true, color: 'blue' },
props: { flat: true, class: 'link' },
event: (prop) => selectCustomerId(prop.value),
},
clientName: {
@ -52,7 +52,12 @@ const tableColumnComponents = {
};
const columns = computed(() => [
{ label: 'Id', field: 'clientId', name: 'clientId', align: 'left' },
{
align: 'left',
name: 'clientId',
label: 'Id',
field: 'clientId',
},
{
label: t('invoiceOut.globalInvoices.table.client'),
field: 'clientName',

View File

@ -1,74 +1,160 @@
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { onMounted, onUnmounted, ref, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import CardList from 'src/components/ui/CardList.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import CreateManualInvoiceForm from 'src/components/CreateManualInvoiceForm.vue';
import InvoiceOutFilter from './InvoiceOutFilter.vue';
import { toDate, toCurrency } from 'src/filters/index';
import { useStateStore } from 'stores/useStateStore';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useSession } from 'src/composables/useSession';
import RightMenu from 'src/components/common/RightMenu.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
import { toCurrency, toDate } from 'src/filters/index';
import { useStateStore } from 'stores/useStateStore';
import { QBtn } from 'quasar';
const { t } = useI18n();
const router = useRouter();
const stateStore = useStateStore();
const session = useSession();
const tokenMultimedia = session.getTokenMultimedia();
const { viewSummary } = useSummaryDialog();
const tableRef = ref();
const invoiceOutSerialsOptions = ref([]);
const ticketsOptions = ref([]);
const customerOptions = ref([]);
const selectedRows = ref([]);
const hasSelectedCards = computed(() => selectedRows.value.length > 0);
const manualInvoiceDialogRef = ref(null);
const selectedCards = ref(new Map());
const columns = computed(() => [
{
align: 'left',
name: 'id',
label: t('invoiceOutList.tableVisibleColumns.id'),
chip: {
condition: () => true,
},
isId: true,
},
{
align: 'left',
name: 'ref',
label: t('invoiceOutList.tableVisibleColumns.ref'),
isTitle: true,
component: 'select',
attrs: {
url: 'InvoiceOuts',
optionLabel: 'ref',
jon marked this conversation as resolved Outdated

5 veces aparece la misma url.

Propuesta, crear una constante que sea MODEL = 'InvoiceOuts'

5 veces aparece la misma url. Propuesta, crear una constante que sea MODEL = 'InvoiceOuts'
optionValue: 'id',
},
columnField: {
component: null,
},
},
{
align: 'left',
name: 'Issued',
label: t('invoiceOutList.tableVisibleColumns.issued'),
component: 'date',
format: (row) => toDate(row.issued),
columnField: {
component: null,
},
},
{
align: 'left',
name: 'clientSocialName',
label: t('invoiceOutModule.customer'),
cardVisible: true,
component: 'select',
attrs: {
url: 'Clients',
fields: ['id', 'name'],
},
columnField: {
component: null,
},
},
{
align: 'left',
name: 'companyCode',
label: t('invoiceOutModule.company'),
cardVisible: true,
component: 'select',
attrs: {
url: 'Companies',
optionLabel: 'code',
optionValue: 'id',
},
columnField: {
component: null,
},
},
{
align: 'left',
name: 'amount',
label: t('invoiceOutModule.amount'),
cardVisible: true,
format: (row) => toCurrency(row.amount),
},
{
align: 'left',
name: 'created',
label: t('invoiceOutList.tableVisibleColumns.created'),
component: 'date',
columnField: {
component: null,
},
format: (row) => toDate(row.created),
},
{
align: 'left',
name: 'dued',
label: t('invoiceOutList.tableVisibleColumns.dueDate'),
component: 'date',
columnField: {
component: null,
},
format: (row) => toDate(row.dued),
},
{
align: 'right',
label: '',
name: 'tableActions',
actions: [
{
title: t('InvoiceOutSummary'),
jsegarra marked this conversation as resolved
Review

Si no pones label, no se rompe nada.
Yo también he pecado de lo mismo

Si no pones label, no se rompe nada. Yo también he pecado de lo mismo
icon: 'preview',
action: (row) => viewSummary(row.id, InvoiceOutSummary),
},
{
title: t('DownloadPdf'),
icon: 'vn:ticket',
isPrimary: true,
action: (row) => openPdf(row.id),
},
],
},
]);
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
function navigate(id) {
router.push({ path: `/invoice-out/${id}` });
}
const toggleIndividualCard = (cardData) => {
if (selectedCards.value.has(cardData.id)) {
selectedCards.value.delete(cardData.id);
return;
}
selectedCards.value.set(cardData.id, cardData);
};
const toggleAllCards = (cardsData) => {
const allSelected = selectedCards.value.size === cardsData.length;
if (!allSelected) {
// Si no todas las tarjetas están seleccionadas, selecciónalas todas
cardsData.forEach((data) => {
if (!selectedCards.value.has(data.id)) {
selectedCards.value.set(data.id, data);
}
});
} else {
// Si todas las tarjetas están seleccionadas, deselecciónalas todas
selectedCards.value.clear();
}
};
const openPdf = () => {
function openPdf(id) {
try {
if (selectedCards.value.size === 0) return;
const selectedCardsArray = Array.from(selectedCards.value.values());
if (selectedCards.value.size === 1) {
const [invoiceOut] = selectedCardsArray;
const url = `api/InvoiceOuts/${invoiceOut.id}/download?access_token=${tokenMultimedia}`;
const url = `api/InvoiceOuts/${id}/download?access_token=${tokenMultimedia}`;
window.open(url, '_blank');
} catch (err) {
console.error('Error opening PDF', err);
}
};
function downloadPdf() {
try {
jon marked this conversation as resolved Outdated

hay un composable que hace justo esto.
Se llama usePrintservice
Pruébalo y sino cumple las necesidades, vemos si aplicarlo o no

En la línea 170 también

hay un composable que hace justo esto. Se llama usePrintservice Pruébalo y sino cumple las necesidades, vemos si aplicarlo o no En la línea 170 también
if (selectedRows.value.size === 0) return;
const selectedCardsArray = Array.from(selectedRows.value.values());
if (selectedRows.value.size === 1) {
const [invoiceOut] = selectedCardsArray;
openPdf(invoiceOut.id);
} else {
const invoiceOutIdsArray = selectedCardsArray.map(
(invoiceOut) => invoiceOut.id
@ -86,11 +172,9 @@ const openPdf = () => {
} catch (err) {
console.error('Error opening PDF');
jsegarra marked this conversation as resolved Outdated

Eliminar comentarios

Eliminar comentarios
}
};
}
const openCreateInvoiceModal = () => {
manualInvoiceDialogRef.value.show();
};
watch(selectedRows, { deep: true });
</script>
<template>
@ -99,115 +183,80 @@ const openCreateInvoiceModal = () => {
:label="t('searchInvoice')"
data-key="InvoiceOutList"
/>
<RightMenu>
<template #right-panel>
<InvoiceOutFilter data-key="InvoiceOutList" />
<VnSubToolbar>
<template #st-actions>
<QBtn color="primary" icon-right="cloud_download" @click="downloadPdf()" :disable="!hasSelectedCards">
<QTooltip>{{ t('globals.downloadPdf') }}</QTooltip>
</QBtn>
</template>
</RightMenu>
<QPage>
<VnPaginate
auto-load
data-key="InvoiceOutList"
:order="['issued DESC', 'id DESC']"
url="InvoiceOuts/filter"
>
<template #body="{ rows }">
<VnSubToolbar class="justify-end">
<template #st-actions>
<QBtn
@click="openPdf()"
class="q-mr-md"
color="primary"
icon="cloud_download"
:disable="selectedCards.size === 0"
>
<QTooltip>{{ t('globals.downloadPdf') }}</QTooltip>
</QBtn>
<QCheckbox
left-label
:label="t('globals.markAll')"
@click="toggleAllCards(rows)"
:model-value="selectedCards.size === rows.length"
class="q-mr-md"
/>
</template>
</VnSubToolbar>
<div class="flex flex-center q-pa-md">
<div class="vn-card-list">
<CardList
:element="row"
:id="row.id"
:show-checkbox="true"
:is-selected="selectedCards.has(row.id)"
:key="row.id"
:title="row.ref"
@click="navigate(row.id)"
@toggle-card-check="toggleIndividualCard(row)"
v-for="row of rows"
>
<template #list-items>
<VnLv
:label="t('invoiceOut.list.issued')"
:value="toDate(row.issued)"
/>
<VnLv
:label="t('invoiceOut.list.amount')"
:value="toCurrency(row.amount)"
/>
<VnLv :label="t('invoiceOut.list.client')">
<template #value>
<span class="link" @click.stop>
{{ row?.clientSocialName }}
<CustomerDescriptorProxy
:id="row?.clientFk"
/>
</span>
</template>
</VnLv>
<VnLv
:label="t('invoiceOut.list.shortCreated')"
:title-label="t('invoiceOut.list.created')"
:value="toDate(row.created)"
/>
<VnLv
:label="t('invoiceOut.list.company')"
:value="row.companyCode"
/>
<VnLv
:label="t('invoiceOut.list.shortDued')"
:title-label="t('invoiceOut.list.dued')"
:value="toDate(row.dued)"
/>
</template>
<template #actions>
<QBtn
:label="t('components.smartCard.openSummary')"
@click.stop="viewSummary(row.id, InvoiceOutSummary)"
color="primary"
style="margin-top: 15px"
type="submit"
/>
</template>
</CardList>
</div>
</div>
</template>
</VnPaginate>
<QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" color="primary" @click="openCreateInvoiceModal()" />
<QTooltip>
{{ t('createInvoice') }}
</QTooltip>
</QPageSticky>
</VnSubToolbar>
<VnTable
ref="tableRef"
data-key="invoiceOut"
url="InvoiceOuts/filter"
:create="{
urlCreate: 'InvoiceOuts/createManualInvoice',
title: t('Create Manual Invoice'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {
active: true,
},
}"
v-model:selected="selectedRows"
order="id DESC"
:columns="columns"
default-mode="table"
redirect="invoice-out"
auto-load
:q-table="{
'row-key':'id',
selection:'multiple'
}"
<QDialog
ref="manualInvoiceDialogRef"
transition-show="scale"
transition-hide="scale"
>
<CreateManualInvoiceForm />
</QDialog>
</QPage>
>
<template #more-create-dialog="{ data }">
<VnSelect
url="Tickets"
v-model="data.ticketFk"
jsegarra marked this conversation as resolved Outdated

Duda: era q-table o table?

Duda: era q-table o table?
:label="t('invoiceOutList.tableVisibleColumns.ticket')"
:options="ticketsOptions"
option-label="nickname"
option-value="id"
/>
<VnSelect
url="Clients"
v-model="data.clientFk"
:label="t('invoiceOutModule.customer')"
:options="customerOptions"
option-label="name"
option-value="id"
/>
<VnInputDate
:label="t('invoiceOutList.tableVisibleColumns.dueDate')"
v-model="data.maxShipped"
/>
<VnSelect
url="InvoiceOutSerials"
v-model="data.invoiceOutSerial"
:label="t('invoiceOutList.tableVisibleColumns.invoiceOutSerial')"
:options="invoiceOutSerialsOptions"
option-label="description"
option-value="code"
/>
<VnSelect
url="TaxAreas"
v-model="data.area"
:label="t('invoiceOutList.tableVisibleColumns.taxArea')"
:options="taxAreasOptions"
option-label="code"
option-value="code"
/>
<QInput
v-model="data.reference"
:label="t('invoiceOutList.tableVisibleColumns.ref')"
/>
</template>
</VnTable>
</template>
<i18n>
@ -223,4 +272,4 @@ es:
fileAllowed: Descarga exitosa de archivo CSV
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
createInvoice: Crear factura
</i18n>
</i18n>

View File

@ -1,21 +1,19 @@
<script setup>
import { computed, onBeforeMount } from 'vue';
import { computed, ref, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import InvoiceOutNegativeFilter from './InvoiceOutNegativeBasesFilter.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { toCurrency } from 'src/filters';
import VnTable from 'src/components/VnTable/VnTable.vue';
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
import { useStateStore } from 'stores/useStateStore';
import { useArrayData } from 'composables/useArrayData';
import RightMenu from 'src/components/common/RightMenu.vue';
import { useArrayData } from 'src/composables/useArrayData';
// import useStateStore from 'src/stores/useStateStore';
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
const stateStore = useStateStore();
const { t } = useI18n();
const tableRef = ref();
// const userFrom = Date.vnFirstDayOfMonth().toISOString();
// const userTo = Date.vnLastDayOfMonth().toISOString();
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
// const stateStore = useStateStore();
jsegarra marked this conversation as resolved
Review

Se ha decidido dejar así para no hacer dos llamadas iguales(la del Vntable y la del arrayData)

Se ha decidido dejar así para no hacer dos llamadas iguales(la del Vntable y la del arrayData)
const arrayData = useArrayData('InvoiceOutNegative', {
url: 'InvoiceOuts/negativeBases',
limit: 0,
@ -36,165 +34,170 @@ const arrayData = useArrayData('InvoiceOutNegative', {
onBeforeMount(async () => {
await arrayData.fetch({ append: false });
stateStore.rightDrawer = true;
// stateStore.rightDrawer = true;
});
const columns = computed(() => [
{
label: t('invoiceOut.negativeBases.company'),
field: 'company',
align: 'left',
name: 'company',
align: 'left',
label: t('invoiceOutModule.company'),
component: 'select',
cardVisible: true,
attrs: {
url: 'Companies',
optionLabel: 'code',
optionValue: 'id',
},
columnField: {
component: null,
}
},
{
label: t('invoiceOut.negativeBases.country'),
field: 'country',
align: 'left',
name: 'country',
align: 'left',
label: t('negativeBases.country'),
component: 'select',
attrs: {
url: 'Countries',
optionLabel: 'name',
optionValue: 'id',
},
columnField: {
component: null,
}
},
{
label: t('invoiceOut.negativeBases.clientId'),
field: 'clientId',
align: 'left',
name: 'clientId',
align: 'left',
label: t('negativeBases.clientId'),
cardVisible: true,
},
{
label: t('invoiceOut.negativeBases.client'),
field: 'clientSocialName',
name: 'client',
align: 'left',
name: 'clientSocialName',
label: t('invoiceOutModule.customer'),
component: 'select',
isTitle: true,
cardVisible: true,
attrs: {
url: 'Clients',
fields: ['id', 'name'],
},
columnField: {
component: null,
}
},
{
label: t('invoiceOut.negativeBases.amount'),
field: 'amount',
align: 'left',
name: 'amount',
align: 'left',
format: (value) => toCurrency(value),
label: t('invoiceOutModule.amount'),
format: (row) => toCurrency(row.amount),
cardVisible: true,
},
{
label: t('invoiceOut.negativeBases.base'),
field: 'taxableBase',
name: 'base',
align: 'left',
name: 'taxableBase',
label: t('negativeBases.base'),
},
{
label: t('invoiceOut.negativeBases.ticketId'),
field: 'ticketFk',
name: 'ticketId',
align: 'left',
name: 'ticketFk',
label: t('negativeBases.ticketId'),
cardVisible: true,
},
{
label: t('invoiceOut.negativeBases.active'),
field: 'isActive',
name: 'active',
align: 'left',
name: 'isActive',
label: t('negativeBases.active'),
},
{
label: t('invoiceOut.negativeBases.hasToInvoice'),
field: 'hasToInvoice',
align: 'left',
name: 'hasToInvoice',
align: 'left',
label: t('negativeBases.hasToInvoice'),
},
{
label: t('invoiceOut.negativeBases.verifiedData'),
field: 'isTaxDataChecked',
name: 'verifiedData',
align: 'left',
name: 'hasVerifiedData',
label: t('negativeBases.verifiedData'),
},
{
label: t('invoiceOut.negativeBases.comercial'),
field: 'workerName',
name: 'worker',
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'),
name: 'workerName',
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
},
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.workerName),
},
]);
const downloadCSV = async () => {
const params = {}; // filter.value;
console.log('arrayData from: ', arrayData.store.userParams.from);
console.log('arrayData to: ', arrayData.store.userParams.to);
const filterParams = {
limit: 20,
where: {
and: [],
},
};
for (const param in params) {
if (params[param]) filterParams.where.and.push({ [param]: params[param] });
}
for (const [key, value] of Object.entries(arrayData.store.userParams)) {
if (value) {
filterParams.where.and.push({ [key]: value });
}
};
await invoiceOutGlobalStore.getNegativeBasesCsv(
arrayData.value.store.userParams.from,
arrayData.value.store.userParams.to,
params
arrayData.store.userParams.from,
arrayData.store.userParams.to,
filterParams
);
};
</script>
<template>
<VnSubToolbar>
<template #st-actions>
<QBtn color="primary" icon-right="download" no-caps @click="downloadCSV()">
<QBtn color="primary" icon-right="download" @click="downloadCSV()">
<QTooltip>{{ t('Download as CSV') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
jsegarra marked this conversation as resolved Outdated

👀

👀
<RightMenu>
<template #right-panel>
<InvoiceOutNegativeFilter data-key="InvoiceOutNegative" />
</template>
</RightMenu>
<QPage class="column items-center q-pa-md">
<QTable
:columns="columns"
:rows="arrayData.store.data"
row-key="clientId"
class="full-width q-mt-md"
>
<template #body-cell-clientId="{ row }">
<QTd>
<QBtn flat dense class="link"> {{ row.clientId }}</QBtn>
<CustomerDescriptorProxy :id="row.clientId" />
</QTd>
</template>
<template #body-cell-ticketId="{ row }">
<QTd>
<QBtn flat dense class="link"> {{ row.ticketFk }}</QBtn>
<TicketDescriptorProxy :id="row.ticketFk" />
</QTd>
</template>
<template #body-cell-worker="{ row }">
<QTd>
<QBtn class="no-uppercase link" flat dense>{{ row.workerName }}</QBtn>
<WorkerDescriptorProxy :id="row.comercialId" />
</QTd>
</template>
<template #body-cell-active="{ row }">
<QTd>
<QCheckbox :model-value="!!row.isActive" disable />
</QTd>
</template>
<template #body-cell-hasToInvoice="{ row }">
<QTd>
<QCheckbox :model-value="!!row.hasToInvoice" disable />
</QTd>
</template>
<template #body-cell-verifiedData="{ row }">
<QTd>
<QCheckbox :model-value="!!row.isTaxDataChecked" disable />
</QTd>
</template>
</QTable>
</QPage>
<VnTable
ref="tableRef"
data-key="invoiceOut"
url="InvoiceOuts/negativeBases"
:user-params="arrayData.store.userParams"
:expr-builder="(param, value) => {
switch (param) {
case 'from':
case 'to':
return;
default:
return { [param]: value };
}
}"
:limit="0"
:columns="columns"
default-mode="table"
auto-load
:is-editable="false"
:use-model="true"
>
<!-- <template #moreBeforeActions>
<QBtn color="primary" icon-right="download" @click="downloadCSV()">
<QTooltip>{{ t('Download as CSV') }}</QTooltip>
</QBtn>
</template> -->
</VnTable>
</template>
<style lang="scss" scoped>
.col-content {
border-radius: 4px;
padding: 6px;
}
.no-uppercase {
text-transform: none;
}
</style>
<i18n>
es:
Download as CSV: Descargar como CSV

View File

@ -0,0 +1,25 @@
invoiceOutModule:
customer: Client
amount: Amount
company: Company
invoiceOutList:
tableVisibleColumns:
id: ID
ref: Reference
issued: Issued
created: Created
dueDate: Max date
invoiceOutSerial: Serial
ticket: Ticket
taxArea: Tax area
DownloadPdf: Download PDF
InvoiceOutSummary: Summary
negativeBases:
country: Country
clientId: Client ID
base: Base
ticketId: Ticket
active: Active
hasToInvoice: Has to invoice
verifiedData: Verified data
commercial: Commercial

View File

@ -1,2 +1,31 @@
Search invoice: Buscar factura emitida
You can search by invoice reference: Puedes buscar por referencia de la factura
invoiceOutModule:
customer: Cliente
amount: Importe
company: Empresa
invoiceOutList:
tableVisibleColumns:
id: ID
ref: Referencia
issued: Fecha emisión
customer: Cliente
company: Empresa
amount: Importe
created: F. creación
dueDate: F. máxima
invoiceOutSerial: Serial
ticket: Ticket
taxArea: Area
DownloadPdf: Descargar PDF
InvoiceOutSummary: Resumen
negativeBases:
country: País
clientId: ID del cliente
client: Cliente
base: Base
ticketId: Ticket
active: Activo
hasToInvoice: Debe facturar
verifiedData: Datos verificados
commercial: Comercial

View File

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