#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)" @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
jsegarra marked this conversation as resolved
Review

Has probado a usar FormPopupDialog?

Has probado a usar FormPopupDialog?
@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`;
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( 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();
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> </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';
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 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',
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)); 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 {
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 { } 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');
jsegarra marked this conversation as resolved Outdated

Eliminar comentarios

Eliminar comentarios
} }
}; }
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"
jsegarra marked this conversation as resolved Outdated

Duda: era q-table o table?

Duda: era q-table o table?
<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>
@ -223,4 +272,4 @@ es:
fileAllowed: Descarga exitosa de archivo CSV fileAllowed: Descarga exitosa de archivo CSV
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
createInvoice: Crear factura createInvoice: Crear factura
</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();
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', { 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>
jsegarra marked this conversation as resolved Outdated

👀

👀
<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'),
}, },