Files
sam-api/app/Models/Tenants/Payroll.php

338 lines
7.9 KiB
PHP
Raw Normal View History

<?php
namespace App\Models\Tenants;
use App\Models\Members\User;
use App\Traits\Auditable;
use App\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* 급여 모델
*
* @property int $id
* @property int $tenant_id
* @property int $user_id
* @property int $pay_year
* @property int $pay_month
* @property float $base_salary
* @property float $overtime_pay
* @property float $bonus
* @property array|null $allowances
* @property float $gross_salary
* @property float $income_tax
* @property float $resident_tax
* @property float $health_insurance
* @property float $pension
* @property float $employment_insurance
* @property array|null $deductions
* @property float $total_deductions
* @property float $net_salary
* @property string $status
* @property \Carbon\Carbon|null $confirmed_at
* @property int|null $confirmed_by
* @property \Carbon\Carbon|null $paid_at
* @property int|null $withdrawal_id
* @property string|null $note
* @property int|null $created_by
* @property int|null $updated_by
* @property int|null $deleted_by
*/
class Payroll extends Model
{
use Auditable, BelongsToTenant, SoftDeletes;
protected $table = 'payrolls';
protected $casts = [
'allowances' => 'array',
'deductions' => 'array',
'base_salary' => 'decimal:2',
'overtime_pay' => 'decimal:2',
'bonus' => 'decimal:2',
'gross_salary' => 'decimal:2',
'income_tax' => 'decimal:2',
'resident_tax' => 'decimal:2',
'health_insurance' => 'decimal:2',
'pension' => 'decimal:2',
'employment_insurance' => 'decimal:2',
'total_deductions' => 'decimal:2',
'net_salary' => 'decimal:2',
'confirmed_at' => 'datetime',
'paid_at' => 'datetime',
'pay_year' => 'integer',
'pay_month' => 'integer',
];
protected $fillable = [
'tenant_id',
'user_id',
'pay_year',
'pay_month',
'base_salary',
'overtime_pay',
'bonus',
'allowances',
'gross_salary',
'income_tax',
'resident_tax',
'health_insurance',
'pension',
'employment_insurance',
'deductions',
'total_deductions',
'net_salary',
'status',
'confirmed_at',
'confirmed_by',
'paid_at',
'withdrawal_id',
'note',
'created_by',
'updated_by',
'deleted_by',
];
protected $attributes = [
'status' => 'draft',
'base_salary' => 0,
'overtime_pay' => 0,
'bonus' => 0,
'gross_salary' => 0,
'income_tax' => 0,
'resident_tax' => 0,
'health_insurance' => 0,
'pension' => 0,
'employment_insurance' => 0,
'total_deductions' => 0,
'net_salary' => 0,
];
// =========================================================================
// 상태 상수
// =========================================================================
public const STATUS_DRAFT = 'draft'; // 작성중
public const STATUS_CONFIRMED = 'confirmed'; // 확정
public const STATUS_PAID = 'paid'; // 지급완료
public const STATUSES = [
self::STATUS_DRAFT,
self::STATUS_CONFIRMED,
self::STATUS_PAID,
];
// =========================================================================
// 관계 정의
// =========================================================================
/**
* 급여 대상 사용자
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* 확정자
*/
public function confirmer(): BelongsTo
{
return $this->belongsTo(User::class, 'confirmed_by');
}
/**
* 출금 내역
*/
public function withdrawal(): BelongsTo
{
return $this->belongsTo(Withdrawal::class, 'withdrawal_id');
}
/**
* 생성자
*/
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
/**
* 수정자
*/
public function updater(): BelongsTo
{
return $this->belongsTo(User::class, 'updated_by');
}
// =========================================================================
// 스코프
// =========================================================================
/**
* 특정 상태
*/
public function scopeWithStatus($query, string $status)
{
return $query->where('status', $status);
}
/**
* 작성중
*/
public function scopeDraft($query)
{
return $query->where('status', self::STATUS_DRAFT);
}
/**
* 확정
*/
public function scopeConfirmed($query)
{
return $query->where('status', self::STATUS_CONFIRMED);
}
/**
* 지급완료
*/
public function scopePaid($query)
{
return $query->where('status', self::STATUS_PAID);
}
/**
* 특정 연월
*/
public function scopeForPeriod($query, int $year, int $month)
{
return $query->where('pay_year', $year)->where('pay_month', $month);
}
/**
* 특정 사용자
*/
public function scopeForUser($query, int $userId)
{
return $query->where('user_id', $userId);
}
// =========================================================================
// 헬퍼 메서드
// =========================================================================
/**
* 수정 가능 여부 (작성중 상태만)
*/
public function isEditable(): bool
{
return $this->status === self::STATUS_DRAFT;
}
/**
* 확정 가능 여부
*/
public function isConfirmable(): bool
{
return $this->status === self::STATUS_DRAFT;
}
/**
* 지급 가능 여부
*/
public function isPayable(): bool
{
return $this->status === self::STATUS_CONFIRMED;
}
/**
* 삭제 가능 여부 (작성중만)
*/
public function isDeletable(): bool
{
return $this->status === self::STATUS_DRAFT;
}
/**
* 상태 라벨
*/
public function getStatusLabelAttribute(): string
{
return match ($this->status) {
self::STATUS_DRAFT => '작성중',
self::STATUS_CONFIRMED => '확정',
self::STATUS_PAID => '지급완료',
default => $this->status,
};
}
/**
* 급여 기간 문자열
*/
public function getPeriodLabelAttribute(): string
{
return sprintf('%d년 %02d월', $this->pay_year, $this->pay_month);
}
/**
* 수당 합계
*/
public function getAllowancesTotalAttribute(): float
{
if (empty($this->allowances)) {
return 0;
}
return collect($this->allowances)->sum('amount');
}
/**
* 공제 합계 (JSON)
*/
public function getDeductionsTotalAttribute(): float
{
if (empty($this->deductions)) {
return 0;
}
return collect($this->deductions)->sum('amount');
}
/**
* 총지급액 계산
*/
public function calculateGrossSalary(): float
{
return $this->base_salary
+ $this->overtime_pay
+ $this->bonus
+ $this->allowances_total;
}
/**
* 총공제액 계산
*/
public function calculateTotalDeductions(): float
{
return $this->income_tax
+ $this->resident_tax
+ $this->health_insurance
+ $this->pension
+ $this->employment_insurance
+ $this->deductions_total;
}
/**
* 실수령액 계산
*/
public function calculateNetSalary(): float
{
return $this->gross_salary - $this->total_deductions;
}
}