webmoney/backend/app/Models/RecurringTemplate.php
marco 54cccdd095 refactor: migração para desenvolvimento direto no servidor
- Removido README.md padrão do Laravel (backend)
- Removidos scripts de deploy (não mais necessários)
- Atualizado copilot-instructions.md para novo fluxo
- Adicionada documentação de auditoria do servidor
- Sincronizado código de produção com repositório

Novo workflow:
- Trabalhamos diretamente em /root/webmoney (symlink para /var/www/webmoney)
- Mudanças PHP são instantâneas
- Mudanças React requerem 'npm run build'
- Commit após validação funcional
2025-12-19 11:45:32 +01:00

170 lines
4.4 KiB
PHP
Executable File

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class RecurringTemplate extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'user_id',
'source_transaction_id',
'name',
'description',
'frequency',
'frequency_interval',
'day_of_month',
'day_of_week',
'start_date',
'end_date',
'max_occurrences',
'account_id',
'category_id',
'cost_center_id',
'type',
'planned_amount',
'transaction_description',
'notes',
'is_active',
'last_generated_date',
'occurrences_generated',
];
protected $casts = [
'start_date' => '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');
}
}