'date', 'end_date' => 'date', 'last_generated_date' => 'date', 'planned_amount' => 'decimal:2', 'is_active' => 'boolean', 'frequency_interval' => 'integer', 'day_of_month' => 'integer', 'day_of_week' => 'integer', 'max_occurrences' => 'integer', 'occurrences_generated' => 'integer', ]; /** * Frequências disponíveis */ public const FREQUENCIES = [ 'daily' => ['label' => 'Diária', 'days' => 1], 'weekly' => ['label' => 'Semanal', 'days' => 7], 'biweekly' => ['label' => 'Quinzenal', 'days' => 14], 'monthly' => ['label' => 'Mensal', 'months' => 1], 'bimonthly' => ['label' => 'Bimestral', 'months' => 2], 'quarterly' => ['label' => 'Trimestral', 'months' => 3], 'semiannual' => ['label' => 'Semestral', 'months' => 6], 'annual' => ['label' => 'Anual', 'months' => 12], ]; // ============================================ // Relacionamentos // ============================================ public function user(): BelongsTo { return $this->belongsTo(User::class); } public function sourceTransaction(): BelongsTo { return $this->belongsTo(Transaction::class, 'source_transaction_id'); } public function account(): BelongsTo { return $this->belongsTo(Account::class); } public function category(): BelongsTo { return $this->belongsTo(Category::class); } public function costCenter(): BelongsTo { return $this->belongsTo(CostCenter::class); } public function instances(): HasMany { return $this->hasMany(RecurringInstance::class); } public function pendingInstances(): HasMany { return $this->hasMany(RecurringInstance::class)->where('status', 'pending'); } public function paidInstances(): HasMany { return $this->hasMany(RecurringInstance::class)->where('status', 'paid'); } // ============================================ // Métodos de Negócio // ============================================ /** * Verifica se pode gerar mais instâncias */ public function canGenerateMore(): bool { if (!$this->is_active) { return false; } // Verificar limite de ocorrências if ($this->max_occurrences !== null && $this->occurrences_generated >= $this->max_occurrences) { return false; } // Verificar data fim if ($this->end_date !== null && now()->startOfDay()->gt($this->end_date)) { return false; } return true; } /** * Retorna a frequência formatada para exibição */ public function getFrequencyLabelAttribute(): string { $freq = self::FREQUENCIES[$this->frequency] ?? null; if (!$freq) { return $this->frequency; } $label = $freq['label']; if ($this->frequency_interval > 1) { $label = "A cada {$this->frequency_interval} " . strtolower($label); } return $label; } /** * Retorna quantas instâncias pendentes existem */ public function getPendingCountAttribute(): int { return $this->pendingInstances()->count(); } /** * Retorna o total pago até agora */ public function getTotalPaidAttribute(): float { return (float) $this->paidInstances()->sum('paid_amount'); } }