Files
sam-api/app/Models/Tenants/PayrollSetting.php
hskwon 7089dd1e46 feat: 급여 관리 API 구현 (Phase 2: 3.2)
- 마이그레이션: payrolls, payroll_settings 테이블 생성
- 모델: Payroll (상태관리 draft→confirmed→paid), PayrollSetting
- 서비스: PayrollService (4대보험 계산, 급여명세서)
- 컨트롤러: PayrollController + FormRequest 5개
- API 엔드포인트 13개:
  - 급여 CRUD + confirm/pay/payslip
  - 일괄 계산/확정 (calculate, bulk-confirm)
  - 설정 관리 (settings/payroll)
- Swagger 문서: PayrollApi.php
- i18n: error.php, message.php, validation.php 키 추가
2025-12-18 10:56:16 +09:00

192 lines
5.7 KiB
PHP

<?php
namespace App\Models\Tenants;
use App\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* 급여 설정 모델
*
* @property int $id
* @property int $tenant_id
* @property float $income_tax_rate
* @property float $resident_tax_rate
* @property float $health_insurance_rate
* @property float $long_term_care_rate
* @property float $pension_rate
* @property float $employment_insurance_rate
* @property float $pension_max_salary
* @property float $pension_min_salary
* @property int $pay_day
* @property bool $auto_calculate
* @property array|null $allowance_types
* @property array|null $deduction_types
*/
class PayrollSetting extends Model
{
use BelongsToTenant;
protected $table = 'payroll_settings';
public $timestamps = true;
protected $casts = [
'income_tax_rate' => 'decimal:2',
'resident_tax_rate' => 'decimal:2',
'health_insurance_rate' => 'decimal:3',
'long_term_care_rate' => 'decimal:4',
'pension_rate' => 'decimal:3',
'employment_insurance_rate' => 'decimal:3',
'pension_max_salary' => 'decimal:2',
'pension_min_salary' => 'decimal:2',
'pay_day' => 'integer',
'auto_calculate' => 'boolean',
'allowance_types' => 'array',
'deduction_types' => 'array',
];
protected $fillable = [
'tenant_id',
'income_tax_rate',
'resident_tax_rate',
'health_insurance_rate',
'long_term_care_rate',
'pension_rate',
'employment_insurance_rate',
'pension_max_salary',
'pension_min_salary',
'pay_day',
'auto_calculate',
'allowance_types',
'deduction_types',
];
protected $attributes = [
'income_tax_rate' => 0,
'resident_tax_rate' => 10,
'health_insurance_rate' => 3.545,
'long_term_care_rate' => 0.9082,
'pension_rate' => 4.5,
'employment_insurance_rate' => 0.9,
'pension_max_salary' => 5900000,
'pension_min_salary' => 370000,
'pay_day' => 25,
'auto_calculate' => false,
];
// =========================================================================
// 기본 수당 유형
// =========================================================================
public const DEFAULT_ALLOWANCE_TYPES = [
['code' => 'meal', 'name' => '식대', 'is_taxable' => false],
['code' => 'transport', 'name' => '교통비', 'is_taxable' => false],
['code' => 'position', 'name' => '직책수당', 'is_taxable' => true],
['code' => 'skill', 'name' => '기술수당', 'is_taxable' => true],
['code' => 'family', 'name' => '가족수당', 'is_taxable' => true],
['code' => 'housing', 'name' => '주거수당', 'is_taxable' => true],
];
// =========================================================================
// 기본 공제 유형
// =========================================================================
public const DEFAULT_DEDUCTION_TYPES = [
['code' => 'loan', 'name' => '대출상환'],
['code' => 'union', 'name' => '조합비'],
['code' => 'savings', 'name' => '저축'],
['code' => 'etc', 'name' => '기타공제'],
];
// =========================================================================
// 관계 정의
// =========================================================================
/**
* 테넌트
*/
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
// =========================================================================
// 헬퍼 메서드
// =========================================================================
/**
* 수당 유형 목록 (기본값 포함)
*/
public function getAllowanceTypesWithDefaultAttribute(): array
{
return $this->allowance_types ?? self::DEFAULT_ALLOWANCE_TYPES;
}
/**
* 공제 유형 목록 (기본값 포함)
*/
public function getDeductionTypesWithDefaultAttribute(): array
{
return $this->deduction_types ?? self::DEFAULT_DEDUCTION_TYPES;
}
/**
* 건강보험료 계산
*/
public function calculateHealthInsurance(float $salary): float
{
return round($salary * ($this->health_insurance_rate / 100), 0);
}
/**
* 장기요양보험료 계산 (건강보험료의 %)
*/
public function calculateLongTermCare(float $healthInsurance): float
{
return round($healthInsurance * ($this->long_term_care_rate / 100), 0);
}
/**
* 국민연금 계산
*/
public function calculatePension(float $salary): float
{
// 기준소득월액 상/하한 적용
$standardSalary = min(max($salary, $this->pension_min_salary), $this->pension_max_salary);
return round($standardSalary * ($this->pension_rate / 100), 0);
}
/**
* 고용보험료 계산
*/
public function calculateEmploymentInsurance(float $salary): float
{
return round($salary * ($this->employment_insurance_rate / 100), 0);
}
/**
* 주민세 계산 (소득세의 10%)
*/
public function calculateResidentTax(float $incomeTax): float
{
return round($incomeTax * ($this->resident_tax_rate / 100), 0);
}
/**
* 테넌트별 설정 가져오기 또는 생성
*/
public static function getOrCreate(int $tenantId): self
{
return self::firstOrCreate(
['tenant_id' => $tenantId],
[
'allowance_types' => self::DEFAULT_ALLOWANCE_TYPES,
'deduction_types' => self::DEFAULT_DEDUCTION_TYPES,
]
);
}
}