'integer', 'due_date' => 'date', 'paid_amount' => 'integer', 'paid_at' => 'datetime', 'status' => EmployeeAdvanceStatus::class, 'verified_at' => 'datetime', ]; } // 3. Scope (grouped by column, then alphabetical) // Column Group: status #[Scope] protected function approved(Builder $query): void { $query->where('status', EmployeeAdvanceStatus::APPROVED); } #[Scope] protected function paid(Builder $query): void { $query->where('status', EmployeeAdvanceStatus::PAID); } #[Scope] protected function partiallyPaid(Builder $query): void { $query->where('status', EmployeeAdvanceStatus::PARTIALLY_PAID); } #[Scope] protected function pending(Builder $query): void { $query->where('status', EmployeeAdvanceStatus::PENDING); } #[Scope] protected function rejected(Builder $query): void { $query->where('status', EmployeeAdvanceStatus::REJECTED); } // 4. Attribute public function amountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->amount, 0, ',', '.'), ); } public function canPay(): Attribute { return Attribute::make( get: fn () => in_array($this->status, [EmployeeAdvanceStatus::APPROVED, EmployeeAdvanceStatus::PARTIALLY_PAID], true), ); } public function canVerify(): Attribute { return Attribute::make( get: fn () => $this->status === EmployeeAdvanceStatus::PENDING, ); } public function createdAtFormatted(): Attribute { return Attribute::make( get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), ); } public function dueDateFormatted(): Attribute { return Attribute::make( get: fn () => $this->due_date?->translatedFormat('l, d F Y'), ); } public function dueDateInput(): Attribute { return Attribute::make( get: fn () => $this->due_date?->format('Y-m-d'), ); } public function employeeName(): Attribute { return Attribute::make( get: fn () => $this->employee?->user?->profile?->full_name ?? $this->employee?->user?->username, ); } public function isEditable(): Attribute { return Attribute::make( get: fn () => $this->status === EmployeeAdvanceStatus::PENDING, ); } public function paidAmountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->paid_amount, 0, ',', '.'), ); } public function rejectionReason(): Attribute { return Attribute::make( get: fn () => $this->rejection?->reason, ); } public function remainingAmount(): Attribute { return Attribute::make( get: fn () => $this->amount - $this->paid_amount, ); } public function remainingAmountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->remaining_amount, 0, ',', '.'), ); } public function statusLabel(): Attribute { return Attribute::make( get: fn () => $this->status?->label(), ); } // 5. Other Methods public function ensureEditable(): void { if ($this->status !== EmployeeAdvanceStatus::PENDING) { throw ValidationException::withMessages([ 'status' => 'Kasbon tidak dapat diubah.', ]); } } public static function canSubmit(User $user): bool { return $user->can(Permission::EMPLOYEE_ADVANCES_CREATE->value) && ! $user->can(Permission::EMPLOYEE_ADVANCES_VERIFY->value) && $user->employee !== null; } // 6. Relation public function cashTransaction(): BelongsTo { return $this->belongsTo(CashTransaction::class); } public function employee(): BelongsTo { return $this->belongsTo(Employee::class)->withTrashed(); } public function paidBy(): BelongsTo { return $this->belongsTo(User::class, 'paid_by_id'); } public function payments(): HasMany { return $this->hasMany(EmployeeAdvancePayment::class)->latest('paid_at'); } public function repaymentCashTransaction(): BelongsTo { return $this->belongsTo(CashTransaction::class, 'repayment_cash_transaction_id'); } public function verifiedBy(): BelongsTo { return $this->belongsTo(User::class, 'verified_by_id'); } }