165 lines
4.3 KiB
PHP
165 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Carbon\Carbon;
|
|
|
|
class Budget extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $fillable = [
|
|
'user_id',
|
|
'category_id',
|
|
'name',
|
|
'amount',
|
|
'currency',
|
|
'year',
|
|
'month',
|
|
'period_type',
|
|
'is_active',
|
|
'notes',
|
|
];
|
|
|
|
protected $casts = [
|
|
'amount' => 'decimal:2',
|
|
'is_active' => 'boolean',
|
|
];
|
|
|
|
protected $appends = [
|
|
'spent_amount',
|
|
'remaining_amount',
|
|
'usage_percentage',
|
|
'is_exceeded',
|
|
'period_label',
|
|
];
|
|
|
|
// ============================================
|
|
// Relaciones
|
|
// ============================================
|
|
|
|
public function user()
|
|
{
|
|
return $this->belongsTo(User::class);
|
|
}
|
|
|
|
public function category()
|
|
{
|
|
return $this->belongsTo(Category::class);
|
|
}
|
|
|
|
// ============================================
|
|
// Accessors
|
|
// ============================================
|
|
|
|
public function getSpentAmountAttribute()
|
|
{
|
|
// Calcular el gasto real de las transacciones
|
|
$query = Transaction::where('user_id', $this->user_id)
|
|
->where('type', 'debit')
|
|
->whereYear('effective_date', $this->year);
|
|
|
|
if ($this->period_type === 'monthly' && $this->month) {
|
|
$query->whereMonth('effective_date', $this->month);
|
|
}
|
|
|
|
if ($this->category_id) {
|
|
// Incluir subcategorías
|
|
$categoryIds = [$this->category_id];
|
|
$subcategories = Category::where('parent_id', $this->category_id)->pluck('id')->toArray();
|
|
$categoryIds = array_merge($categoryIds, $subcategories);
|
|
|
|
$query->whereIn('category_id', $categoryIds);
|
|
}
|
|
|
|
return abs($query->sum('amount'));
|
|
}
|
|
|
|
public function getRemainingAmountAttribute()
|
|
{
|
|
return $this->amount - $this->spent_amount;
|
|
}
|
|
|
|
public function getUsagePercentageAttribute()
|
|
{
|
|
if ($this->amount <= 0) return 0;
|
|
return round(($this->spent_amount / $this->amount) * 100, 1);
|
|
}
|
|
|
|
public function getIsExceededAttribute()
|
|
{
|
|
return $this->spent_amount > $this->amount;
|
|
}
|
|
|
|
public function getPeriodLabelAttribute()
|
|
{
|
|
if ($this->period_type === 'yearly') {
|
|
return $this->year;
|
|
}
|
|
|
|
$months = [
|
|
1 => 'Enero', 2 => 'Febrero', 3 => 'Marzo', 4 => 'Abril',
|
|
5 => 'Mayo', 6 => 'Junio', 7 => 'Julio', 8 => 'Agosto',
|
|
9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre'
|
|
];
|
|
|
|
return ($months[$this->month] ?? '') . ' ' . $this->year;
|
|
}
|
|
|
|
// ============================================
|
|
// Methods
|
|
// ============================================
|
|
|
|
public static function copyToNextMonth($userId, $fromYear, $fromMonth)
|
|
{
|
|
$nextMonth = $fromMonth === 12 ? 1 : $fromMonth + 1;
|
|
$nextYear = $fromMonth === 12 ? $fromYear + 1 : $fromYear;
|
|
|
|
$budgets = self::where('user_id', $userId)
|
|
->where('year', $fromYear)
|
|
->where('month', $fromMonth)
|
|
->where('is_active', true)
|
|
->get();
|
|
|
|
foreach ($budgets as $budget) {
|
|
self::firstOrCreate([
|
|
'user_id' => $userId,
|
|
'category_id' => $budget->category_id,
|
|
'year' => $nextYear,
|
|
'month' => $nextMonth,
|
|
], [
|
|
'name' => $budget->name,
|
|
'amount' => $budget->amount,
|
|
'currency' => $budget->currency,
|
|
'period_type' => 'monthly',
|
|
'is_active' => true,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Scopes
|
|
// ============================================
|
|
|
|
public function scopeActive($query)
|
|
{
|
|
return $query->where('is_active', true);
|
|
}
|
|
|
|
public function scopeForPeriod($query, $year, $month = null)
|
|
{
|
|
$query->where('year', $year);
|
|
if ($month) {
|
|
$query->where('month', $month);
|
|
}
|
|
return $query;
|
|
}
|
|
|
|
public function scopeForUser($query, $userId)
|
|
{
|
|
return $query->where('user_id', $userId);
|
|
}
|
|
}
|