salix-front/src/pages/InvoiceOut/InvoiceOutList.vue

431 lines
16 KiB
Vue

<script setup>
import { ref, computed, watchEffect } from 'vue';
import { useI18n } from 'vue-i18n';
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 VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { usePrintService } from 'src/composables/usePrintService';
import VnTable from 'src/components/VnTable/VnTable.vue';
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
import { toCurrency, toDate } from 'src/filters/index';
import { QBtn } from 'quasar';
import axios from 'axios';
import RightMenu from 'src/components/common/RightMenu.vue';
import InvoiceOutFilter from './InvoiceOutFilter.vue';
import VnRow from 'src/components/ui/VnRow.vue';
import VnRadio from 'src/components/common/VnRadio.vue';
import VnInput from 'src/components/common/VnInput.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
const { t } = useI18n();
const { viewSummary } = useSummaryDialog();
const tableRef = ref();
const invoiceOutSerialsOptions = ref([]);
const customerOptions = ref([]);
const selectedRows = ref([]);
const hasSelectedCards = computed(() => selectedRows.value.length > 0);
const MODEL = 'InvoiceOuts';
const { openReport } = usePrintService();
const addressOptions = ref([]);
const selectedOption = ref('ticket');
async function fetchClientAddress(id) {
const { data } = await axios.get(
`Clients/${id}/addresses?filter[order]=isActive DESC`
);
addressOptions.value = data;
}
const exprBuilder = (_, value) => {
return {
or: [{ code: value }, { description: { like: `%${value}%` } }],
};
};
const columns = computed(() => [
{
align: 'center',
name: 'id',
label: t('invoiceOutList.tableVisibleColumns.id'),
chip: { condition: () => true },
isId: true,
columnFilter: {
name: 'id',
},
},
{
align: 'left',
name: 'ref',
label: t('globals.reference'),
isTitle: true,
component: 'select',
attrs: {
url: MODEL,
optionLabel: 'ref',
optionValue: 'ref',
},
columnField: { component: null },
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
name: 'issued',
label: t('invoiceOut.summary.issued'),
component: 'date',
format: (row) => toDate(row.issued),
columnField: { component: null },
},
{
align: 'left',
name: 'clientFk',
label: t('globals.client'),
cardVisible: true,
component: 'select',
attrs: {
url: 'Clients',
fields: ['id', 'socialName'],
optionLabel: 'socialName',
optionValue: 'id',
},
columnField: {
component: null,
},
},
{
align: 'left',
name: 'companyCode',
label: t('globals.company'),
cardVisible: true,
component: 'select',
attrs: { url: 'Companies', optionLabel: 'code', optionValue: 'id' },
columnField: { component: null },
},
{
align: 'left',
name: 'amount',
label: t('globals.amount'),
cardVisible: true,
format: (row) => toCurrency(row.amount),
},
{
align: 'left',
name: 'created',
label: t('globals.created'),
component: 'date',
columnField: { component: null },
format: (row) => toDate(row.created),
},
{
align: 'left',
name: 'dued',
label: t('invoiceOut.summary.dued'),
component: 'date',
columnField: { component: null },
format: (row) => toDate(row.dued),
},
{
align: 'left',
name: 'customsAgentFk',
label: t('invoiceOutList.tableVisibleColumns.customsAgent'),
cardVisible: true,
format: (row, dashIfEmpty) => dashIfEmpty(row.customsAgentName),
},
{
align: 'right',
name: 'tableActions',
actions: [
{
title: t('components.smartCard.viewSummary'),
icon: 'preview',
isPrimary: true,
action: (row) => viewSummary(row.id, InvoiceOutSummary),
},
{
title: t('globals.downloadPdf'),
icon: 'cloud_download',
isPrimary: true,
action: (row) => openPdf(row.id),
},
],
},
]);
function openPdf(id) {
openReport(`${MODEL}/${id}/download`);
}
function downloadPdf() {
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);
const invoiceOutIds = invoiceOutIdsArray.join(',');
const params = {
ids: invoiceOutIds,
};
openReport(`${MODEL}/downloadZip`, params);
}
}
watchEffect(selectedRows);
</script>
<template>
<VnSearchbar
:info="t('youCanSearchByInvoiceReference')"
:label="t('Search invoice')"
data-key="invoiceOutList"
/>
<RightMenu>
<template #right-panel>
<InvoiceOutFilter data-key="invoiceOutList" />
</template>
</RightMenu>
<VnSubToolbar>
<template #st-actions>
<QBtn
color="primary"
icon-right="cloud_download"
@click="downloadPdf()"
:disable="!hasSelectedCards"
data-cy="InvoiceOutDownloadPdfBtn"
>
<QTooltip>{{ t('downloadPdf') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<VnTable
ref="tableRef"
data-key="invoiceOutList"
:url="`${MODEL}/filter`"
:create="{
urlCreate: 'InvoiceOuts/createManualInvoice',
title: t('createManualInvoice'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: { active: true },
}"
:right-search="false"
v-model:selected="selectedRows"
order="id DESC"
:columns="columns"
redirect="invoice-out"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
>
<template #column-clientFk="{ row }">
<span class="link" @click.stop>
{{ row.clientSocialName }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #more-create-dialog="{ data }">
<div class="row q-col-gutter-xs">
<div class="col-12">
<div class="q-col-gutter-xs">
<VnRow fixed>
<VnRadio
v-model="selectedOption"
val="ticket"
:label="t('globals.ticket')"
class="q-my-none q-mr-md"
/>
<VnInput
v-show="selectedOption === 'ticket'"
v-model="data.ticketFk"
:label="t('globals.ticket')"
style="flex: 1"
data-cy="InvoiceOutCreateTicketinput"
/>
<div
class="row q-col-gutter-xs q-ml-none"
v-show="selectedOption !== 'ticket'"
>
<div class="col">
<VnSelect
v-model="data.clientFk"
:label="t('globals.client')"
url="Clients"
:options="customerOptions"
option-label="name"
option-value="id"
@update:model-value="fetchClientAddress"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
#{{ scope.opt?.id }} -
{{ scope.opt?.name }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</div>
<div class="col">
<VnSelect
v-model="data.addressFk"
:label="t('ticket.summary.consignee')"
:options="addressOptions"
option-label="nickname"
option-value="id"
v-if="
data.clientFk &&
selectedOption === 'consignatario'
"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel
:class="{
'color-vn-label':
!scope.opt?.isActive,
}"
>
{{
`${
!scope.opt?.isActive
? t('inactive')
: ''
} `
}}
<span>{{
scope.opt?.nickname
}}</span>
<span
v-if="
scope.opt?.province ||
scope.opt?.city ||
scope.opt?.street
"
>
, {{ scope.opt?.street }},
{{ scope.opt?.city }},
{{
scope.opt?.province?.name
}}
-
{{
scope.opt?.agencyMode
?.name
}}
</span>
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</div>
</div>
</VnRow>
<VnRow fixed>
<VnRadio
v-model="selectedOption"
val="cliente"
:label="t('globals.client')"
class="q-my-none q-mr-md"
/>
</VnRow>
<VnRow fixed>
<VnRadio
v-model="selectedOption"
val="consignatario"
:label="t('ticket.summary.consignee')"
class="q-my-none q-mr-md"
/>
</VnRow>
</div>
</div>
<div class="full-width">
<VnRow class="row q-col-gutter-xs">
<VnSelect
url="InvoiceOutSerials"
v-model="data.serial"
:label="t('invoicein.serial')"
:options="invoiceOutSerialsOptions"
option-label="description"
option-value="code"
option-filter
:expr-builder="exprBuilder"
data-cy="InvoiceOutCreateSerialSelect"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.code }} -
{{ scope.opt?.description }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInputDate
:label="t('invoiceOut.summary.dued')"
v-model="data.maxShipped"
/>
</VnRow>
<VnRow class="row q-col-gutter-xs">
<VnSelect
url="TaxAreas"
v-model="data.taxArea"
:label="t('invoiceOutList.tableVisibleColumns.taxArea')"
:options="taxAreasOptions"
option-label="code"
option-value="code"
/>
<VnInput
v-model="data.reference"
:label="t('globals.reference')"
/>
</VnRow>
</div>
</div>
</template>
</VnTable>
</template>
<style lang="scss" scoped>
#formModel .vn-row {
min-height: 45px;
.q-radio {
align-self: flex-end;
flex: 0.3;
}
> .q-input,
> .q-select {
flex: 0.75;
}
}
</style>
<i18n>
en:
invoiceId: Invoice ID
youCanSearchByInvoiceReference: You can search by invoice reference
createManualInvoice: Create Manual Invoice
inactive: (Inactive)
es:
invoiceId: ID de factura
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
createManualInvoice: Crear factura manual
inactive: (Inactivo)
</i18n>