webmoney/backend/app/Console/Commands/GenerateDemoInstallments.php
marcoitaloesp-ai 9c9d6443e7
v1.57.0: Redesign category modals + i18n updates + demo transactions fix
- Redesigned category create/edit modal with elegant wizard-style UI
- Redesigned batch categorization modal with visual cards and better preview
- Added missing i18n translations (common.continue, creating, remove)
- Added budgets.general and wizard translations for ES, PT-BR, EN
- Fixed 3 demo user transactions that were missing categories
2025-12-18 19:06:07 +00:00

120 lines
4.1 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\LiabilityAccount;
use App\Models\LiabilityInstallment;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class GenerateDemoInstallments extends Command
{
protected $signature = 'demo:generate-installments';
protected $description = 'Gerar parcelas de exemplo para passivos DEMO que não têm parcelas';
public function handle(): int
{
$this->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();
}
}