- 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
150 lines
3.7 KiB
PHP
Executable File
150 lines
3.7 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;
|
|
|
|
class RecurringInstance extends Model
|
|
{
|
|
use HasFactory, SoftDeletes;
|
|
|
|
protected $fillable = [
|
|
'user_id',
|
|
'recurring_template_id',
|
|
'occurrence_number',
|
|
'due_date',
|
|
'planned_amount',
|
|
'status',
|
|
'transaction_id',
|
|
'paid_at',
|
|
'paid_amount',
|
|
'paid_notes',
|
|
];
|
|
|
|
protected $casts = [
|
|
'due_date' => 'date',
|
|
'paid_at' => 'datetime',
|
|
'planned_amount' => 'decimal:2',
|
|
'paid_amount' => 'decimal:2',
|
|
'occurrence_number' => 'integer',
|
|
];
|
|
|
|
public const STATUS_PENDING = 'pending';
|
|
public const STATUS_PAID = 'paid';
|
|
public const STATUS_SKIPPED = 'skipped';
|
|
public const STATUS_CANCELLED = 'cancelled';
|
|
|
|
// ============================================
|
|
// Relacionamentos
|
|
// ============================================
|
|
|
|
public function user(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class);
|
|
}
|
|
|
|
public function template(): BelongsTo
|
|
{
|
|
return $this->belongsTo(RecurringTemplate::class, 'recurring_template_id');
|
|
}
|
|
|
|
public function transaction(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Transaction::class);
|
|
}
|
|
|
|
// ============================================
|
|
// Scopes
|
|
// ============================================
|
|
|
|
public function scopePending($query)
|
|
{
|
|
return $query->where('status', self::STATUS_PENDING);
|
|
}
|
|
|
|
public function scopePaid($query)
|
|
{
|
|
return $query->where('status', self::STATUS_PAID);
|
|
}
|
|
|
|
public function scopeOverdue($query)
|
|
{
|
|
return $query->where('status', self::STATUS_PENDING)
|
|
->where('due_date', '<', now()->startOfDay());
|
|
}
|
|
|
|
public function scopeDueSoon($query, int $days = 7)
|
|
{
|
|
return $query->where('status', self::STATUS_PENDING)
|
|
->whereBetween('due_date', [
|
|
now()->startOfDay(),
|
|
now()->addDays($days)->endOfDay()
|
|
]);
|
|
}
|
|
|
|
// ============================================
|
|
// Métodos de Negócio
|
|
// ============================================
|
|
|
|
/**
|
|
* Verifica se está vencida
|
|
*/
|
|
public function isOverdue(): bool
|
|
{
|
|
return $this->status === self::STATUS_PENDING
|
|
&& $this->due_date->lt(now()->startOfDay());
|
|
}
|
|
|
|
/**
|
|
* Verifica se está paga
|
|
*/
|
|
public function isPaid(): bool
|
|
{
|
|
return $this->status === self::STATUS_PAID;
|
|
}
|
|
|
|
/**
|
|
* Verifica se está pendente
|
|
*/
|
|
public function isPending(): bool
|
|
{
|
|
return $this->status === self::STATUS_PENDING;
|
|
}
|
|
|
|
/**
|
|
* Retorna a diferença entre valor planejado e pago
|
|
*/
|
|
public function getDifferenceAttribute(): ?float
|
|
{
|
|
if ($this->paid_amount === null) {
|
|
return null;
|
|
}
|
|
return (float) $this->paid_amount - (float) $this->planned_amount;
|
|
}
|
|
|
|
/**
|
|
* Retorna dias até o vencimento (negativo se vencido)
|
|
*/
|
|
public function getDaysUntilDueAttribute(): int
|
|
{
|
|
return (int) now()->startOfDay()->diffInDays($this->due_date, false);
|
|
}
|
|
|
|
/**
|
|
* Retorna status formatado
|
|
*/
|
|
public function getStatusLabelAttribute(): string
|
|
{
|
|
return match($this->status) {
|
|
self::STATUS_PENDING => 'Pendente',
|
|
self::STATUS_PAID => 'Pago',
|
|
self::STATUS_SKIPPED => 'Pulado',
|
|
self::STATUS_CANCELLED => 'Cancelado',
|
|
default => $this->status,
|
|
};
|
|
}
|
|
}
|