修正
This commit is contained in:
@@ -131,6 +131,40 @@ def approve_bonus(bonus_id: int, approved_by: str):
|
||||
return result
|
||||
|
||||
|
||||
@router.put("/{bonus_id}", response_model=schemas.BonusPayment)
|
||||
def update_bonus(bonus_id: int, request: schemas.BonusUpdateRequest):
|
||||
"""賞与の支給項目を更新(再計算は別途実行)"""
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT status FROM bonus_payments WHERE bonus_id = %s", (bonus_id,))
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="賞与が見つかりません")
|
||||
if row["status"] == "approved":
|
||||
raise HTTPException(status_code=400, detail="承認済みの賞与は編集できません")
|
||||
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE bonus_payments SET
|
||||
base_bonus = %s, performance_bonus = %s, other_bonus = %s,
|
||||
other_deduction = %s,
|
||||
payment_date = COALESCE(%s, payment_date),
|
||||
bonus_type = COALESCE(%s, bonus_type)
|
||||
WHERE bonus_id = %s
|
||||
RETURNING *
|
||||
""",
|
||||
(
|
||||
request.base_bonus, request.performance_bonus, request.other_bonus,
|
||||
request.other_deduction,
|
||||
request.payment_date, request.bonus_type,
|
||||
bonus_id
|
||||
)
|
||||
)
|
||||
result = cur.fetchone()
|
||||
conn.commit()
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/{bonus_id}/recalculate", response_model=schemas.BonusPayment)
|
||||
def recalculate_bonus(bonus_id: int):
|
||||
"""賞与を再計算"""
|
||||
@@ -170,7 +204,8 @@ def recalculate_bonus(bonus_id: int):
|
||||
base_bonus = %s, performance_bonus = %s, other_bonus = %s,
|
||||
total_bonus = %s,
|
||||
health_insurance = %s, care_insurance = %s, pension_insurance = %s,
|
||||
employment_insurance = %s, income_tax = %s, other_deduction = %s,
|
||||
employment_insurance = %s, child_support = %s,
|
||||
income_tax = %s, other_deduction = %s,
|
||||
total_deduction = %s, net_bonus = %s,
|
||||
calculated_at = %s, calculated_by = %s
|
||||
WHERE bonus_id = %s
|
||||
@@ -181,6 +216,7 @@ def recalculate_bonus(bonus_id: int):
|
||||
calc_result["other_bonus"], calc_result["total_bonus"],
|
||||
calc_result["health_insurance"], calc_result["care_insurance"],
|
||||
calc_result["pension_insurance"], calc_result["employment_insurance"],
|
||||
calc_result["child_support"],
|
||||
calc_result["income_tax"], calc_result["other_deduction"],
|
||||
calc_result["total_deduction"], calc_result["net_bonus"],
|
||||
datetime.now(), "recalculated",
|
||||
|
||||
@@ -38,6 +38,16 @@ class BonusCalculationRequest(BaseModel):
|
||||
calculated_by: str = "system"
|
||||
|
||||
|
||||
class BonusUpdateRequest(BaseModel):
|
||||
"""賞与支給項目の更新リクエスト"""
|
||||
base_bonus: Decimal
|
||||
performance_bonus: Decimal = Decimal("0")
|
||||
other_bonus: Decimal = Decimal("0")
|
||||
other_deduction: Decimal = Decimal("0")
|
||||
payment_date: Optional[date] = None
|
||||
bonus_type: Optional[str] = None
|
||||
|
||||
|
||||
class BonusPayment(BonusPaymentBase):
|
||||
"""賞与計算結果"""
|
||||
bonus_id: int
|
||||
@@ -47,6 +57,7 @@ class BonusPayment(BonusPaymentBase):
|
||||
care_insurance: Decimal
|
||||
pension_insurance: Decimal
|
||||
employment_insurance: Decimal
|
||||
child_support: Decimal = Decimal("0")
|
||||
income_tax: Decimal
|
||||
other_deduction: Decimal
|
||||
total_deduction: Decimal
|
||||
|
||||
@@ -89,27 +89,27 @@ class BonusCalculationService:
|
||||
|
||||
@staticmethod
|
||||
def get_previous_salary_avg(employee_id: int, payment_date: date) -> Decimal:
|
||||
"""前月までの3ヶ月平均給与を取得(賞与所得税計算用)"""
|
||||
"""前月の社会保険料等控除後の給与を取得(賞与所得税計算用)
|
||||
|
||||
税法上は「前月分の社会保険料等控除後の給与」= 直前1ヶ月分のみを使用する。
|
||||
ステータス問わず最新の月次給与レコードを参照する。
|
||||
"""
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
# 前月までの3ヶ月の社会保険料等控除後の給与を取得
|
||||
# 賞与支払日より前の直近1ヶ月分の給与を取得(ステータス問わず)
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT AVG(total_payment - commute_allowance - health_insurance - care_insurance - pension_insurance - employment_insurance - COALESCE(child_support, 0)) as avg_salary
|
||||
FROM (
|
||||
SELECT total_payment, commute_allowance, health_insurance, care_insurance, pension_insurance, employment_insurance, child_support
|
||||
FROM monthly_payroll
|
||||
WHERE employee_id = %s
|
||||
AND payment_date < %s
|
||||
AND status IN ('approved', 'paid')
|
||||
ORDER BY payment_date DESC
|
||||
LIMIT 3
|
||||
) as recent_payroll
|
||||
SELECT total_payment - commute_allowance - health_insurance - care_insurance - pension_insurance - employment_insurance - COALESCE(child_support, 0) as net_salary
|
||||
FROM monthly_payroll
|
||||
WHERE employee_id = %s
|
||||
AND payment_date < %s
|
||||
ORDER BY payment_date DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
(employee_id, payment_date)
|
||||
)
|
||||
result = cur.fetchone()
|
||||
return Decimal(str(result["avg_salary"])) if result and result["avg_salary"] else Decimal("0")
|
||||
return Decimal(str(result["net_salary"])) if result and result["net_salary"] else Decimal("0")
|
||||
|
||||
@staticmethod
|
||||
def calculate_bonus_income_tax(
|
||||
@@ -230,11 +230,11 @@ class BonusCalculationService:
|
||||
total_bonus * Decimal(str(employment_insurance_rate["employee_rate"]))
|
||||
).quantize(Decimal("1"), rounding=ROUND_HALF_UP)
|
||||
|
||||
# 子ども・子育て支援金(令和8年4月分以降のみ適用)
|
||||
# 賞与の場合: 社会保険料率設定(insurance_rates)の料率 × 賞与総額で計算
|
||||
# 子ども・子育て支援金(令和8年5月支給分以降のみ適用)
|
||||
# 賞与は bonus_month でなく実際の支給日(payment_date)で判定する
|
||||
child_support = Decimal("0")
|
||||
is_child_support_applicable = (
|
||||
(bonus_year == 2026 and bonus_month >= 5) or bonus_year >= 2027
|
||||
(payment_date.year == 2026 and payment_date.month >= 5) or payment_date.year >= 2027
|
||||
)
|
||||
if is_child_support_applicable:
|
||||
child_support_rate = BonusCalculationService.get_insurance_rate("子ども・子育て支援金", payment_date)
|
||||
|
||||
Reference in New Issue
Block a user