All checks were successful
Dev Build & Deploy Portal / build-deploy (push) Successful in 2m31s
227 lines
11 KiB
Vue
227 lines
11 KiB
Vue
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { useExtratoDebitosPortal } from '@/composables/useExtratoDebitosPortal'
|
|
import { formatCurrency, formatDate } from '@/utils/formatador'
|
|
import ModalTransacoesContaCorrente from '@/components/extrato/ModalTransacoesContaCorrente.vue'
|
|
|
|
definePageMeta({
|
|
layout: 'portal',
|
|
middleware: 'auth',
|
|
})
|
|
|
|
const modalTransacoes = ref(null)
|
|
|
|
const {
|
|
resultados,
|
|
isLoading,
|
|
isLoadingGuia,
|
|
isLoadingExtrato,
|
|
mensagemErro,
|
|
dataVencimento,
|
|
erros,
|
|
filtro,
|
|
opcoesEstadoConta,
|
|
temSelecionado,
|
|
temDebitoSelecionado,
|
|
totalizadores,
|
|
consultar,
|
|
limparFiltros,
|
|
gerarGuia,
|
|
gerarExtratoPdf,
|
|
getEstadoSeverity,
|
|
} = useExtratoDebitosPortal()
|
|
|
|
onMounted(() => consultar())
|
|
|
|
function getSegundoNome(texto) {
|
|
const palavras = (texto || '').trim().split(/\s+/)
|
|
return palavras.length > 1 ? palavras[1] : texto
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100">Extrato de Débitos</h1>
|
|
<p class="text-sm text-slate-500 dark:text-slate-400 mt-0.5">
|
|
Consulte seus débitos, selecione parcelas e emita guias de pagamento.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 space-y-4">
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1.5">Período Início</label>
|
|
<DatePicker v-model="filtro.periodoIni" view="month" date-format="mm/yy" show-icon class="w-full" size="small" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1.5">Período Fim</label>
|
|
<DatePicker v-model="filtro.periodoFim" view="month" date-format="mm/yy" show-icon class="w-full" size="small" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1.5">Estado da Conta</label>
|
|
<Select
|
|
v-model="filtro.estadoConta"
|
|
:options="opcoesEstadoConta"
|
|
option-label="label"
|
|
option-value="value"
|
|
placeholder="Todos"
|
|
class="w-full"
|
|
size="small"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2 flex-wrap">
|
|
<Button label="Consultar" icon="pi pi-search" size="small" :loading="isLoading" @click="consultar" />
|
|
<Button label="Limpar" icon="pi pi-eraser" size="small" severity="secondary" outlined @click="limparFiltros" />
|
|
</div>
|
|
</div>
|
|
|
|
<Message v-if="mensagemErro" severity="warn" :closable="false">{{ mensagemErro }}</Message>
|
|
|
|
<div v-if="isLoading" class="space-y-3">
|
|
<div v-for="i in 3" :key="i" class="h-24 bg-slate-200 dark:bg-slate-700 rounded-xl animate-pulse" />
|
|
</div>
|
|
|
|
<div v-else-if="resultados.length === 0 && !mensagemErro" class="bg-white dark:bg-slate-800 rounded-xl border p-12 text-center">
|
|
<i class="pi pi-check-circle text-emerald-400 text-4xl mb-3 block" />
|
|
<p class="font-semibold text-slate-700 dark:text-slate-200">Nenhum débito encontrado</p>
|
|
</div>
|
|
|
|
<template v-else-if="resultados.length">
|
|
<Accordion :multiple="true" :active-index="[0]">
|
|
<AccordionPanel v-for="(tributo, index) in resultados" :key="tributo.idContaTributo" :value="String(index)">
|
|
<AccordionHeader>
|
|
<div class="flex items-center justify-between w-full gap-2 pr-2">
|
|
<span class="text-sm font-semibold truncate">
|
|
Conta {{ tributo.idContaTributo }} — {{ tributo.descricaoContaTributo }} / {{ tributo.descricaoTributo }}
|
|
</span>
|
|
<span class="text-sm shrink-0">
|
|
Total: <strong>{{ formatCurrency(tributo.totalPagamentos) }}</strong>
|
|
</span>
|
|
</div>
|
|
</AccordionHeader>
|
|
<AccordionContent>
|
|
<DataTable
|
|
v-model:selection="tributo.selecionados"
|
|
:value="tributo.debitos"
|
|
data-key="id"
|
|
size="small"
|
|
show-gridlines
|
|
selection-mode="multiple"
|
|
:is-row-selectable="({ data }) => data.codigoEstadoConta === 1"
|
|
scrollable
|
|
class="mt-2"
|
|
>
|
|
<Column selection-mode="multiple" header-style="width: 3rem" />
|
|
<Column field="numDoc" header="Nº Doc" style="min-width: 90px" />
|
|
<Column field="estadoConta" header="Estado" style="min-width: 90px">
|
|
<template #body="{ data }">
|
|
<Tag :value="getSegundoNome(data.estadoConta)" :severity="getEstadoSeverity(data.codigoEstadoConta)" />
|
|
</template>
|
|
</Column>
|
|
<Column field="periodoRef" header="Período" style="min-width: 80px" />
|
|
<Column field="dataVencimento" header="Vencimento" style="min-width: 95px">
|
|
<template #body="{ data }">{{ formatDate(data.dataVencimento) }}</template>
|
|
</Column>
|
|
<Column field="valorPrincipal" header="Principal" body-class="text-right" style="min-width: 100px">
|
|
<template #body="{ data }">{{ formatCurrency(data.valorPrincipal) }}</template>
|
|
</Column>
|
|
<Column field="valorMulta" header="Multa" body-class="text-right" style="min-width: 90px">
|
|
<template #body="{ data }">{{ formatCurrency(data.valorMulta) }}</template>
|
|
</Column>
|
|
<Column field="valorJuros" header="Juros" body-class="text-right" style="min-width: 90px">
|
|
<template #body="{ data }">{{ formatCurrency(data.valorJuros) }}</template>
|
|
</Column>
|
|
<Column field="valorTotal" header="Total" body-class="text-right" style="min-width: 100px">
|
|
<template #body="{ data }">
|
|
<strong>{{ formatCurrency(data.valorTotal) }}</strong>
|
|
</template>
|
|
</Column>
|
|
<Column header="" style="width: 3rem">
|
|
<template #body="{ data }">
|
|
<Button
|
|
icon="pi pi-search"
|
|
text
|
|
rounded
|
|
size="small"
|
|
@click="modalTransacoes?.abrir(data.idContaCorrente)"
|
|
/>
|
|
</template>
|
|
</Column>
|
|
</DataTable>
|
|
</AccordionContent>
|
|
</AccordionPanel>
|
|
</Accordion>
|
|
|
|
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 space-y-4">
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate-500 uppercase mb-1.5">Vencimento da Guia</label>
|
|
<DatePicker
|
|
v-model="dataVencimento"
|
|
show-icon
|
|
:min-date="new Date()"
|
|
date-format="dd/mm/yy"
|
|
class="w-full"
|
|
size="small"
|
|
/>
|
|
<p v-if="erros.dataVencimento" class="text-xs text-red-500 mt-1">{{ erros.dataVencimento }}</p>
|
|
</div>
|
|
<div class="sm:col-span-3 grid grid-cols-2 sm:grid-cols-4 gap-3 items-end">
|
|
<div>
|
|
<p class="text-xs text-slate-500">Principal</p>
|
|
<p class="font-bold">{{ formatCurrency(totalizadores.principal) }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-slate-500">Multa</p>
|
|
<p class="font-bold">{{ formatCurrency(totalizadores.multa) }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-slate-500">Juros</p>
|
|
<p class="font-bold">{{ formatCurrency(totalizadores.juros) }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-slate-500">Total a Pagar</p>
|
|
<p class="font-bold text-primary">{{ formatCurrency(totalizadores.valorTotal) }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2 justify-between">
|
|
<div class="flex flex-wrap gap-2">
|
|
<Button
|
|
label="Imprimir Extrato (todos)"
|
|
icon="pi pi-print"
|
|
size="small"
|
|
severity="secondary"
|
|
:loading="isLoadingExtrato"
|
|
@click="gerarExtratoPdf(false)"
|
|
/>
|
|
<Button
|
|
v-if="temSelecionado"
|
|
label="Imprimir Selecionados"
|
|
icon="pi pi-print"
|
|
size="small"
|
|
severity="secondary"
|
|
outlined
|
|
:loading="isLoadingExtrato"
|
|
@click="gerarExtratoPdf(true)"
|
|
/>
|
|
</div>
|
|
<Button
|
|
v-if="temDebitoSelecionado"
|
|
label="Gerar Guia de Pagamento"
|
|
icon="pi pi-file-pdf"
|
|
size="small"
|
|
:loading="isLoadingGuia"
|
|
@click="gerarGuia"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<ModalTransacoesContaCorrente ref="modalTransacoes" />
|
|
</div>
|
|
</template>
|