794 lines
24 KiB
TypeScript
794 lines
24 KiB
TypeScript
import { Component, OnDestroy, OnInit } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { HttpErrorResponse } from '@angular/common/http';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { Subscription, firstValueFrom } from 'rxjs';
|
|
import { VigenciaService, VigenciaRow, VigenciaClientGroup, PagedResult, UpdateVigenciaRequest } from '../../services/vigencia.service';
|
|
import { CustomSelectComponent } from '../../components/custom-select/custom-select';
|
|
import { AuthService } from '../../services/auth.service';
|
|
import { LinesService, MobileLineDetail } from '../../services/lines.service';
|
|
import { PlanAutoFillService } from '../../services/plan-autofill.service';
|
|
import { TableExportService } from '../../services/table-export.service';
|
|
import { ImportPageTemplateService } from '../../services/import-page-template.service';
|
|
import { confirmDeletionWithTyping } from '../../utils/destructive-confirmation';
|
|
import { computeTotalPages } from '../../utils/pagination.util';
|
|
import { VigenciaModalsComponent } from '../../components/page-modals/vigencia-modals/vigencia-modals';
|
|
|
|
type SortDir = 'asc' | 'desc';
|
|
type ToastType = 'success' | 'danger';
|
|
type ViewMode = 'lines' | 'groups';
|
|
|
|
interface LineOptionDto {
|
|
id: string;
|
|
item: number;
|
|
linha: string | null;
|
|
usuario: string | null;
|
|
label?: string;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-vigencia',
|
|
standalone: true,
|
|
imports: [CommonModule, FormsModule, CustomSelectComponent, VigenciaModalsComponent],
|
|
templateUrl: './vigencia.html',
|
|
styleUrls: ['./vigencia.scss'],
|
|
})
|
|
export class VigenciaComponent implements OnInit, OnDestroy {
|
|
readonly vm = this;
|
|
loading = false;
|
|
exporting = false;
|
|
exportingTemplate = false;
|
|
errorMsg = '';
|
|
|
|
// Filtros
|
|
search = '';
|
|
client = '';
|
|
clients: string[] = [];
|
|
|
|
// Paginação
|
|
page = 1;
|
|
pageSize = 10;
|
|
pageSizeOptions = [10, 20, 50, 100];
|
|
total = 0;
|
|
|
|
// Ordenação
|
|
sortBy = 'cliente';
|
|
sortDir: SortDir = 'asc';
|
|
|
|
// PADRÃO: GROUPS
|
|
viewMode: ViewMode = 'groups';
|
|
|
|
// Dados
|
|
groups: VigenciaClientGroup[] = [];
|
|
rows: VigenciaRow[] = [];
|
|
|
|
// === KPIs GERAIS (Vindos do Backend) ===
|
|
kpiTotalClientes = 0;
|
|
kpiTotalLinhas = 0;
|
|
kpiTotalVencidos = 0;
|
|
|
|
// === ACORDEÃO ===
|
|
expandedGroup: string | null = null;
|
|
expandedLoading = false;
|
|
groupRows: VigenciaRow[] = [];
|
|
|
|
// UI
|
|
detailsOpen = false;
|
|
selectedRow: VigenciaRow | null = null;
|
|
editOpen = false;
|
|
editSaving = false;
|
|
editModel: VigenciaRow | null = null;
|
|
editEfetivacao = '';
|
|
editTermino = '';
|
|
editingId: string | null = null;
|
|
deleteOpen = false;
|
|
deleteTarget: VigenciaRow | null = null;
|
|
|
|
createOpen = false;
|
|
createSaving = false;
|
|
createModel: any = {
|
|
selectedClient: '',
|
|
mobileLineId: '',
|
|
item: '',
|
|
conta: '',
|
|
linha: '',
|
|
cliente: '',
|
|
usuario: '',
|
|
planoContrato: '',
|
|
total: null
|
|
};
|
|
createEfetivacao = '';
|
|
createTermino = '';
|
|
|
|
lineOptionsCreate: LineOptionDto[] = [];
|
|
createClientsLoading = false;
|
|
createLinesLoading = false;
|
|
clientsFromGeral: string[] = [];
|
|
planOptions: string[] = [];
|
|
|
|
isSysAdmin = false;
|
|
toastOpen = false;
|
|
toastMessage = '';
|
|
toastType: ToastType = 'success';
|
|
private toastTimer: any = null;
|
|
private searchTimer: any = null;
|
|
private readonly subs = new Subscription();
|
|
|
|
constructor(
|
|
private vigenciaService: VigenciaService,
|
|
private authService: AuthService,
|
|
private linesService: LinesService,
|
|
private planAutoFill: PlanAutoFillService,
|
|
private route: ActivatedRoute,
|
|
private tableExportService: TableExportService,
|
|
private importPageTemplateService: ImportPageTemplateService
|
|
) {}
|
|
|
|
ngOnInit(): void {
|
|
this.isSysAdmin = this.authService.hasRole('sysadmin');
|
|
this.loadClients();
|
|
this.loadPlanRules();
|
|
this.fetch(1);
|
|
this.bindOpenFromNotificationQuery();
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
if (this.searchTimer) clearTimeout(this.searchTimer);
|
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
this.subs.unsubscribe();
|
|
}
|
|
|
|
setView(mode: ViewMode): void {
|
|
if (this.viewMode === mode) return;
|
|
this.viewMode = mode;
|
|
this.page = 1;
|
|
this.expandedGroup = null;
|
|
this.groupRows = [];
|
|
this.sortBy = mode === 'groups' ? 'cliente' : 'item';
|
|
this.fetch(1);
|
|
}
|
|
|
|
loadClients(): void {
|
|
this.vigenciaService.getClients().subscribe({
|
|
next: (list) => (this.clients = list ?? []),
|
|
error: () => (this.clients = []),
|
|
});
|
|
}
|
|
|
|
private async loadPlanRules() {
|
|
try {
|
|
await this.planAutoFill.load();
|
|
this.planOptions = this.planAutoFill.getPlanOptions();
|
|
} catch {
|
|
this.planOptions = [];
|
|
}
|
|
}
|
|
|
|
get totalPages(): number {
|
|
return computeTotalPages(this.total || 0, this.pageSize || 10);
|
|
}
|
|
|
|
fetch(goToPage?: number): void {
|
|
if (goToPage) this.page = goToPage;
|
|
this.loading = true;
|
|
this.errorMsg = '';
|
|
|
|
if(goToPage && goToPage !== this.page) this.expandedGroup = null;
|
|
|
|
if (this.viewMode === 'groups') {
|
|
this.fetchGroups();
|
|
} else {
|
|
this.fetchLines();
|
|
}
|
|
}
|
|
|
|
private fetchGroups() {
|
|
this.vigenciaService.getGroups({
|
|
search: this.search?.trim(),
|
|
page: this.page,
|
|
pageSize: this.pageSize,
|
|
sortBy: this.sortBy,
|
|
sortDir: this.sortDir,
|
|
}).subscribe({
|
|
next: (res) => {
|
|
// ✅ Preenche Lista
|
|
this.groups = res.data.items || [];
|
|
this.total = res.data.total || 0;
|
|
|
|
// ✅ Preenche KPIs Globais
|
|
this.kpiTotalClientes = res.kpis.totalClientes;
|
|
this.kpiTotalLinhas = res.kpis.totalLinhas;
|
|
this.kpiTotalVencidos = res.kpis.totalVencidos;
|
|
|
|
this.loading = false;
|
|
},
|
|
error: (err) => this.handleError(err, 'Erro ao carregar clientes.'),
|
|
});
|
|
}
|
|
|
|
private fetchLines() {
|
|
this.vigenciaService.getVigencia({
|
|
search: this.search?.trim(),
|
|
client: this.client?.trim(),
|
|
page: this.page,
|
|
pageSize: this.pageSize,
|
|
sortBy: this.sortBy,
|
|
sortDir: this.sortDir,
|
|
}).subscribe({
|
|
next: (res) => {
|
|
this.rows = res.items || [];
|
|
this.total = res.total || 0;
|
|
this.loading = false;
|
|
},
|
|
error: (err) => this.handleError(err, 'Erro ao carregar linhas.'),
|
|
});
|
|
}
|
|
|
|
toggleGroup(g: VigenciaClientGroup): void {
|
|
if (this.expandedGroup === g.cliente) {
|
|
this.expandedGroup = null;
|
|
this.groupRows = [];
|
|
return;
|
|
}
|
|
|
|
this.expandedGroup = g.cliente;
|
|
this.expandedLoading = true;
|
|
this.groupRows = [];
|
|
|
|
this.vigenciaService.getVigencia({
|
|
client: g.cliente,
|
|
page: 1,
|
|
pageSize: 200,
|
|
sortBy: 'item',
|
|
sortDir: 'asc'
|
|
}).subscribe({
|
|
next: (res) => {
|
|
this.groupRows = res.items || [];
|
|
this.expandedLoading = false;
|
|
},
|
|
error: () => {
|
|
this.showToast('Erro ao carregar detalhes do cliente.', 'danger');
|
|
this.expandedLoading = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
public isVencido(dateValue: any): boolean {
|
|
if(!dateValue) return false;
|
|
const d = this.parseAnyDate(dateValue);
|
|
if(!d) return false;
|
|
return this.startOfDay(d) < this.startOfDay(new Date());
|
|
}
|
|
|
|
public isAtivo(dateValue: any): boolean {
|
|
if(!dateValue) return true;
|
|
const d = this.parseAnyDate(dateValue);
|
|
if(!d) return true;
|
|
return this.startOfDay(d) >= this.startOfDay(new Date());
|
|
}
|
|
|
|
public isAVencer(dateValue: any): boolean {
|
|
if (!dateValue) return false;
|
|
const d = this.parseAnyDate(dateValue);
|
|
if (!d) return false;
|
|
const today = this.startOfDay(new Date());
|
|
const end = this.startOfDay(d);
|
|
const days = Math.round((end.getTime() - today.getTime()) / (24 * 60 * 60 * 1000));
|
|
return days >= 0 && days <= 30;
|
|
}
|
|
|
|
getRenewalBadge(row: VigenciaRow): string {
|
|
if (!row.autoRenewYears) return '';
|
|
return `Auto +${row.autoRenewYears} ano(s)`;
|
|
}
|
|
|
|
public parseAnyDate(value: any): Date | null {
|
|
if (!value) return null;
|
|
const d = new Date(value);
|
|
return isNaN(d.getTime()) ? null : d;
|
|
}
|
|
|
|
public startOfDay(d: Date): Date {
|
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
}
|
|
|
|
onSearchChange() {
|
|
if (this.searchTimer) clearTimeout(this.searchTimer);
|
|
this.searchTimer = setTimeout(() => this.fetch(1), 300);
|
|
}
|
|
|
|
clearFilters() {
|
|
this.search = '';
|
|
if (this.searchTimer) clearTimeout(this.searchTimer);
|
|
this.fetch(1);
|
|
}
|
|
|
|
async onExport(): Promise<void> {
|
|
if (this.exporting) return;
|
|
this.exporting = true;
|
|
|
|
try {
|
|
const baseRows = await this.fetchAllRowsForExport();
|
|
const rows = await this.fetchDetailedRowsForExport(baseRows);
|
|
if (!rows.length) {
|
|
this.showToast('Nenhum registro encontrado para exportar.', 'danger');
|
|
return;
|
|
}
|
|
|
|
const timestamp = this.tableExportService.buildTimestamp();
|
|
await this.tableExportService.exportAsXlsx<VigenciaRow>({
|
|
fileName: `vigencia_${timestamp}`,
|
|
sheetName: 'Vigencia',
|
|
rows,
|
|
columns: [
|
|
{ header: 'ID', value: (row) => row.id ?? '' },
|
|
{ header: 'Item', type: 'number', value: (row) => this.toNullableNumber(row.item) ?? 0 },
|
|
{ header: 'Linha', value: (row) => row.linha ?? '' },
|
|
{ header: 'Conta', value: (row) => row.conta ?? '' },
|
|
{ header: 'Cliente', value: (row) => row.cliente ?? '' },
|
|
{ header: 'Usuario', value: (row) => row.usuario ?? '' },
|
|
{ header: 'Plano', value: (row) => row.planoContrato ?? '' },
|
|
{ header: 'Efetivacao', type: 'date', value: (row) => row.dtEfetivacaoServico ?? '' },
|
|
{ header: 'Termino Fidelizacao', type: 'date', value: (row) => row.dtTerminoFidelizacao ?? '' },
|
|
{ header: 'Status', value: (row) => (this.isVencido(row.dtTerminoFidelizacao) ? 'Vencido' : 'Ativo') },
|
|
{ header: 'Auto Renovacao (anos)', type: 'number', value: (row) => this.toNullableNumber(row.autoRenewYears) ?? 0 },
|
|
{ header: 'Auto Renovacao Referencia', type: 'date', value: (row) => row.autoRenewReferenceEndDate ?? '' },
|
|
{ header: 'Auto Renovacao Configurada Em', type: 'datetime', value: (row) => row.autoRenewConfiguredAt ?? '' },
|
|
{ header: 'Ultima Auto Renovacao', type: 'datetime', value: (row) => row.lastAutoRenewedAt ?? '' },
|
|
{ header: 'Total', type: 'currency', value: (row) => this.toNullableNumber(row.total) ?? 0 },
|
|
{ header: 'Criado Em', type: 'datetime', value: (row) => row.createdAt ?? '' },
|
|
{ header: 'Atualizado Em', type: 'datetime', value: (row) => row.updatedAt ?? '' },
|
|
],
|
|
});
|
|
|
|
this.showToast(`Planilha exportada com ${rows.length} registro(s).`, 'success');
|
|
} catch {
|
|
this.showToast('Erro ao exportar planilha.', 'danger');
|
|
} finally {
|
|
this.exporting = false;
|
|
}
|
|
}
|
|
|
|
async onExportTemplate(): Promise<void> {
|
|
if (this.exportingTemplate) return;
|
|
this.exportingTemplate = true;
|
|
|
|
try {
|
|
await this.importPageTemplateService.exportVigenciaTemplate();
|
|
this.showToast('Modelo da página exportado.', 'success');
|
|
} catch {
|
|
this.showToast('Erro ao exportar o modelo da página.', 'danger');
|
|
} finally {
|
|
this.exportingTemplate = false;
|
|
}
|
|
}
|
|
|
|
private async fetchAllRowsForExport(): Promise<VigenciaRow[]> {
|
|
const pageSize = 500;
|
|
let page = 1;
|
|
let expectedTotal = 0;
|
|
const all: VigenciaRow[] = [];
|
|
|
|
while (page <= 500) {
|
|
const response = await firstValueFrom(
|
|
this.vigenciaService.getVigencia({
|
|
search: this.search?.trim(),
|
|
client: this.client?.trim(),
|
|
page,
|
|
pageSize,
|
|
sortBy: 'item',
|
|
sortDir: 'asc',
|
|
})
|
|
);
|
|
|
|
const items = response?.items ?? [];
|
|
expectedTotal = response?.total ?? 0;
|
|
all.push(...items);
|
|
|
|
if (items.length === 0) break;
|
|
if (items.length < pageSize) break;
|
|
if (expectedTotal > 0 && all.length >= expectedTotal) break;
|
|
page += 1;
|
|
}
|
|
|
|
return all;
|
|
}
|
|
|
|
private async fetchDetailedRowsForExport(rows: VigenciaRow[]): Promise<VigenciaRow[]> {
|
|
if (!rows.length) return [];
|
|
|
|
const detailedRows: VigenciaRow[] = [];
|
|
const chunkSize = 10;
|
|
|
|
for (let i = 0; i < rows.length; i += chunkSize) {
|
|
const chunk = rows.slice(i, i + chunkSize);
|
|
const resolved = await Promise.all(
|
|
chunk.map(async (row) => {
|
|
try {
|
|
return await firstValueFrom(this.vigenciaService.getById(row.id));
|
|
} catch {
|
|
return row;
|
|
}
|
|
})
|
|
);
|
|
|
|
detailedRows.push(...resolved);
|
|
}
|
|
|
|
return detailedRows;
|
|
}
|
|
|
|
scheduleAutoRenew(row: VigenciaRow): void {
|
|
if (!row?.id) return;
|
|
const years = 2;
|
|
|
|
this.vigenciaService.configureAutoRenew(row.id, { years }).subscribe({
|
|
next: () => {
|
|
row.autoRenewYears = years;
|
|
row.autoRenewReferenceEndDate = row.dtTerminoFidelizacao;
|
|
row.autoRenewConfiguredAt = new Date().toISOString();
|
|
this.showToast(`Renovação automática (+${years} ano${years > 1 ? 's' : ''}) programada.`, 'success');
|
|
},
|
|
error: () => this.showToast('Não foi possível programar a renovação automática.', 'danger')
|
|
});
|
|
}
|
|
|
|
openDetails(r: VigenciaRow) { this.selectedRow = r; this.detailsOpen = true; }
|
|
closeDetails() { this.detailsOpen = false; }
|
|
|
|
openEdit(r: VigenciaRow) {
|
|
if (!this.isSysAdmin) return;
|
|
this.editingId = r.id;
|
|
this.editModel = { ...r };
|
|
this.editEfetivacao = this.toDateInput(r.dtEfetivacaoServico);
|
|
this.editTermino = this.toDateInput(r.dtTerminoFidelizacao);
|
|
this.editOpen = true;
|
|
}
|
|
|
|
closeEdit() {
|
|
this.editOpen = false;
|
|
this.editSaving = false;
|
|
this.editModel = null;
|
|
this.editEfetivacao = '';
|
|
this.editTermino = '';
|
|
this.editingId = null;
|
|
}
|
|
|
|
saveEdit() {
|
|
if (!this.editModel || !this.editingId) return;
|
|
this.editSaving = true;
|
|
|
|
const payload: UpdateVigenciaRequest = {
|
|
item: this.toNullableNumber(this.editModel.item),
|
|
conta: this.editModel.conta,
|
|
linha: this.editModel.linha,
|
|
cliente: this.editModel.cliente,
|
|
usuario: this.editModel.usuario,
|
|
planoContrato: this.editModel.planoContrato,
|
|
dtEfetivacaoServico: this.dateInputToIso(this.editEfetivacao),
|
|
dtTerminoFidelizacao: this.dateInputToIso(this.editTermino),
|
|
total: this.toNullableNumber(this.editModel.total)
|
|
};
|
|
|
|
this.vigenciaService.update(this.editingId, payload).subscribe({
|
|
next: () => {
|
|
this.editSaving = false;
|
|
this.closeEdit();
|
|
this.fetch();
|
|
this.showToast('Registro atualizado!', 'success');
|
|
},
|
|
error: () => {
|
|
this.editSaving = false;
|
|
this.showToast('Erro ao salvar.', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
// ==========================
|
|
// CREATE
|
|
// ==========================
|
|
openCreate() {
|
|
if (!this.isSysAdmin) return;
|
|
this.resetCreateModel();
|
|
this.createOpen = true;
|
|
this.preloadGeralClients();
|
|
}
|
|
|
|
closeCreate() {
|
|
this.createOpen = false;
|
|
this.createSaving = false;
|
|
this.createModel = null;
|
|
}
|
|
|
|
private resetCreateModel() {
|
|
this.createModel = {
|
|
selectedClient: '',
|
|
mobileLineId: '',
|
|
item: '',
|
|
conta: '',
|
|
linha: '',
|
|
cliente: '',
|
|
usuario: '',
|
|
planoContrato: '',
|
|
total: null
|
|
};
|
|
this.createEfetivacao = '';
|
|
this.createTermino = '';
|
|
this.lineOptionsCreate = [];
|
|
this.createLinesLoading = false;
|
|
this.createClientsLoading = false;
|
|
}
|
|
|
|
private preloadGeralClients() {
|
|
this.createClientsLoading = true;
|
|
this.linesService.getClients().subscribe({
|
|
next: (list) => {
|
|
this.clientsFromGeral = list ?? [];
|
|
this.createClientsLoading = false;
|
|
},
|
|
error: () => {
|
|
this.clientsFromGeral = [];
|
|
this.createClientsLoading = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
onCreateClientChange() {
|
|
const c = (this.createModel.selectedClient ?? '').trim();
|
|
this.createModel.mobileLineId = '';
|
|
this.createModel.linha = '';
|
|
this.createModel.conta = '';
|
|
this.createModel.usuario = '';
|
|
this.createModel.planoContrato = '';
|
|
this.createModel.total = null;
|
|
this.createModel.cliente = c;
|
|
this.lineOptionsCreate = [];
|
|
|
|
if (c) this.loadLinesForClient(c);
|
|
}
|
|
|
|
private loadLinesForClient(cliente: string) {
|
|
const c = (cliente ?? '').trim();
|
|
if (!c) return;
|
|
|
|
this.createLinesLoading = true;
|
|
this.linesService.getLinesByClient(c).subscribe({
|
|
next: (items: any[]) => {
|
|
const mapped: LineOptionDto[] = (items ?? [])
|
|
.filter(x => !!String(x?.id ?? '').trim())
|
|
.map(x => ({
|
|
id: String(x.id),
|
|
item: Number(x.item ?? 0),
|
|
linha: x.linha ?? null,
|
|
usuario: x.usuario ?? null,
|
|
label: `${x.item ?? ''} • ${x.linha ?? '-'} • ${x.usuario ?? 'SEM USUÁRIO'}`
|
|
}))
|
|
.filter(x => !!String(x.linha ?? '').trim());
|
|
|
|
this.lineOptionsCreate = mapped;
|
|
this.createLinesLoading = false;
|
|
},
|
|
error: () => {
|
|
this.lineOptionsCreate = [];
|
|
this.createLinesLoading = false;
|
|
this.showToast('Erro ao carregar linhas da GERAL.', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
onCreateLineChange() {
|
|
const id = String(this.createModel.mobileLineId ?? '').trim();
|
|
if (!id) return;
|
|
|
|
this.linesService.getById(id).subscribe({
|
|
next: (d: MobileLineDetail) => this.applyLineDetailToCreate(d),
|
|
error: () => this.showToast('Erro ao carregar dados da linha.', 'danger')
|
|
});
|
|
}
|
|
|
|
private applyLineDetailToCreate(d: MobileLineDetail) {
|
|
this.createModel.linha = d.linha ?? '';
|
|
this.createModel.conta = d.conta ?? '';
|
|
this.createModel.cliente = d.cliente ?? this.createModel.cliente ?? '';
|
|
this.createModel.usuario = d.usuario ?? '';
|
|
this.createModel.planoContrato = d.planoContrato ?? '';
|
|
this.createEfetivacao = this.toDateInput(d.dtEfetivacaoServico ?? null);
|
|
this.createTermino = this.toDateInput(d.dtTerminoFidelizacao ?? null);
|
|
|
|
this.ensurePlanOption(this.createModel.planoContrato);
|
|
|
|
if (!String(this.createModel.item ?? '').trim() && d.item) {
|
|
this.createModel.item = String(d.item);
|
|
}
|
|
|
|
this.onCreatePlanChange();
|
|
}
|
|
|
|
onCreatePlanChange() {
|
|
this.ensurePlanOption(this.createModel?.planoContrato);
|
|
this.applyPlanSuggestion(this.createModel);
|
|
}
|
|
|
|
onEditPlanChange() {
|
|
if (!this.editModel) return;
|
|
this.ensurePlanOption(this.editModel?.planoContrato);
|
|
this.applyPlanSuggestion(this.editModel);
|
|
}
|
|
|
|
private applyPlanSuggestion(model: any) {
|
|
const plan = (model?.planoContrato ?? '').toString().trim();
|
|
if (!plan) return;
|
|
|
|
const suggestion = this.planAutoFill.suggest(plan);
|
|
if (!suggestion) return;
|
|
|
|
if (suggestion.valorPlano != null) {
|
|
model.total = suggestion.valorPlano;
|
|
}
|
|
}
|
|
|
|
private ensurePlanOption(plan: any) {
|
|
const p = (plan ?? '').toString().trim();
|
|
if (!p) return;
|
|
if (!this.planOptions.includes(p)) {
|
|
this.planOptions = [p, ...this.planOptions];
|
|
}
|
|
}
|
|
|
|
saveCreate() {
|
|
if (!this.createModel) return;
|
|
this.applyPlanSuggestion(this.createModel);
|
|
|
|
const payload = {
|
|
item: this.toNullableNumber(this.createModel.item),
|
|
conta: this.createModel.conta,
|
|
linha: this.createModel.linha,
|
|
cliente: this.createModel.cliente,
|
|
usuario: this.createModel.usuario,
|
|
planoContrato: this.createModel.planoContrato,
|
|
dtEfetivacaoServico: this.dateInputToIso(this.createEfetivacao),
|
|
dtTerminoFidelizacao: this.dateInputToIso(this.createTermino),
|
|
total: this.toNullableNumber(this.createModel.total)
|
|
};
|
|
|
|
this.createSaving = true;
|
|
this.vigenciaService.create(payload).subscribe({
|
|
next: () => {
|
|
this.createSaving = false;
|
|
this.closeCreate();
|
|
this.fetch();
|
|
this.showToast('Vigência criada com sucesso!', 'success');
|
|
},
|
|
error: () => {
|
|
this.createSaving = false;
|
|
this.showToast('Erro ao criar vigência.', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
openDelete(r: VigenciaRow) {
|
|
if (!this.isSysAdmin) return;
|
|
this.deleteTarget = r;
|
|
this.deleteOpen = true;
|
|
}
|
|
|
|
cancelDelete() {
|
|
this.deleteOpen = false;
|
|
this.deleteTarget = null;
|
|
}
|
|
|
|
async confirmDelete() {
|
|
if (!this.deleteTarget) return;
|
|
if (!(await confirmDeletionWithTyping('este registro de vigência'))) return;
|
|
const id = this.deleteTarget.id;
|
|
this.vigenciaService.remove(id).subscribe({
|
|
next: () => {
|
|
this.deleteOpen = false;
|
|
this.deleteTarget = null;
|
|
this.fetch();
|
|
this.showToast('Registro removido.', 'success');
|
|
},
|
|
error: () => {
|
|
this.deleteOpen = false;
|
|
this.deleteTarget = null;
|
|
this.showToast('Erro ao remover.', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
private toDateInput(value: string | null): string {
|
|
if (!value) return '';
|
|
const d = new Date(value);
|
|
if (isNaN(d.getTime())) return '';
|
|
return d.toISOString().slice(0, 10);
|
|
}
|
|
|
|
private dateInputToIso(value: string): string | null {
|
|
if (!value) return null;
|
|
const d = new Date(`${value}T00:00:00`);
|
|
if (isNaN(d.getTime())) return null;
|
|
return d.toISOString();
|
|
}
|
|
|
|
private toNullableNumber(value: any): number | null {
|
|
if (value === undefined || value === null || value === '') return null;
|
|
const n = Number(value);
|
|
return Number.isNaN(n) ? null : n;
|
|
}
|
|
|
|
private bindOpenFromNotificationQuery(): void {
|
|
this.subs.add(
|
|
this.route.queryParamMap.subscribe((params) => {
|
|
const lineId = (params.get('lineId') ?? '').trim();
|
|
const linha = (params.get('linha') ?? '').trim();
|
|
if (!lineId && !linha) return;
|
|
|
|
const openMode = (params.get('open') ?? 'edit').trim().toLowerCase();
|
|
if (lineId) {
|
|
this.openVigenciaLineById(lineId, openMode);
|
|
} else if (linha) {
|
|
this.openVigenciaLineByNumber(linha, openMode);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
private openVigenciaLineById(lineId: string, openMode: string): void {
|
|
this.vigenciaService.getById(lineId).subscribe({
|
|
next: (row) => {
|
|
if (this.isSysAdmin && openMode !== 'details') {
|
|
this.openEdit(row);
|
|
return;
|
|
}
|
|
this.openDetails(row);
|
|
},
|
|
error: () => this.showToast('Não foi possível abrir a linha da vigência pela notificação.', 'danger')
|
|
});
|
|
}
|
|
|
|
private openVigenciaLineByNumber(linha: string, openMode: string): void {
|
|
const onlyDigits = (linha || '').replace(/\D/g, '');
|
|
const lookup = onlyDigits || linha;
|
|
if (!lookup) return;
|
|
|
|
this.vigenciaService.getVigencia({
|
|
search: lookup,
|
|
page: 1,
|
|
pageSize: 20,
|
|
sortBy: 'item',
|
|
sortDir: 'asc'
|
|
}).subscribe({
|
|
next: (res) => {
|
|
const rows = res?.items ?? [];
|
|
const match = rows.find(r => (r.linha ?? '').replace(/\D/g, '') === onlyDigits) ?? rows[0];
|
|
if (!match) {
|
|
this.showToast('Linha da notificação não encontrada na vigência.', 'danger');
|
|
return;
|
|
}
|
|
|
|
if (this.isSysAdmin && openMode !== 'details') {
|
|
this.openEdit(match);
|
|
return;
|
|
}
|
|
this.openDetails(match);
|
|
},
|
|
error: () => this.showToast('Não foi possível localizar a linha da notificação na vigência.', 'danger')
|
|
});
|
|
}
|
|
|
|
handleError(err: HttpErrorResponse, msg: string) {
|
|
this.loading = false;
|
|
this.expandedLoading = false;
|
|
this.errorMsg = (err.error as any)?.message || msg;
|
|
this.showToast(msg, 'danger');
|
|
}
|
|
|
|
showToast(msg: string, type: ToastType) {
|
|
this.toastMessage = msg; this.toastType = type; this.toastOpen = true;
|
|
if(this.toastTimer) clearTimeout(this.toastTimer);
|
|
this.toastTimer = setTimeout(() => this.toastOpen = false, 3000);
|
|
}
|
|
hideToast() { this.toastOpen = false; }
|
|
}
|