import type { LancamentoItem } from "@/components/lancamentos/types"; import { getTodayDateString } from "@/lib/utils/date"; import { derivePeriodFromDate } from "@/lib/utils/period"; import { LANCAMENTO_CONDITIONS, LANCAMENTO_PAYMENT_METHODS, LANCAMENTO_TRANSACTION_TYPES, } from "./constants"; /** * Split type for dividing transactions between payers */ export type SplitType = "equal" | "60-40" | "70-30" | "80-20" | "custom"; /** * Form state type for lancamento dialog */ export type LancamentoFormState = { purchaseDate: string; period: string; name: string; transactionType: string; amount: string; condition: string; paymentMethod: string; pagadorId: string | undefined; secondaryPagadorId: string | undefined; isSplit: boolean; splitType: SplitType; primarySplitAmount: string; secondarySplitAmount: string; contaId: string | undefined; cartaoId: string | undefined; categoriaId: string | undefined; installmentCount: string; recurrenceCount: string; dueDate: string; boletoPaymentDate: string; note: string; isSettled: boolean | null; }; /** * Initial state overrides for lancamento form */ export type LancamentoFormOverrides = { defaultCartaoId?: string | null; defaultPaymentMethod?: string | null; defaultPurchaseDate?: string | null; defaultName?: string | null; defaultAmount?: string | null; defaultTransactionType?: "Despesa" | "Receita"; isImporting?: boolean; }; /** * Builds initial form state from lancamento data and defaults */ export function buildLancamentoInitialState( lancamento?: LancamentoItem, defaultPagadorId?: string | null, preferredPeriod?: string, overrides?: LancamentoFormOverrides, ): LancamentoFormState { const purchaseDate = lancamento?.purchaseDate ? lancamento.purchaseDate.slice(0, 10) : (overrides?.defaultPurchaseDate ?? ""); const paymentMethod = lancamento?.paymentMethod ?? overrides?.defaultPaymentMethod ?? LANCAMENTO_PAYMENT_METHODS[0]; const derivedPeriod = derivePeriodFromDate(purchaseDate); const fallbackPeriod = preferredPeriod && /^\d{4}-\d{2}$/.test(preferredPeriod) ? preferredPeriod : derivedPeriod; // Quando importando, usar valores padrão do usuário logado ao invés dos valores do lançamento original const isImporting = overrides?.isImporting ?? false; const fallbackPagadorId = isImporting ? (defaultPagadorId ?? null) : (lancamento?.pagadorId ?? defaultPagadorId ?? null); const boletoPaymentDate = lancamento?.boletoPaymentDate ?? (paymentMethod === "Boleto" && (lancamento?.isSettled ?? false) ? getTodayDateString() : ""); // Calcular o valor correto para importação de parcelados let amountValue = overrides?.defaultAmount ?? ""; if (!amountValue && typeof lancamento?.amount === "number") { let baseAmount = Math.abs(lancamento.amount); // Se está importando e é parcelado, usar o valor total (parcela * quantidade) if ( isImporting && lancamento.condition === "Parcelado" && lancamento.installmentCount ) { baseAmount = baseAmount * lancamento.installmentCount; } amountValue = (Math.round(baseAmount * 100) / 100).toFixed(2); } return { purchaseDate, period: lancamento?.period && /^\d{4}-\d{2}$/.test(lancamento.period) ? lancamento.period : fallbackPeriod, name: lancamento?.name ?? overrides?.defaultName ?? "", transactionType: lancamento?.transactionType ?? overrides?.defaultTransactionType ?? LANCAMENTO_TRANSACTION_TYPES[0], amount: amountValue, condition: lancamento?.condition ?? LANCAMENTO_CONDITIONS[0], paymentMethod, pagadorId: fallbackPagadorId ?? undefined, secondaryPagadorId: undefined, isSplit: false, splitType: "equal", primarySplitAmount: "", secondarySplitAmount: "", contaId: paymentMethod === "Cartão de crédito" ? undefined : isImporting ? undefined : (lancamento?.contaId ?? undefined), cartaoId: paymentMethod === "Cartão de crédito" ? isImporting ? (overrides?.defaultCartaoId ?? undefined) : (lancamento?.cartaoId ?? overrides?.defaultCartaoId ?? undefined) : undefined, categoriaId: isImporting ? undefined : (lancamento?.categoriaId ?? undefined), installmentCount: lancamento?.installmentCount ? String(lancamento.installmentCount) : "", recurrenceCount: lancamento?.recurrenceCount ? String(lancamento.recurrenceCount) : "", dueDate: lancamento?.dueDate ?? "", boletoPaymentDate, note: lancamento?.note ?? "", isSettled: paymentMethod === "Cartão de crédito" ? null : (lancamento?.isSettled ?? true), }; } /** * Split presets with their percentages */ const SPLIT_PRESETS: Record = { equal: { primary: 50, secondary: 50 }, "60-40": { primary: 60, secondary: 40 }, "70-30": { primary: 70, secondary: 30 }, "80-20": { primary: 80, secondary: 20 }, custom: { primary: 50, secondary: 50 }, }; /** * Calculates split amounts based on total and split type */ export function calculateSplitAmounts( totalAmount: number, splitType: SplitType, ): { primary: string; secondary: string } { if (totalAmount <= 0) { return { primary: "", secondary: "" }; } const preset = SPLIT_PRESETS[splitType]; const primaryAmount = (totalAmount * preset.primary) / 100; const secondaryAmount = totalAmount - primaryAmount; return { primary: primaryAmount.toFixed(2), secondary: secondaryAmount.toFixed(2), }; } /** * Applies field dependencies when form state changes * This function encapsulates the business logic for field interdependencies */ export function applyFieldDependencies( key: keyof LancamentoFormState, value: LancamentoFormState[keyof LancamentoFormState], currentState: LancamentoFormState, _periodDirty: boolean, ): Partial { const updates: Partial = {}; // Removed automatic period update when purchase date changes // if (key === "purchaseDate" && typeof value === "string") { // if (!periodDirty) { // updates.period = derivePeriodFromDate(value); // } // } // When condition changes, clear irrelevant fields if (key === "condition" && typeof value === "string") { if (value !== "Parcelado") { updates.installmentCount = ""; } if (value !== "Recorrente") { updates.recurrenceCount = ""; } } // When payment method changes, adjust related fields if (key === "paymentMethod" && typeof value === "string") { if (value === "Cartão de crédito") { updates.contaId = undefined; updates.isSettled = null; } else { updates.cartaoId = undefined; updates.isSettled = currentState.isSettled ?? true; } // Clear boleto-specific fields if not boleto if (value !== "Boleto") { updates.dueDate = ""; updates.boletoPaymentDate = ""; } else if ( currentState.isSettled || (updates.isSettled !== null && updates.isSettled !== undefined) ) { // Set today's date for boleto payment if settled const settled = updates.isSettled ?? currentState.isSettled; if (settled) { updates.boletoPaymentDate = currentState.boletoPaymentDate || getTodayDateString(); } } } // When split is disabled, clear secondary pagador and split fields if (key === "isSplit" && value === false) { updates.secondaryPagadorId = undefined; updates.splitType = "equal"; updates.primarySplitAmount = ""; updates.secondarySplitAmount = ""; } // When split is enabled and amount exists, calculate initial split amounts if (key === "isSplit" && value === true) { const totalAmount = Number.parseFloat(currentState.amount) || 0; if (totalAmount > 0) { const half = (totalAmount / 2).toFixed(2); updates.primarySplitAmount = half; updates.secondarySplitAmount = half; } } // When amount changes and split is enabled, recalculate split amounts if (key === "amount" && typeof value === "string" && currentState.isSplit) { const totalAmount = Number.parseFloat(value) || 0; if (totalAmount > 0) { const splitAmounts = calculateSplitAmounts( totalAmount, currentState.splitType, ); updates.primarySplitAmount = splitAmounts.primary; updates.secondarySplitAmount = splitAmounts.secondary; } else { updates.primarySplitAmount = ""; updates.secondarySplitAmount = ""; } } // When primary pagador changes, clear secondary if it matches if (key === "pagadorId" && typeof value === "string") { const secondaryValue = currentState.secondaryPagadorId; if (secondaryValue && secondaryValue === value) { updates.secondaryPagadorId = undefined; } } // When isSettled changes and payment method is Boleto if (key === "isSettled" && currentState.paymentMethod === "Boleto") { if (value === true) { updates.boletoPaymentDate = currentState.boletoPaymentDate || getTodayDateString(); } else if (value === false) { updates.boletoPaymentDate = ""; } } return updates; }