info('Verificando passivos sem parcelas...'); // Buscar passivos que têm total_installments > 0 mas não têm parcelas $accounts = LiabilityAccount::withCount('installments') ->having('installments_count', '=', 0) ->where('total_installments', '>', 0) ->get(); if ($accounts->isEmpty()) { $this->info('Todos os passivos já têm parcelas geradas.'); return Command::SUCCESS; } $this->info("Encontrados {$accounts->count()} passivos sem parcelas."); DB::beginTransaction(); try { foreach ($accounts as $account) { $this->generateInstallments($account); } DB::commit(); $this->info('✓ Parcelas geradas com sucesso!'); return Command::SUCCESS; } catch (\Exception $e) { DB::rollBack(); $this->error('Erro: ' . $e->getMessage()); return Command::FAILURE; } } private function generateInstallments(LiabilityAccount $account): void { $this->info("Gerando parcelas para: {$account->name}"); $totalInstallments = $account->total_installments; $paidInstallments = $account->paid_installments ?? 0; $principal = $account->principal_amount; $annualRate = $account->annual_interest_rate ?? 0; $monthlyRate = $annualRate / 12 / 100; $startDate = $account->first_due_date ?? $account->start_date ?? now(); if (is_string($startDate)) { $startDate = Carbon::parse($startDate); } // Calcular parcela mensal (sistema PRICE) if ($monthlyRate > 0) { $pmt = $principal * ($monthlyRate * pow(1 + $monthlyRate, $totalInstallments)) / (pow(1 + $monthlyRate, $totalInstallments) - 1); } else { $pmt = $principal / $totalInstallments; } $balance = $principal; $installments = []; for ($i = 1; $i <= $totalInstallments; $i++) { $dueDate = $startDate->copy()->addMonths($i - 1); // Calcular juros e capital $interestAmount = $balance * $monthlyRate; $principalPaid = $pmt - $interestAmount; // Última parcela ajusta para zerar o saldo if ($i === $totalInstallments) { $principalPaid = $balance; $pmt = $principalPaid + $interestAmount; } $balance -= $principalPaid; // Determinar status $isPaid = $i <= $paidInstallments; $status = $isPaid ? 'paid' : ($dueDate->isPast() ? 'overdue' : 'pending'); $installments[] = [ 'liability_account_id' => $account->id, 'installment_number' => $i, 'due_date' => $dueDate->format('Y-m-d'), 'installment_amount' => round($pmt, 2), 'principal_amount' => round($principalPaid, 2), 'interest_amount' => round($interestAmount, 2), 'fee_amount' => 0, 'paid_amount' => $isPaid ? round($pmt, 2) : 0, 'paid_date' => $isPaid ? $dueDate->format('Y-m-d') : null, 'status' => $status, 'created_at' => now(), 'updated_at' => now(), ]; } // Inserir em lotes foreach (array_chunk($installments, 50) as $chunk) { LiabilityInstallment::insert($chunk); } $this->info(" ✓ {$totalInstallments} parcelas criadas"); // Recalcular totais $account->recalculateTotals(); } }