'integer', 'balance_after' => 'integer', 'type' => CashTransactionType::class, ]; } // 3. Scope (grouped by column, then alphabetical) // Column Group: type #[Scope] protected function deposit(Builder $query): void { $query->where('type', CashTransactionType::DEPOSIT); } #[Scope] protected function withdrawal(Builder $query): void { $query->where('type', CashTransactionType::WITHDRAWAL); } // 4. Attribute public function amountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->amount, 0, ',', '.'), ); } public function balanceAfterFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->balance_after, 0, ',', '.'), ); } public function createdAtFormatted(): Attribute { return Attribute::make( get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'), ); } public function createdByName(): Attribute { return Attribute::make( get: fn () => $this->createdBy?->profile?->full_name ?? $this->createdBy?->username, ); } public function isIncoming(): Attribute { return Attribute::make( get: fn () => $this->reference_type === EmployeeAdvance::class ? $this->isEmployeeAdvanceRepayment() : $this->type === CashTransactionType::DEPOSIT, ); } public function referenceLabel(): Attribute { return Attribute::make( get: function () { if ($this->reference_type === null) { return $this->getManualTransactionLabel(); } if ($this->reference_type === EmployeeAdvance::class) { return $this->getEmployeeAdvanceLabel(); } return self::labelForReferenceType($this->reference_type); }, ); } public function sourceBadgeClass(): Attribute { return Attribute::make( get: fn () => match ($this->reference_type) { null => 'bg-blue-100 text-blue-700 hover:bg-blue-100', Expense::class => 'bg-red-100 text-red-700 hover:bg-red-100', EmployeeAdvance::class => 'bg-amber-100 text-amber-700 hover:bg-amber-100', Payroll::class => 'bg-green-100 text-green-700 hover:bg-green-100', Order::class => 'bg-purple-100 text-purple-700 hover:bg-purple-100', default => 'bg-gray-100 text-gray-700 hover:bg-gray-100', }, ); } // 5. Other Methods public function getEmployeeAdvanceLabel(): string { return $this->isEmployeeAdvanceRepayment() ? 'Pelunasan Kasbon' : 'Pencairan Kasbon'; } public function getManualTransactionLabel(): string { return $this->type === CashTransactionType::WITHDRAWAL ? 'Tarik Kas' : 'Setor Kas'; } public function isEmployeeAdvanceRepayment(): bool { return $this->reference instanceof EmployeeAdvance && $this->reference->repayment_cash_transaction_id === $this->id; } public static function isIncomingTransaction(self $transaction): bool { return (bool) $transaction->is_incoming; } public static function labelForReferenceType(?string $referenceType): string { if ($referenceType === null) { return 'Setor Kas'; } return match ($referenceType) { Expense::class => 'Pengeluaran', EmployeeAdvance::class => 'Kasbon Pegawai', Payroll::class => 'Gaji Pegawai', Order::class => 'Pesanan', default => class_basename($referenceType), }; } public static function mediaModuleName(): string { return 'cash'; } public function registerMediaCollections(): void { $this->addMediaCollection('photos'); } // 6. Relation public function cashAccount(): BelongsTo { return $this->belongsTo(CashAccount::class); } public function createdBy(): BelongsTo { return $this->belongsTo(User::class, 'created_by_id'); } public function order(): HasOne { return $this->hasOne(Order::class); } public function reference(): MorphTo { return $this->morphTo(); } }