Files
sam-manage/app/Models/Finance/FundSchedule.php
2026-02-25 11:45:01 +09:00

328 lines
7.9 KiB
PHP

<?php
namespace App\Models\Finance;
use App\Models\Tenants\Tenant;
use App\Traits\BelongsToTenant;
use App\Traits\ModelTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* 자금계획일정 모델
*/
class FundSchedule extends Model
{
use BelongsToTenant, ModelTrait, SoftDeletes;
// 일정 유형 상수
public const TYPE_INCOME = 'income';
public const TYPE_EXPENSE = 'expense';
// 상태 상수
public const STATUS_PENDING = 'pending';
public const STATUS_COMPLETED = 'completed';
public const STATUS_CANCELLED = 'cancelled';
// 반복 규칙 상수
public const RECURRENCE_DAILY = 'daily';
public const RECURRENCE_WEEKLY = 'weekly';
public const RECURRENCE_MONTHLY = 'monthly';
public const RECURRENCE_YEARLY = 'yearly';
protected $fillable = [
'tenant_id',
'title',
'description',
'schedule_type',
'scheduled_date',
'amount',
'currency',
'related_bank_account_id',
'counterparty',
'category',
'status',
'is_recurring',
'recurrence_rule',
'recurrence_end_date',
'completed_date',
'completed_amount',
'memo',
'created_by',
'updated_by',
'deleted_by',
];
protected $hidden = [
'created_by',
'updated_by',
'deleted_by',
'deleted_at',
];
protected $casts = [
'amount' => 'decimal:2',
'completed_amount' => 'decimal:2',
'scheduled_date' => 'date:Y-m-d',
'completed_date' => 'date:Y-m-d',
'recurrence_end_date' => 'date:Y-m-d',
'is_recurring' => 'boolean',
];
// ============================================================
// 관계 정의
// ============================================================
/**
* 테넌트
*/
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
/**
* 관련 은행 계좌
*/
public function bankAccount(): BelongsTo
{
return $this->belongsTo(BankAccount::class, 'related_bank_account_id');
}
// ============================================================
// Accessors
// ============================================================
/**
* 포맷된 금액
*/
public function getFormattedAmountAttribute(): string
{
$amount = abs($this->amount);
if ($amount >= 100000000) {
return number_format($amount / 100000000, 1).'억원';
} elseif ($amount >= 10000000) {
return number_format($amount / 10000000, 1).'천만원';
} elseif ($amount >= 10000) {
return number_format($amount / 10000, 0).'만원';
}
return number_format($amount).'원';
}
/**
* 일정 유형 라벨
*/
public function getTypeLabelAttribute(): string
{
return match ($this->schedule_type) {
self::TYPE_INCOME => '입금 예정',
self::TYPE_EXPENSE => '지급 예정',
default => '기타',
};
}
/**
* 상태 라벨
*/
public function getStatusLabelAttribute(): string
{
return match ($this->status) {
self::STATUS_PENDING => '예정',
self::STATUS_COMPLETED => '완료',
self::STATUS_CANCELLED => '취소',
default => '미정',
};
}
/**
* 일정 유형별 색상 클래스
*/
public function getTypeColorClassAttribute(): string
{
return match ($this->schedule_type) {
self::TYPE_INCOME => 'bg-green-100 text-green-800 border-green-200',
self::TYPE_EXPENSE => 'bg-red-100 text-red-800 border-red-200',
default => 'bg-gray-100 text-gray-800 border-gray-200',
};
}
/**
* 상태별 색상 클래스
*/
public function getStatusColorClassAttribute(): string
{
return match ($this->status) {
self::STATUS_PENDING => 'bg-yellow-100 text-yellow-800',
self::STATUS_COMPLETED => 'bg-green-100 text-green-800',
self::STATUS_CANCELLED => 'bg-gray-100 text-gray-500',
default => 'bg-gray-100 text-gray-800',
};
}
// ============================================================
// Scopes
// ============================================================
/**
* 입금 예정만
*/
public function scopeIncome($query)
{
return $query->where('schedule_type', self::TYPE_INCOME);
}
/**
* 지급 예정만
*/
public function scopeExpense($query)
{
return $query->where('schedule_type', self::TYPE_EXPENSE);
}
/**
* 예정 상태만
*/
public function scopePending($query)
{
return $query->where('status', self::STATUS_PENDING);
}
/**
* 완료 상태만
*/
public function scopeCompleted($query)
{
return $query->where('status', self::STATUS_COMPLETED);
}
/**
* 특정 월의 일정
*/
public function scopeForMonth($query, int $year, int $month)
{
return $query->whereYear('scheduled_date', $year)
->whereMonth('scheduled_date', $month);
}
/**
* 날짜 범위 필터
*/
public function scopeDateBetween($query, string $startDate, string $endDate)
{
return $query->whereBetween('scheduled_date', [$startDate, $endDate]);
}
/**
* 정렬 (예정일 기준)
*/
public function scopeOrdered($query)
{
return $query->orderBy('scheduled_date')->orderBy('id');
}
// ============================================================
// 메서드
// ============================================================
/**
* 입금 예정인지 확인
*/
public function isIncome(): bool
{
return $this->schedule_type === self::TYPE_INCOME;
}
/**
* 지급 예정인지 확인
*/
public function isExpense(): bool
{
return $this->schedule_type === self::TYPE_EXPENSE;
}
/**
* 예정 상태인지 확인
*/
public function isPending(): bool
{
return $this->status === self::STATUS_PENDING;
}
/**
* 완료 상태인지 확인
*/
public function isCompleted(): bool
{
return $this->status === self::STATUS_COMPLETED;
}
/**
* 완료 처리
*/
public function markAsCompleted(?float $actualAmount = null, ?string $completedDate = null): void
{
$this->update([
'status' => self::STATUS_COMPLETED,
'completed_date' => $completedDate ?? now()->toDateString(),
'completed_amount' => $actualAmount ?? $this->amount,
'updated_by' => auth()->id(),
]);
}
/**
* 취소 처리
*/
public function markAsCancelled(): void
{
$this->update([
'status' => self::STATUS_CANCELLED,
'updated_by' => auth()->id(),
]);
}
/**
* 일정 유형 목록
*/
public static function getTypeOptions(): array
{
return [
self::TYPE_INCOME => '입금 예정',
self::TYPE_EXPENSE => '지급 예정',
];
}
/**
* 상태 목록
*/
public static function getStatusOptions(): array
{
return [
self::STATUS_PENDING => '예정',
self::STATUS_COMPLETED => '완료',
self::STATUS_CANCELLED => '취소',
];
}
/**
* 반복 규칙 목록
*/
public static function getRecurrenceOptions(): array
{
return [
self::RECURRENCE_DAILY => '매일',
self::RECURRENCE_WEEKLY => '매주',
self::RECURRENCE_MONTHLY => '매월',
self::RECURRENCE_YEARLY => '매년',
];
}
}