'date', 'end_date' => 'date', 'is_all_day' => 'boolean', 'is_recurring' => 'boolean', 'is_active' => 'boolean', ]; protected $attributes = [ 'is_all_day' => true, 'type' => 'event', 'is_recurring' => false, 'is_active' => true, ]; // ========================================================================= // 일정 유형 상수 // ========================================================================= public const TYPE_TAX = 'tax'; // 세금 신고 public const TYPE_HOLIDAY = 'holiday'; // 공휴일 public const TYPE_EVENT = 'event'; // 일반 이벤트 public const TYPE_NOTICE = 'notice'; // 공지/알림 public const TYPE_MEETING = 'meeting'; // 회의 public const TYPE_OTHER = 'other'; // 기타 public const TYPES = [ self::TYPE_TAX => '세금 신고', self::TYPE_HOLIDAY => '공휴일', self::TYPE_EVENT => '이벤트', self::TYPE_NOTICE => '공지', self::TYPE_MEETING => '회의', self::TYPE_OTHER => '기타', ]; // ========================================================================= // 반복 규칙 상수 // ========================================================================= public const RECURRENCE_YEARLY = 'yearly'; // 매년 public const RECURRENCE_QUARTERLY = 'quarterly'; // 매분기 public const RECURRENCE_MONTHLY = 'monthly'; // 매월 public const RECURRENCE_WEEKLY = 'weekly'; // 매주 public const RECURRENCE_RULES = [ self::RECURRENCE_YEARLY => '매년', self::RECURRENCE_QUARTERLY => '매분기', self::RECURRENCE_MONTHLY => '매월', self::RECURRENCE_WEEKLY => '매주', ]; // ========================================================================= // 관계 정의 // ========================================================================= /** * 생성자 */ public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } /** * 수정자 */ public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); } // ========================================================================= // 스코프 // ========================================================================= /** * 특정 테넌트 + 글로벌 일정 조회 */ public function scopeForTenant(Builder $query, int $tenantId): Builder { return $query->where(function ($q) use ($tenantId) { $q->where('tenant_id', $tenantId) ->orWhereNull('tenant_id'); // 글로벌 일정 포함 }); } /** * 글로벌 일정만 조회 (본사 등록) */ public function scopeGlobal(Builder $query): Builder { return $query->whereNull('tenant_id'); } /** * 활성 일정만 */ public function scopeActive(Builder $query): Builder { return $query->where('is_active', true); } /** * 특정 유형 */ public function scopeOfType(Builder $query, string $type): Builder { return $query->where('type', $type); } /** * 세금 관련 일정 */ public function scopeTax(Builder $query): Builder { return $query->where('type', self::TYPE_TAX); } /** * 기간 내 일정 (겹치는 일정 포함) */ public function scopeBetweenDates(Builder $query, string $startDate, string $endDate): Builder { return $query->where(function ($q) use ($startDate, $endDate) { $q->where('start_date', '<=', $endDate) ->where(function ($inner) use ($startDate) { $inner->where('end_date', '>=', $startDate) ->orWhereNull('end_date'); }); }); } /** * 특정 날짜 이후 가장 가까운 일정 */ public function scopeUpcoming(Builder $query, ?string $fromDate = null): Builder { $fromDate = $fromDate ?? now()->format('Y-m-d'); return $query->where('start_date', '>=', $fromDate) ->orderBy('start_date'); } // ========================================================================= // 헬퍼 메서드 // ========================================================================= /** * 글로벌 일정인지 확인 */ public function isGlobal(): bool { return $this->tenant_id === null; } /** * 유형 라벨 */ public function getTypeLabelAttribute(): string { return self::TYPES[$this->type] ?? $this->type; } /** * 반복 규칙 라벨 */ public function getRecurrenceRuleLabelAttribute(): ?string { if (! $this->recurrence_rule) { return null; } return self::RECURRENCE_RULES[$this->recurrence_rule] ?? $this->recurrence_rule; } }