2026-02-26 22:49:44 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Models\HR;
|
|
|
|
|
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
use App\Traits\ModelTrait;
|
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
|
|
|
|
|
|
class Payroll extends Model
|
|
|
|
|
{
|
|
|
|
|
use ModelTrait, SoftDeletes;
|
|
|
|
|
|
|
|
|
|
protected $table = 'payrolls';
|
|
|
|
|
|
|
|
|
|
protected $fillable = [
|
|
|
|
|
'tenant_id',
|
|
|
|
|
'user_id',
|
|
|
|
|
'pay_year',
|
|
|
|
|
'pay_month',
|
|
|
|
|
'base_salary',
|
|
|
|
|
'overtime_pay',
|
|
|
|
|
'bonus',
|
|
|
|
|
'allowances',
|
|
|
|
|
'gross_salary',
|
|
|
|
|
'income_tax',
|
|
|
|
|
'resident_tax',
|
|
|
|
|
'health_insurance',
|
2026-02-27 10:06:28 +09:00
|
|
|
'long_term_care',
|
2026-02-26 22:49:44 +09:00
|
|
|
'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 $casts = [
|
|
|
|
|
'tenant_id' => 'int',
|
|
|
|
|
'user_id' => 'int',
|
|
|
|
|
'pay_year' => 'int',
|
|
|
|
|
'pay_month' => 'int',
|
|
|
|
|
'base_salary' => 'decimal:0',
|
|
|
|
|
'overtime_pay' => 'decimal:0',
|
|
|
|
|
'bonus' => 'decimal:0',
|
|
|
|
|
'gross_salary' => 'decimal:0',
|
|
|
|
|
'income_tax' => 'decimal:0',
|
|
|
|
|
'resident_tax' => 'decimal:0',
|
|
|
|
|
'health_insurance' => 'decimal:0',
|
2026-02-27 10:06:28 +09:00
|
|
|
'long_term_care' => 'decimal:0',
|
2026-02-26 22:49:44 +09:00
|
|
|
'pension' => 'decimal:0',
|
|
|
|
|
'employment_insurance' => 'decimal:0',
|
|
|
|
|
'total_deductions' => 'decimal:0',
|
|
|
|
|
'net_salary' => 'decimal:0',
|
|
|
|
|
'allowances' => 'array',
|
|
|
|
|
'deductions' => 'array',
|
|
|
|
|
'confirmed_at' => 'datetime',
|
|
|
|
|
'paid_at' => 'datetime',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
protected $attributes = [
|
|
|
|
|
'status' => 'draft',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public const STATUS_DRAFT = 'draft';
|
|
|
|
|
|
|
|
|
|
public const STATUS_CONFIRMED = 'confirmed';
|
|
|
|
|
|
|
|
|
|
public const STATUS_PAID = 'paid';
|
|
|
|
|
|
|
|
|
|
public const STATUS_MAP = [
|
|
|
|
|
'draft' => '작성중',
|
|
|
|
|
'confirmed' => '확정',
|
|
|
|
|
'paid' => '지급완료',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public const STATUS_COLORS = [
|
|
|
|
|
'draft' => 'amber',
|
|
|
|
|
'confirmed' => 'blue',
|
|
|
|
|
'paid' => 'emerald',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// 관계 정의
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
public function user(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(User::class, 'user_id');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function confirmer(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(User::class, 'confirmed_by');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function creator(): BelongsTo
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsTo(User::class, 'created_by');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// Accessor
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
public function getStatusLabelAttribute(): string
|
|
|
|
|
{
|
|
|
|
|
return self::STATUS_MAP[$this->status] ?? $this->status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getStatusColorAttribute(): string
|
|
|
|
|
{
|
|
|
|
|
return self::STATUS_COLORS[$this->status] ?? 'gray';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getPeriodLabelAttribute(): string
|
|
|
|
|
{
|
|
|
|
|
return sprintf('%d년 %d월', $this->pay_year, $this->pay_month);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// 상태 헬퍼
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
public function isEditable(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->status === self::STATUS_DRAFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function isConfirmable(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->status === self::STATUS_DRAFT;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-27 22:17:15 +09:00
|
|
|
public function isUnconfirmable(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->status === self::STATUS_CONFIRMED;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-26 22:49:44 +09:00
|
|
|
public function isPayable(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->status === self::STATUS_CONFIRMED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function isDeletable(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->status === self::STATUS_DRAFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// 스코프
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
public function scopeForTenant($query, ?int $tenantId = null)
|
|
|
|
|
{
|
2026-02-27 09:42:58 +09:00
|
|
|
$tenantId = $tenantId ?? session('selected_tenant_id', 1);
|
2026-02-26 22:49:44 +09:00
|
|
|
|
2026-02-27 09:42:58 +09:00
|
|
|
return $query->where($this->table.'.tenant_id', $tenantId);
|
2026-02-26 22:49:44 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function scopeForPeriod($query, int $year, int $month)
|
|
|
|
|
{
|
|
|
|
|
return $query->where('pay_year', $year)->where('pay_month', $month);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function scopeWithStatus($query, string $status)
|
|
|
|
|
{
|
|
|
|
|
return $query->where('status', $status);
|
|
|
|
|
}
|
|
|
|
|
}
|