'integer', 'bonus_amount' => 'integer', 'deduction_amount' => 'integer', 'total_amount' => 'integer', 'status' => PayrollStatus::class, 'paid_at' => 'datetime', ]; } public function adjustments(): HasMany { return $this->hasMany(PayrollAdjustment::class); } public function cashTransaction(): BelongsTo { return $this->belongsTo(CashTransaction::class); } public function employee(): BelongsTo { return $this->belongsTo(Employee::class); } public function paidBy(): BelongsTo { return $this->belongsTo(User::class, 'paid_by_id'); } public function payrollPeriod(): BelongsTo { return $this->belongsTo(PayrollPeriod::class); } public function baseSalaryFormatted(): Attribute { return Attribute::make( get: fn () => $this->base_salary !== null ? 'Rp '.number_format($this->base_salary, 0, ',', '.') : '-', ); } public function bonusAmountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->bonus_amount, 0, ',', '.'), ); } public function canAdjust(): Attribute { return Attribute::make( get: fn () => $this->status === PayrollStatus::UNPAID && $this->relationLoaded('payrollPeriod') && $this->payrollPeriod?->isOpen(), ); } public function deductionAmountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->deduction_amount, 0, ',', '.'), ); } public function employeeName(): Attribute { return Attribute::make( get: fn () => $this->employee?->user?->profile?->full_name ?? $this->employee?->user?->username, ); } public function totalAmountFormatted(): Attribute { return Attribute::make( get: fn () => 'Rp '.number_format($this->total_amount, 0, ',', '.'), ); } public function paidAtFormatted(): Attribute { return Attribute::make( get: fn () => $this->paid_at?->translatedFormat('l, d F Y H:i'), ); } public function statusLabel(): Attribute { return Attribute::make( get: fn () => $this->status?->label(), ); } public function calculateKasbonDeduction(): int { $outstanding = (int) EmployeeAdvance::query() ->where('employee_id', $this->employee_id) ->where('status', EmployeeAdvanceStatus::APPROVED) ->sum('amount'); return min($outstanding, (int) $this->base_salary + (int) $this->adjustments() ->where('type', PayrollAdjustmentType::BONUS) ->sum('amount')); } public function recalculateAmounts(): void { $bonusAmount = (int) $this->adjustments() ->where('type', PayrollAdjustmentType::BONUS) ->sum('amount'); $manualDeduction = (int) $this->adjustments() ->where('type', PayrollAdjustmentType::DEDUCTION) ->sum('amount'); $kasbonDeduction = $this->calculateKasbonDeduction(); $this->bonus_amount = $bonusAmount; $this->deduction_amount = $kasbonDeduction + $manualDeduction; $this->total_amount = max(0, (int) $this->base_salary + $bonusAmount - $this->deduction_amount); } }