refactor: refs #6899 end invoiceOut migration with VnTable
gitea/salix-front/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
Jon Elias 2024-07-02 14:54:14 +02:00
parent f78ae08550
commit cdbbd576e3
14 changed files with 527 additions and 401 deletions

View File

@ -46,22 +46,6 @@ const onDataSaved = async (formData, requestResponse) => {
@on-fetch="(data) => (taxAreasOptions = data)" @on-fetch="(data) => (taxAreasOptions = data)"
auto-load 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 <FormModelPopup
ref="formModelPopupRef" ref="formModelPopupRef"
:title="t('Create manual invoice')" :title="t('Create manual invoice')"
@ -84,6 +68,10 @@ const onDataSaved = async (formData, requestResponse) => {
option-value="id" option-value="id"
v-model="data.ticketFk" v-model="data.ticketFk"
@update:model-value="data.clientFk = null" @update:model-value="data.clientFk = null"
url="Tickets"
:where="{ refFk: null }"
:fields="['id', 'nickname']"
:filter-options="{ order: 'shipped DESC' }"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -105,6 +93,9 @@ const onDataSaved = async (formData, requestResponse) => {
option-value="id" option-value="id"
v-model="data.clientFk" v-model="data.clientFk"
@update:model-value="data.ticketFk = null" @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" /> <VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
</VnRow> </VnRow>
@ -116,7 +107,6 @@ const onDataSaved = async (formData, requestResponse) => {
option-label="description" option-label="description"
option-value="code" option-value="code"
v-model="data.serial" v-model="data.serial"
:required="true"
/> />
<VnSelect <VnSelect
:label="t('Area')" :label="t('Area')"
@ -125,7 +115,6 @@ const onDataSaved = async (formData, requestResponse) => {
option-label="code" option-label="code"
option-value="code" option-value="code"
v-model="data.taxArea" v-model="data.taxArea"
:required="true"
/> />
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <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 FetchData from 'components/FetchData.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import FormPopup from './FormPopup.vue'; import FormPopup from './FormPopup.vue';
import { useDialogPluginComponent } from 'quasar';
import axios from 'axios'; import axios from 'axios';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
@ -18,7 +18,7 @@ const $props = defineProps({
default: () => {}, default: () => {},
}, },
}); });
const { dialogRef } = useDialogPluginComponent();
const quasar = useQuasar(); const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
@ -70,14 +70,11 @@ const makeInvoice = async () => {
}); });
}); });
if (!response) { if (!response) {
console.log('entra cuando no checkbox');
return; return;
} }
} }
console.log('params: ', params);
const { data } = await axios.post('InvoiceOuts/transferInvoice', params); const { data } = await axios.post('InvoiceOuts/transferInvoice', params);
console.log('data: ', data);
notify(t('Transferred invoice'), 'positive'); notify(t('Transferred invoice'), 'positive');
const id = data?.[0]; const id = data?.[0];
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } }); if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });
@ -119,90 +116,92 @@ const makeInvoice = async () => {
@on-fetch="(data) => (invoiceCorrectionTypesOptions = data)" @on-fetch="(data) => (invoiceCorrectionTypesOptions = data)"
auto-load auto-load
/> />
<FormPopup <QDialog ref="dialogRef">
<FormPopup
@on-submit="makeInvoice()" @on-submit="makeInvoice()"
:title="t('Transfer invoice')" :title="t('Transfer invoice')"
:custom-submit-button-label="t('Transfer client')" :custom-submit-button-label="t('Transfer client')"
:default-cancel-button="false" :default-cancel-button="false"
> >
<template #form-inputs> <template #form-inputs>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<VnSelect <VnSelect
:label="t('Client')" :label="t('Client')"
:options="clientsOptions" :options="clientsOptions"
hide-selected hide-selected
option-label="name" option-label="name"
option-value="id" option-value="id"
v-model="transferInvoiceParams.newClientFk" v-model="transferInvoiceParams.newClientFk"
:required="true" :required="true"
url="Clients" url="Clients"
:fields="['id', 'name', 'hasToInvoiceByAddress']" :fields="['id', 'name', 'hasToInvoiceByAddress']"
auto-load auto-load
> >
<template #option="scope"> <template #option="scope">
<QItem <QItem
v-bind="scope.itemProps" v-bind="scope.itemProps"
@click="selectedClient(scope.opt)" @click="selectedClient(scope.opt)"
> >
<QItemSection> <QItemSection>
<QItemLabel> <QItemLabel>
#{{ scope.opt?.id }} - {{ scope.opt?.name }} #{{ scope.opt?.id }} - {{ scope.opt?.name }}
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelect> </VnSelect>
<VnSelect <VnSelect
:label="t('Rectificative type')" :label="t('Rectificative type')"
:options="rectificativeTypeOptions" :options="rectificativeTypeOptions"
hide-selected hide-selected
option-label="description" option-label="description"
option-value="id" option-value="id"
v-model="transferInvoiceParams.cplusRectificationTypeFk" v-model="transferInvoiceParams.cplusRectificationTypeFk"
:required="true" :required="true"
/> />
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<VnSelect <VnSelect
:label="t('Class')" :label="t('Class')"
:options="siiTypeInvoiceOutsOptions" :options="siiTypeInvoiceOutsOptions"
hide-selected hide-selected
option-label="description" option-label="description"
option-value="id" option-value="id"
v-model="transferInvoiceParams.siiTypeInvoiceOutFk" v-model="transferInvoiceParams.siiTypeInvoiceOutFk"
:required="true" :required="true"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
<QItemSection> <QItemSection>
<QItemLabel> <QItemLabel>
{{ scope.opt?.code }} - {{ scope.opt?.code }} -
{{ scope.opt?.description }} {{ scope.opt?.description }}
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>
</VnSelect> </VnSelect>
<VnSelect <VnSelect
:label="t('Type')" :label="t('Type')"
:options="invoiceCorrectionTypesOptions" :options="invoiceCorrectionTypesOptions"
hide-selected hide-selected
option-label="description" option-label="description"
option-value="id" option-value="id"
v-model="transferInvoiceParams.invoiceCorrectionTypeFk" v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
:required="true" :required="true"
/> />
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<div> <div>
<QCheckbox :label="t('Bill destination client')" v-model="checked" /> <QCheckbox :label="t('Bill destination client')" v-model="checked" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm"> <QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip>{{ t('transferInvoiceInfo') }}</QTooltip> <QTooltip>{{ t('transferInvoiceInfo') }}</QTooltip>
</QIcon> </QIcon>
</div> </div>
</VnRow> </VnRow>
</template> </template>
</FormPopup> </FormPopup>
</QDialog>
</template> </template>
<i18n> <i18n>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -11,13 +11,20 @@ import useNotify from 'src/composables/useNotify';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { usePrintService } from 'composables/usePrintService'; import { usePrintService } from 'composables/usePrintService';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
import { getUrl } from 'src/composables/getUrl';
import axios from 'axios'; import axios from 'axios';
onBeforeMount(async () => (salixUrl.value = await getUrl('')));
const $props = defineProps({ const $props = defineProps({
invoiceOutData: { invoiceOutData: {
type: Object, type: Object,
default: () => {}, default: () => {},
}, },
menuRef: {
type: Object,
default: () => {},
},
}); });
const { notify } = useNotify(); const { notify } = useNotify();
@ -28,8 +35,7 @@ const { t } = useI18n();
const { openReport, sendEmail } = usePrintService(); const { openReport, sendEmail } = usePrintService();
const { openConfirmationModal } = useVnConfirm(); const { openConfirmationModal } = useVnConfirm();
const quasar = useQuasar(); const quasar = useQuasar();
const salixUrl = ref();
const transferInvoiceDialogRef = ref();
const invoiceFormType = ref('pdf'); const invoiceFormType = ref('pdf');
const defaultEmailAddress = ref($props.invoiceOutData.client?.email); const defaultEmailAddress = ref($props.invoiceOutData.client?.email);
@ -115,6 +121,7 @@ const refundInvoice = async (withWarehouse) => {
try { try {
const params = { ref: $props.invoiceOutData.ref, withWarehouse: withWarehouse }; const params = { ref: $props.invoiceOutData.ref, withWarehouse: withWarehouse };
const { data } = await axios.post('InvoiceOuts/refund', params); const { data } = await axios.post('InvoiceOuts/refund', params);
location.href = `${salixUrl.value}ticket/${data[0].id}/sale`;
notify( notify(
t('refundInvoiceSuccessMessage', { t('refundInvoiceSuccessMessage', {
refundTicket: data[0].id, refundTicket: data[0].id,
@ -125,11 +132,21 @@ const refundInvoice = async (withWarehouse) => {
console.error('Error generating invoice out pdf', err); console.error('Error generating invoice out pdf', err);
} }
}; };
const showTransferInvoiceForm = async () => {
$props.menuRef.hide();
quasar.dialog({
component: TransferInvoiceForm,
componentProps: {
invoiceOutData: $props.invoiceOutData,
},
})
}
</script> </script>
<template> <template>
<QItem v-ripple clickable @click="transferInvoiceDialogRef.show()"> <QItem v-ripple clickable>
<QItemSection>{{ t('Transfer invoice to...') }}</QItemSection> <QItemSection @click="showTransferInvoiceForm()">{{ t('Transfer invoice to...') }}</QItemSection>
</QItem> </QItem>
<QItem v-ripple clickable> <QItem v-ripple clickable>
<QItemSection>{{ t('Show invoice...') }}</QItemSection> <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') }} {{ t('Create a single ticket with all the content of the current invoice') }}
</QTooltip> </QTooltip>
</QItem> </QItem>
<QDialog ref="transferInvoiceDialogRef">
<TransferInvoiceForm :invoice-out-data="invoiceOutData" />
</QDialog>
</template> </template>
<i18n> <i18n>

View File

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

View File

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

View File

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

View File

@ -1,74 +1,160 @@
<script setup> <script setup>
import { onMounted, onUnmounted, ref } from 'vue'; import { onMounted, onUnmounted, ref, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.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 VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import CreateManualInvoiceForm from 'src/components/CreateManualInvoiceForm.vue';
import InvoiceOutFilter from './InvoiceOutFilter.vue';
import { toDate, toCurrency } from 'src/filters/index';
import { useStateStore } from 'stores/useStateStore';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useSession } from 'src/composables/useSession'; 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 { t } = useI18n();
const router = useRouter();
const stateStore = useStateStore(); const stateStore = useStateStore();
const session = useSession(); const session = useSession();
const tokenMultimedia = session.getTokenMultimedia(); const tokenMultimedia = session.getTokenMultimedia();
const { viewSummary } = useSummaryDialog(); 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 columns = computed(() => [
const selectedCards = ref(new Map()); {
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',
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'),
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)); onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
function navigate(id) { function openPdf(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 = () => {
try { try {
if (selectedCards.value.size === 0) return; const url = `api/InvoiceOuts/${id}/download?access_token=${tokenMultimedia}`;
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}`;
window.open(url, '_blank'); window.open(url, '_blank');
} catch (err) {
console.error('Error opening PDF', err);
}
};
function downloadPdf() {
try {
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 { } else {
const invoiceOutIdsArray = selectedCardsArray.map( const invoiceOutIdsArray = selectedCardsArray.map(
(invoiceOut) => invoiceOut.id (invoiceOut) => invoiceOut.id
@ -86,11 +172,9 @@ const openPdf = () => {
} catch (err) { } catch (err) {
console.error('Error opening PDF'); console.error('Error opening PDF');
} }
}; }
const openCreateInvoiceModal = () => { watch(selectedRows, { deep: true });
manualInvoiceDialogRef.value.show();
};
</script> </script>
<template> <template>
@ -99,115 +183,80 @@ const openCreateInvoiceModal = () => {
:label="t('searchInvoice')" :label="t('searchInvoice')"
data-key="InvoiceOutList" data-key="InvoiceOutList"
/> />
<RightMenu> <VnSubToolbar>
<template #right-panel> <template #st-actions>
<InvoiceOutFilter data-key="InvoiceOutList" /> <QBtn color="primary" icon-right="cloud_download" @click="downloadPdf()" :disable="!hasSelectedCards">
<QTooltip>{{ t('globals.downloadPdf') }}</QTooltip>
</QBtn>
</template> </template>
</RightMenu> </VnSubToolbar>
<QPage> <VnTable
<VnPaginate ref="tableRef"
auto-load data-key="invoiceOut"
data-key="InvoiceOutList" url="InvoiceOuts/filter"
:order="['issued DESC', 'id DESC']" :create="{
url="InvoiceOuts/filter" urlCreate: 'InvoiceOuts/createManualInvoice',
> title: t('Create Manual Invoice'),
<template #body="{ rows }"> onDataSaved: ({ id }) => tableRef.redirect(id),
<VnSubToolbar class="justify-end"> formInitialData: {
<template #st-actions> active: true,
<QBtn },
@click="openPdf()" }"
class="q-mr-md" v-model:selected="selectedRows"
color="primary" order="id DESC"
icon="cloud_download" :columns="columns"
:disable="selectedCards.size === 0" default-mode="table"
> redirect="invoice-out"
<QTooltip>{{ t('globals.downloadPdf') }}</QTooltip> auto-load
</QBtn> :q-table="{
<QCheckbox 'row-key':'id',
left-label selection:'multiple'
: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>
<QDialog >
ref="manualInvoiceDialogRef" <template #more-create-dialog="{ data }">
transition-show="scale" <VnSelect
transition-hide="scale" url="Tickets"
> v-model="data.ticketFk"
<CreateManualInvoiceForm /> :label="t('invoiceOutList.tableVisibleColumns.ticket')"
</QDialog> :options="ticketsOptions"
</QPage> 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> </template>
<i18n> <i18n>

View File

@ -1,21 +1,19 @@
<script setup> <script setup>
import { computed, onBeforeMount } from 'vue'; import { computed, ref, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n'; 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 VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
import VnTable from 'src/components/VnTable/VnTable.vue';
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js'; import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
import { useStateStore } from 'stores/useStateStore'; import { useArrayData } from 'src/composables/useArrayData';
import { useArrayData } from 'composables/useArrayData'; // import useStateStore from 'src/stores/useStateStore';
import RightMenu from 'src/components/common/RightMenu.vue';
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
const stateStore = useStateStore();
const { t } = useI18n(); const { t } = useI18n();
const tableRef = ref();
// const userFrom = Date.vnFirstDayOfMonth().toISOString();
// const userTo = Date.vnLastDayOfMonth().toISOString();
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
// const stateStore = useStateStore();
const arrayData = useArrayData('InvoiceOutNegative', { const arrayData = useArrayData('InvoiceOutNegative', {
url: 'InvoiceOuts/negativeBases', url: 'InvoiceOuts/negativeBases',
limit: 0, limit: 0,
@ -36,165 +34,170 @@ const arrayData = useArrayData('InvoiceOutNegative', {
onBeforeMount(async () => { onBeforeMount(async () => {
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
stateStore.rightDrawer = true; // stateStore.rightDrawer = true;
}); });
const columns = computed(() => [ const columns = computed(() => [
{ {
label: t('invoiceOut.negativeBases.company'), align: 'left',
field: 'company',
name: 'company', 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'), align: 'left',
field: 'country',
name: 'country', 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'), align: 'left',
field: 'clientId',
name: 'clientId', name: 'clientId',
align: 'left', label: t('negativeBases.clientId'),
cardVisible: true,
}, },
{ {
label: t('invoiceOut.negativeBases.client'),
field: 'clientSocialName',
name: 'client',
align: 'left', 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'), align: 'left',
field: 'amount',
name: 'amount', name: 'amount',
align: 'left', label: t('invoiceOutModule.amount'),
format: (value) => toCurrency(value), format: (row) => toCurrency(row.amount),
cardVisible: true,
}, },
{ {
label: t('invoiceOut.negativeBases.base'),
field: 'taxableBase',
name: 'base',
align: 'left', align: 'left',
name: 'taxableBase',
label: t('negativeBases.base'),
}, },
{ {
label: t('invoiceOut.negativeBases.ticketId'),
field: 'ticketFk',
name: 'ticketId',
align: 'left', align: 'left',
name: 'ticketFk',
label: t('negativeBases.ticketId'),
cardVisible: true,
}, },
{ {
label: t('invoiceOut.negativeBases.active'),
field: 'isActive',
name: 'active',
align: 'left', align: 'left',
name: 'isActive',
label: t('negativeBases.active'),
}, },
{ {
label: t('invoiceOut.negativeBases.hasToInvoice'), align: 'left',
field: 'hasToInvoice',
name: 'hasToInvoice', name: 'hasToInvoice',
align: 'left', label: t('negativeBases.hasToInvoice'),
}, },
{ {
label: t('invoiceOut.negativeBases.verifiedData'),
field: 'isTaxDataChecked',
name: 'verifiedData',
align: 'left', align: 'left',
name: 'hasVerifiedData',
label: t('negativeBases.verifiedData'),
}, },
{ {
label: t('invoiceOut.negativeBases.comercial'),
field: 'workerName',
name: 'worker',
align: 'left', 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 downloadCSV = async () => {
const params = {}; // filter.value; console.log('arrayData from: ', arrayData.store.userParams.from);
console.log('arrayData to: ', arrayData.store.userParams.to);
const filterParams = { const filterParams = {
limit: 20, limit: 20,
where: { where: {
and: [], 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( await invoiceOutGlobalStore.getNegativeBasesCsv(
arrayData.value.store.userParams.from, arrayData.store.userParams.from,
arrayData.value.store.userParams.to, arrayData.store.userParams.to,
params filterParams
); );
}; };
</script> </script>
<template> <template>
<VnSubToolbar> <VnSubToolbar>
<template #st-actions> <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> <QTooltip>{{ t('Download as CSV') }}</QTooltip>
</QBtn> </QBtn>
</template> </template>
</VnSubToolbar> </VnSubToolbar>
<RightMenu> <VnTable
<template #right-panel> ref="tableRef"
<InvoiceOutNegativeFilter data-key="InvoiceOutNegative" /> data-key="invoiceOut"
</template> url="InvoiceOuts/negativeBases"
</RightMenu> :user-params="arrayData.store.userParams"
<QPage class="column items-center q-pa-md"> :expr-builder="(param, value) => {
<QTable switch (param) {
:columns="columns" case 'from':
:rows="arrayData.store.data" case 'to':
row-key="clientId" return;
class="full-width q-mt-md" default:
> return { [param]: value };
<template #body-cell-clientId="{ row }"> }
<QTd> }"
<QBtn flat dense class="link"> {{ row.clientId }}</QBtn> :limit="0"
<CustomerDescriptorProxy :id="row.clientId" /> :columns="columns"
</QTd> default-mode="table"
</template> auto-load
<template #body-cell-ticketId="{ row }"> :is-editable="false"
<QTd> :use-model="true"
<QBtn flat dense class="link"> {{ row.ticketFk }}</QBtn> >
<TicketDescriptorProxy :id="row.ticketFk" /> <!-- <template #moreBeforeActions>
</QTd> <QBtn color="primary" icon-right="download" @click="downloadCSV()">
</template> <QTooltip>{{ t('Download as CSV') }}</QTooltip>
<template #body-cell-worker="{ row }"> </QBtn>
<QTd> </template> -->
<QBtn class="no-uppercase link" flat dense>{{ row.workerName }}</QBtn> </VnTable>
<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>
</template> </template>
<style lang="scss" scoped>
.col-content {
border-radius: 4px;
padding: 6px;
}
.no-uppercase {
text-transform: none;
}
</style>
<i18n> <i18n>
es: es:
Download as CSV: Descargar como CSV 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 Search invoice: Buscar factura emitida
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
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', name: 'InvoiceOutGlobal',
meta: { meta: {
title: 'globalInvoicing', title: 'globalInvoicing',
icon: 'contact_support', icon: 'Date_Range',
}, },
component: () => import('src/pages/InvoiceOut/InvoiceOutGlobal.vue'), component: () => import('src/pages/InvoiceOut/InvoiceOutGlobal.vue'),
}, },