158 lines
4.3 KiB
PHP
158 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Enums\EmployeeAdvanceStatus;
|
|
use App\Enums\PayrollAdjustmentType;
|
|
use App\Enums\PayrollStatus;
|
|
use App\Models\Concerns\InteractsWithActivityLog;
|
|
use Illuminate\Database\Eloquent\Attributes\Appends;
|
|
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
|
|
#[Guarded(['id'])]
|
|
#[Appends([
|
|
'base_salary_formatted',
|
|
'bonus_amount_formatted',
|
|
'deduction_amount_formatted',
|
|
'total_amount_formatted',
|
|
'status_label',
|
|
'employee_name',
|
|
'paid_at_formatted',
|
|
'can_adjust',
|
|
])]
|
|
class Payroll extends Model
|
|
{
|
|
use HasFactory;
|
|
use InteractsWithActivityLog;
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'base_salary' => '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 () => '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, $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, $this->base_salary + $bonusAmount - $this->deduction_amount);
|
|
}
|
|
}
|