'decimal:2', 'current_amount' => 'decimal:2', 'monthly_contribution' => 'decimal:2', 'target_date' => 'date', 'start_date' => 'date', 'completed_at' => 'date', ]; protected $appends = [ 'progress_percentage', 'remaining_amount', 'months_remaining', 'required_monthly_saving', 'is_on_track', 'status_label', ]; // ============================================ // Relaciones // ============================================ public function user() { return $this->belongsTo(User::class); } public function contributions() { return $this->hasMany(GoalContribution::class); } // ============================================ // Accessors // ============================================ public function getProgressPercentageAttribute() { if ($this->target_amount <= 0) return 0; return min(100, round(($this->current_amount / $this->target_amount) * 100, 1)); } public function getRemainingAmountAttribute() { return max(0, $this->target_amount - $this->current_amount); } public function getMonthsRemainingAttribute() { if (!$this->target_date) return null; $now = Carbon::now(); $target = Carbon::parse($this->target_date); if ($target->isPast()) return 0; return $now->diffInMonths($target); } public function getRequiredMonthlySavingAttribute() { if (!$this->months_remaining || $this->months_remaining <= 0) { return $this->remaining_amount; } return round($this->remaining_amount / $this->months_remaining, 2); } public function getIsOnTrackAttribute() { if (!$this->monthly_contribution || !$this->required_monthly_saving) { return null; } return $this->monthly_contribution >= $this->required_monthly_saving; } public function getStatusLabelAttribute() { return match($this->status) { 'active' => 'En progreso', 'completed' => 'Completada', 'paused' => 'Pausada', 'cancelled' => 'Cancelada', default => $this->status, }; } // ============================================ // Methods // ============================================ public function addContribution($amount, $date = null, $transactionId = null, $notes = null) { $contribution = $this->contributions()->create([ 'amount' => $amount, 'contribution_date' => $date ?? now(), 'transaction_id' => $transactionId, 'notes' => $notes, ]); $this->current_amount += $amount; // Verificar si se completó la meta if ($this->current_amount >= $this->target_amount) { $this->status = 'completed'; $this->completed_at = now(); } $this->save(); return $contribution; } public function markAsCompleted() { $this->status = 'completed'; $this->completed_at = now(); $this->save(); } // ============================================ // Scopes // ============================================ public function scopeActive($query) { return $query->where('status', 'active'); } public function scopeCompleted($query) { return $query->where('status', 'completed'); } public function scopeForUser($query, $userId) { return $query->where('user_id', $userId); } }