import type { Lancamento } from "@/db/schema"; import type { EligibleInstallment } from "./anticipation-types"; /** * Calcula o valor total de antecipação baseado nas parcelas selecionadas */ export function calculateTotalAnticipationAmount( installments: EligibleInstallment[], ): number { return installments.reduce((sum, inst) => sum + Number(inst.amount), 0); } /** * Valida se o período de antecipação é válido * O período não pode ser anterior ao período da primeira parcela selecionada */ export function validateAnticipationPeriod( period: string, installments: EligibleInstallment[], ): boolean { if (installments.length === 0) return false; const earliestPeriod = installments.reduce((earliest, inst) => { return inst.period < earliest ? inst.period : earliest; }, installments[0].period); return period >= earliestPeriod; } /** * Formata os números das parcelas antecipadas em uma string legível * Exemplo: "1, 2, 3" ou "5, 6, 7, 8" */ export function getAnticipatedInstallmentNumbers( installments: EligibleInstallment[], ): string { const numbers = installments .map((inst) => inst.currentInstallment) .filter((num): num is number => num !== null) .sort((a, b) => a - b) .join(", "); return numbers; } /** * Formata o resumo de parcelas antecipadas * Exemplo: "Parcelas 1-3 de 12" ou "Parcela 5 de 12" */ export function formatAnticipatedInstallmentsRange( installments: EligibleInstallment[], ): string { const numbers = installments .map((inst) => inst.currentInstallment) .filter((num): num is number => num !== null) .sort((a, b) => a - b); if (numbers.length === 0) return ""; if (numbers.length === 1) { const total = installments[0]?.installmentCount ?? 0; return `Parcela ${numbers[0]} de ${total}`; } const first = numbers[0]; const last = numbers[numbers.length - 1]; const total = installments[0]?.installmentCount ?? 0; // Se as parcelas são consecutivas const isConsecutive = numbers.every((num, i) => { if (i === 0) return true; return num === numbers[i - 1]! + 1; }); if (isConsecutive) { return `Parcelas ${first}-${last} de ${total}`; } else { return `${numbers.length} parcelas de ${total}`; } } /** * Verifica se uma antecipação pode ser cancelada * Só pode cancelar se o lançamento de antecipação não foi pago */ export function canCancelAnticipation(lancamento: Lancamento): boolean { return lancamento.isSettled !== true; } /** * Ordena parcelas por número da parcela atual */ export function sortInstallmentsByNumber( installments: EligibleInstallment[], ): EligibleInstallment[] { return [...installments].sort((a, b) => { const aNum = a.currentInstallment ?? 0; const bNum = b.currentInstallment ?? 0; return aNum - bNum; }); } /** * Calcula quantas parcelas restam após uma antecipação */ export function calculateRemainingInstallments( totalInstallments: number, anticipatedCount: number, ): number { return Math.max(0, totalInstallments - anticipatedCount); } /** * Valida se as parcelas selecionadas pertencem à mesma série */ export function validateInstallmentsSameSeries( installments: EligibleInstallment[], _seriesId: string, ): boolean { // Esta validação será feita no servidor com os dados completos // Aqui apenas retorna true como placeholder return installments.length > 0; } /** * Gera descrição automática para o lançamento de antecipação */ export function generateAnticipationDescription( lancamentoName: string, installmentCount: number, ): string { return `Antecipação de ${installmentCount} ${ installmentCount === 1 ? "parcela" : "parcelas" } - ${lancamentoName}`; } /** * Formata nota automática para antecipação */ export function generateAnticipationNote( installments: EligibleInstallment[], ): string { const range = formatAnticipatedInstallmentsRange(installments); return `Antecipação: ${range}`; }