This commit is contained in:
admin
2026-01-22 22:10:17 +09:00
parent 536266d70b
commit 50e534094d
9 changed files with 590 additions and 17 deletions

View File

@@ -32,9 +32,9 @@ async def validation_exception_handler(request: Request, exc: ValidationError):
# ─────────────────────────
# ルート(稼働確認)
# ─────────────────────────
@app.get("/", summary="稼働確認")
@app.get("/", include_in_schema=False)
def root():
return {"status": "ok"}
return RedirectResponse(url="/index.html")
# ─────────────────────────
# ルーター登録(※ 必ず app 定義の後)

View File

@@ -12,7 +12,7 @@ class BonusCalculationService:
@staticmethod
def get_insurance_rate(rate_type: str, target_date: date, calculation_target: str = '賞与') -> Dict[str, Any]:
"""保険料率を取得(賞与用)"""
"""保険料率を取得(賞与用、年度がない場合は前年度を参照"""
with get_connection() as conn:
with conn.cursor() as cur:
# rate_yearで年度を判定
@@ -28,7 +28,28 @@ class BonusCalculationService:
""",
(rate_type, target_year, calculation_target)
)
return cur.fetchone()
result = cur.fetchone()
# 現在の年度でデータが見つからない場合は前年度を検索最大3年遡る
if not result:
for year_offset in range(1, 4):
previous_year = target_year - year_offset
cur.execute(
"""
SELECT * FROM insurance_rates
WHERE rate_type = %s
AND rate_year = %s
AND calculation_target = %s
ORDER BY rate_id DESC
LIMIT 1
""",
(rate_type, previous_year, calculation_target)
)
result = cur.fetchone()
if result:
break
return result
@staticmethod
def get_insurance_limit(setting_type: str, target_date: date) -> Decimal:

View File

@@ -12,11 +12,13 @@ class PayrollCalculationService:
@staticmethod
def get_active_dependents_count(employee_id: int, target_date: date) -> int:
"""有効な扶養家族数を取得(源泉税控除対象のみ19歳以上)"""
"""有効な扶養家族数を取得源泉税控除対象16歳以上)"""
with get_connection() as conn:
with conn.cursor() as cur:
# その年の12月31日時点で19歳以上の扶養家族のみをカウント
# 16-18歳は住民税のみ対象なので源泉税控除対象外
# その年の12月31日時点で16歳以上の扶養家族をカウント
# 源泉所得税の扶養控除対象は16歳以上が対象
print(f"[DEBUG] 扶養人数取得: 従業員ID={employee_id}, 対象日={target_date}")
cur.execute(
"""
SELECT COUNT(*) as count FROM dependents
@@ -24,12 +26,14 @@ class PayrollCalculationService:
AND valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND birth_date IS NOT NULL
AND EXTRACT(YEAR FROM AGE(DATE(EXTRACT(YEAR FROM %s) || '-12-31'), birth_date)) >= 19
AND EXTRACT(YEAR FROM AGE(DATE(EXTRACT(YEAR FROM %s) || '-12-31'), birth_date)) >= 16
""",
(employee_id, target_date, target_date, target_date)
)
result = cur.fetchone()
return result["count"] if result else 0
count = result["count"] if result else 0
print(f"[DEBUG] 取得した扶養人数: {count}")
return count
@staticmethod
def get_salary_setting(employee_id: int, target_date: date) -> Dict[str, Any]:
@@ -51,7 +55,7 @@ class PayrollCalculationService:
@staticmethod
def get_insurance_rate(rate_type: str, target_date: date, calculation_target: str = '給与') -> Dict[str, Any]:
"""保険料率を取得"""
"""保険料率を取得(年度がない場合は前年度を参照)"""
with get_connection() as conn:
with conn.cursor() as cur:
# rate_yearで年度を判定
@@ -67,7 +71,28 @@ class PayrollCalculationService:
""",
(rate_type, target_year, calculation_target)
)
return cur.fetchone()
result = cur.fetchone()
# 現在の年度でデータが見つからない場合は前年度を検索最大3年遡る
if not result:
for year_offset in range(1, 4):
previous_year = target_year - year_offset
cur.execute(
"""
SELECT * FROM insurance_rates
WHERE rate_type = %s
AND rate_year = %s
AND calculation_target = %s
ORDER BY rate_id DESC
LIMIT 1
""",
(rate_type, previous_year, calculation_target)
)
result = cur.fetchone()
if result:
break
return result
@staticmethod
def get_insurance_limit(setting_type: str, target_date: date) -> Decimal:
@@ -94,23 +119,101 @@ class PayrollCalculationService:
dependents_count: int,
target_date: date
) -> Decimal:
"""所得税を計算"""
"""所得税を計算(税年度がない場合は前年度の表を参照)"""
with get_connection() as conn:
with conn.cursor() as cur:
# 現在の年度で検索
# 月収がmonthly_income_from <= monthly_income < monthly_income_toの範囲に入るか、
# または月収がmonthly_income_from <= monthly_income <= monthly_income_toの範囲Toが同値の場合も含む
print(f"[DEBUG] 所得税検索: 月収={monthly_income}, 扶養人数={dependents_count}, 日付={target_date}")
cur.execute(
"""
SELECT tax_amount FROM income_tax_table
SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from
FROM income_tax_table
WHERE valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to >= %s
AND monthly_income_to > %s
ORDER BY valid_from DESC
LIMIT 1
""",
(target_date, target_date, dependents_count, monthly_income, monthly_income)
)
result = cur.fetchone()
if result:
print(f"[DEBUG] 見つかった(範囲検索): From={result['monthly_income_from']}, To={result['monthly_income_to']}, 税額={result['tax_amount']}")
# 見つからない場合は、Toが同値のケースも検索月収がちょうどToの値に達する場合
if not result:
cur.execute(
"""
SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from
FROM income_tax_table
WHERE valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to = %s
ORDER BY valid_from DESC
LIMIT 1
""",
(target_date, target_date, dependents_count, monthly_income, monthly_income)
)
result = cur.fetchone()
if result:
print(f"[DEBUG] 見つかったTo同値検索: From={result['monthly_income_from']}, To={result['monthly_income_to']}, 税額={result['tax_amount']}")
# データが見つからない場合は、指定された日付に関係なく前年度以前の表を検索
if not result:
for year_offset in range(1, 4):
previous_date = date(target_date.year - year_offset, target_date.month, target_date.day)
print(f"[DEBUG] 前年度検索: {previous_date}")
cur.execute(
"""
SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from
FROM income_tax_table
WHERE valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to > %s
ORDER BY valid_from DESC
LIMIT 1
""",
(previous_date, previous_date, dependents_count, monthly_income, monthly_income)
)
result = cur.fetchone()
if result:
print(f"[DEBUG] 前年度で見つかった: {previous_date}, 税額={result['tax_amount']}")
break
# Toが同値のケースも検索
cur.execute(
"""
SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from
FROM income_tax_table
WHERE valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to = %s
ORDER BY valid_from DESC
LIMIT 1
""",
(previous_date, previous_date, dependents_count, monthly_income, monthly_income)
)
result = cur.fetchone()
if result:
print(f"[DEBUG] 前年度で見つかったTo同値: {previous_date}, 税額={result['tax_amount']}")
break
if not result:
print(f"[DEBUG] 該当する所得税データなし、デフォルト値 0 を返す")
return Decimal(str(result["tax_amount"])) if result else Decimal("0")
@staticmethod
@@ -189,6 +292,10 @@ class PayrollCalculationService:
with get_connection() as conn:
with conn.cursor() as cur:
# 給与年月の標準報酬月額表から、総支給額が属する等級を検索
target_year = payment_date.year
result = None
# まず指定年度の標準報酬月額表を検索
cur.execute(
"""
SELECT monthly_amount
@@ -199,15 +306,37 @@ class PayrollCalculationService:
ORDER BY rate_year DESC, salary_from DESC
LIMIT 1
""",
(payment_date.year, total_payment, total_payment)
(target_year, total_payment, total_payment)
)
result = cur.fetchone()
# 見つからない場合は前年度以前を検索最大3年遡る
if not result:
for year_offset in range(1, 4):
previous_year = target_year - year_offset
cur.execute(
"""
SELECT monthly_amount
FROM standard_remuneration
WHERE rate_year = %s
AND salary_from <= %s
AND (salary_to IS NULL OR salary_to > %s)
ORDER BY rate_year DESC, salary_from DESC
LIMIT 1
""",
(previous_year, total_payment, total_payment)
)
result = cur.fetchone()
if result:
break
if result:
standard_monthly_salary = Decimal(str(result["monthly_amount"]))
print(f"[DEBUG] 標準報酬月額表検索: 総支給額={total_payment} → 標準報酬月額={standard_monthly_salary}")
else:
print(f"[DEBUG] 標準報酬月額表検索: 該当等級なし(総支給額={total_payment}")
# 社会保険料上限を取得
health_insurance_limit = PayrollCalculationService.get_insurance_limit(
"健康保険標準報酬月額上限", payment_date

View File

@@ -2,7 +2,7 @@
給与設定・税率・保険料管理API (Settings Management API)
"""
from fastapi import APIRouter, HTTPException, status, UploadFile, File
from typing import List
from typing import List, Optional
from datetime import date
import csv
import io
@@ -17,10 +17,40 @@ from ...core.database import get_connection
router = APIRouter(prefix="/payroll/settings", tags=["Payroll - Settings"])
# ========================================
# ヘルパー関数:年度フォールバック処理
# ========================================
def get_child_support_rate_with_fallback(target_year: int) -> Optional[dict]:
"""子ども・子育て拠出金率を取得指定年度がない場合は前年度を参照、最大3年遡る"""
with get_connection() as conn:
with conn.cursor() as cur:
# まず指定年度を検索
cur.execute(
"SELECT * FROM child_support_contribution_rates WHERE rate_year = %s ORDER BY contribution_id DESC LIMIT 1",
(target_year,)
)
result = cur.fetchone()
# 見つからない場合は前年度以前を検索
if not result:
for year_offset in range(1, 4):
previous_year = target_year - year_offset
cur.execute(
"SELECT * FROM child_support_contribution_rates WHERE rate_year = %s ORDER BY contribution_id DESC LIMIT 1",
(previous_year,)
)
result = cur.fetchone()
if result:
break
return result
# ========================================
# 給与基本設定
# ========================================
@router.post("/salary", response_model=schemas.SalarySetting, status_code=status.HTTP_201_CREATED)
def create_salary_setting(setting: schemas.SalarySettingCreate):
"""給与設定を作成"""
@@ -744,6 +774,7 @@ def calculate_income_tax(monthly_income: Decimal, dependents_count: int = 0, tar
with get_connection() as conn:
with conn.cursor() as cur:
# 月収がmonthly_income_from <= income < monthly_income_toの範囲に入るか検索
cur.execute(
"""
SELECT tax_amount FROM income_tax_table
@@ -751,7 +782,23 @@ def calculate_income_tax(monthly_income: Decimal, dependents_count: int = 0, tar
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to >= %s
AND monthly_income_to > %s
LIMIT 1
""",
(target_date, target_date, dependents_count, monthly_income, monthly_income)
)
result = cur.fetchone()
# 見つからない場合は、Toが同値のケースも検索
if not result:
cur.execute(
"""
SELECT tax_amount FROM income_tax_table
WHERE valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to = %s
LIMIT 1
""",
(target_date, target_date, dependents_count, monthly_income, monthly_income)

88
check_income_tax.py Normal file
View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
所得税表の確認スクリプト
ユーザーが提供したデータ:
- 月収(控除後): 356,600円
- 扶養人数: 3人
- 期待される所得税: 5,840円
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'backend'))
from decimal import Decimal
from datetime import date
from app.core.database import get_connection
def check_income_tax():
"""所得税テーブルを確認"""
monthly_income = Decimal("356600")
dependents_count = 3
target_date = date(2025, 1, 22)
print(f"検索条件:")
print(f" 月収: {monthly_income}")
print(f" 扶養人数: {dependents_count}")
print(f" 適用日: {target_date}")
print()
with get_connection() as conn:
with conn.cursor() as cur:
# 全てのデータを確認
print("=== 3人扶養の全データ ===")
cur.execute(
"""
SELECT * FROM income_tax_table
WHERE dependents_count = %s
ORDER BY monthly_income_from
""",
(dependents_count,)
)
results = cur.fetchall()
print(f"総件数: {len(results)}")
# 月収356,600円が属する範囲を探す
for row in results:
if (row["monthly_income_from"] <= monthly_income <= row["monthly_income_to"]):
print(f"\n✓ 月収 {monthly_income} が属する範囲:")
print(f" From: {row['monthly_income_from']}")
print(f" To: {row['monthly_income_to']}")
print(f" 税額: {row['tax_amount']}")
print(f" 有効期間: {row['valid_from']} ~ {row['valid_to']}")
break
else:
print(f"\n✗ 月収 {monthly_income} が属する範囲が見つかりません")
print("\n該当の近い範囲:")
for row in results[-5:]:
print(f" {row['monthly_income_from']} ~ {row['monthly_income_to']}: {row['tax_amount']}")
# 現在の年度で検索(元の検索ロジックをシミュレート)
print("\n=== 元の検索ロジックvalid_from/toで検索===")
cur.execute(
"""
SELECT * FROM income_tax_table
WHERE valid_from <= %s
AND (valid_to IS NULL OR valid_to >= %s)
AND dependents_count = %s
AND monthly_income_from <= %s
AND monthly_income_to >= %s
ORDER BY valid_from DESC
LIMIT 1
""",
(target_date, target_date, dependents_count, monthly_income, monthly_income)
)
result = cur.fetchone()
if result:
print(f"検索結果:")
print(f" 月収範囲: {result['monthly_income_from']} ~ {result['monthly_income_to']}")
print(f" 税額: {result['tax_amount']}")
print(f" 有効期間: {result['valid_from']} ~ {result['valid_to']}")
else:
print("検索結果: なし")
if __name__ == "__main__":
check_income_tax()

121
debug_income_tax.py Normal file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""
所得税計算のデバッグスクリプト
ユーザーが提供したデータ:
- 月収(控除後): 356,600円
- 扶養人数: 3人
- 期待される所得税: 5,840円
"""
# サンプルCSVデータから月収356,600に対応する税額を逆算
sample_data_3 = """
月収From,月収To,扶養人数,税額
0,96000,3,0
96000,98000,3,150
98000,100000,3,200
100000,102000,3,280
102000,104000,3,360
104000,106000,3,440
106000,108000,3,520
108000,110000,3,600
110000,112000,3,680
112000,114000,3,760
114000,116000,3,840
116000,118000,3,930
118000,127000,3,1100
127000,129000,3,1230
129000,131000,3,1360
131000,133000,3,1490
133000,135000,3,1620
135000,137000,3,1750
137000,139000,3,1880
139000,141000,3,2010
141000,143000,3,2140
143000,145000,3,2270
145000,147000,3,2400
147000,149000,3,2530
149000,151000,3,2660
151000,153000,3,2790
153000,155000,3,2920
155000,157000,3,3050
157000,159000,3,3180
159000,161000,3,3310
161000,163000,3,3430
163000,165000,3,3560
165000,167000,3,3690
167000,169000,3,3820
169000,171000,3,3950
171000,173000,3,4080
173000,175000,3,4210
175000,177000,3,4340
177000,179000,3,4470
179000,181000,3,4600
181000,183000,3,4730
183000,185000,3,4860
185000,187000,3,4990
187000,189000,3,5120
189000,191000,3,5250
191000,193000,3,5380
193000,195000,3,5510
195000,197000,3,5640
197000,199000,3,5770
199000,201000,3,5900
201000,203000,3,6030
203000,205000,3,6160
205000,207000,3,6290
207000,209000,3,6420
209000,211000,3,6550
211000,213000,3,6680
213000,215000,3,6810
215000,217000,3,6940
217000,219000,3,7070
219000,221000,3,7200
221000,223000,3,7330
223000,225000,3,7460
225000,227000,3,7590
"""
# パースして月収356,600に対応する税額を探す
lines = sample_data_3.strip().split('\n')[1:] # ヘッダーをスキップ
monthly_income = 356600
print(f"月収 {monthly_income:,} 円に対応する所得税を検索...\n")
found = False
for line in lines:
parts = line.split(',')
from_val = int(parts[0])
to_val = int(parts[1])
dependents = int(parts[2])
tax = int(parts[3])
# 月収が範囲内か確認
if from_val <= monthly_income < to_val:
print(f"✓ 見つかりました!")
print(f" 月収範囲: {from_val:,} {to_val:,}")
print(f" 扶養人数: {dependents}")
print(f" 所得税: ¥{tax:,}")
found = True
break
elif from_val <= monthly_income <= to_val:
print(f"✓ 見つかりましたToが同値")
print(f" 月収範囲: {from_val:,} {to_val:,}")
print(f" 扶養人数: {dependents}")
print(f" 所得税: ¥{tax:,}")
found = True
break
if not found:
print(f"✗ 月収 {monthly_income:,} に対応する範囲が見つかりません")
print(f"\n最後の10行:")
for line in lines[-10:]:
parts = line.split(',')
from_val = int(parts[0])
to_val = int(parts[1])
tax = int(parts[3])
print(f" {from_val:,} {to_val:,}: ¥{tax:,}")
print(f"\n=== ユーザーが提供した期待値 ===")
print(f" 月収: ¥356,600")
print(f" 扶養人数: 3人")
print(f" 期待される所得税: ¥5,840")

52
test_dependents_logic.py Normal file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""
扶養人数の検索ロジック検証
日本の所得税表では、扶養人数が多いほど税額が低くなります。
しかし、「3人」という記録が、実際には「3人以上」を意味しているかもしれません。
"""
# 仮想的な所得税テーブルの状態
sample_table = [
# (From, To, 扶養人数, 税額)
(355000, 360000, 0, 7450), # ← もしかしてこれが使われている?
(355000, 360000, 1, 6700),
(355000, 360000, 2, 6100),
(355000, 360000, 3, 5840), # ← これが正しい
(355000, 360000, 999, 3500), # または「3以上」という意味で大きい番号を使用
]
monthly_income = 356600
dependents_count = 3
print("=== 扶養人数の検索ロジック検証 ===\n")
print(f"検索条件: 月収 {monthly_income:,}, 扶養人数 {dependents_count}\n")
# シナリオ1: 完全一致検索
print("[シナリオ1] WHERE dependents_count = %s")
matches = [tax for from_v, to_v, dep, tax in sample_table
if from_v <= monthly_income < to_v and dep == dependents_count]
print(f"マッチ数: {len(matches)}")
for tax in matches:
print(f" → ¥{tax}")
# シナリオ2: >= 検索3人以上は3人の行を使う
print("\n[シナリオ2] WHERE dependents_count <= %s (DESC)")
matches = [tax for from_v, to_v, dep, tax in sample_table
if from_v <= monthly_income < to_v and dep <= dependents_count]
matches_sorted = sorted(matches, reverse=True)
print(f"マッチ数: {len(matches_sorted)}")
for tax in matches_sorted[:3]:
print(f" → ¥{tax}")
# シナリオ3: テーブルに0人の行が検索される場合
print("\n[シナリオ3] もし扶養人数3が見つからず、0が返される場合")
for from_v, to_v, dep, tax in sample_table:
if from_v <= monthly_income < to_v and dep == 0:
print(f" → ¥{tax} (0人扶養の税額が使用される)")
print("\n=== 推察 ===")
print("実際に 7,450円 が計算されているということは...")
print("1. 扶養人数が 0 人として計算されている可能性")
print("2. または、テーブルの月収範囲の定義が異なる可能性")
print("3. または、複数の条件で検索されて、意図しない行が使用されている可能性")

67
test_query_logic.py Normal file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""
所得税テーブルの詳細確認スクリプト
月収356,600円で対応する所得税を確認
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'backend'))
from decimal import Decimal
from datetime import date
# テスト用の簡単なクエリシミュレーション
sample_rows_3 = [
# (From, To, 扶養3人の税額)
(350000, 355000, 5600),
(355000, 360000, 5840), # ← 356,600はここに該当するはず
(360000, 365000, 6080),
(365000, 370000, 6320),
]
monthly_income = 356600
dependents_count = 3
print("=== テスト: 月収 356,600円、扶養3人の所得税 ===\n")
# ケース1: monthly_income_to > monthly_income 条件
print("[ケース1] WHERE monthly_income_from <= %s AND monthly_income_to > %s")
print(f"条件: {monthly_income} >= From AND {monthly_income} < To\n")
found = False
for from_val, to_val, tax in sample_rows_3:
match = from_val <= monthly_income < to_val
status = "✓ MATCH" if match else "✗ NO"
print(f" {from_val:,} ~ {to_val:,}: {status} (tax={tax})")
if match:
found = True
result = tax
if found:
print(f"\n✓ 見つかりました: ¥{result}")
else:
print(f"\n✗ 見つかりませんでした")
# ケース2: monthly_income_to >= monthly_income 条件(元の間違ったロジック)
print("\n[ケース2元のバグ] WHERE monthly_income_from <= %s AND monthly_income_to >= %s")
print(f"条件: {monthly_income} >= From AND {monthly_income} <= To\n")
matches = []
for from_val, to_val, tax in sample_rows_3:
match = from_val <= monthly_income <= to_val
status = "✓ MATCH" if match else "✗ NO"
print(f" {from_val:,} ~ {to_val:,}: {status} (tax={tax})")
if match:
matches.append(tax)
if matches:
print(f"\n✓ 見つかりました: {len(matches)}")
for i, tax in enumerate(matches, 1):
print(f" {i}. ¥{tax}")
else:
print(f"\n✗ 見つかりませんでした")
print("\n=== 比較 ===")
print("正しいロジック (To > income): 5,840円")
print("バグロジック (To >= income): 複数マッチまたはマッチなし")
print(f"実際の計算結果: 7,450円 ← これはどこから来ているのか?")

48
verify_calculation.py Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""
給与計算の流れを再現してみる
画像から見える情報:
- 基本給: ¥420,000
- 総支給額: ¥420,000
- 社会保険小計: ¥63,400
- 所得税甲欄3人: ¥7,450
ユーザーが言っている「控除後の収入356,600」とは何か
"""
total_payment = 420000
social_insurance = 63400
income_tax_shown = 7450
# 課税対象額の計算
# 課税対象額 = 総支給額 - 通勤手当(非課税限度額まで) - 社会保険料
# 通勤手当がないと仮定
taxable_income = total_payment - social_insurance
print("=== 給与計算の再現 ===")
print(f"総支給額: ¥{total_payment:,}")
print(f"社会保険小計: ¥{social_insurance:,}")
print(f"課税対象額(給与-社保): ¥{taxable_income:,}")
print()
# ユーザーが言った「356,600」との比較
user_mentioned = 356600
print(f"ユーザーが言及した金額: ¥{user_mentioned:,}")
print(f"計算された課税対象額: ¥{taxable_income:,}")
print(f"差異: ¥{taxable_income - user_mentioned:,}")
print()
# 所得税の逆算
print("=== 所得税の検証 ===")
print(f"画像に表示されている所得税甲欄3人: ¥{income_tax_shown:,}")
print(f"ユーザーが期待する所得税: ¥5,840")
print(f"差異: ¥{income_tax_shown - 5840:,}")
print()
# 月収356,600に対して5,840円という金額が妥当か確認
print("=== 税率の検証 ===")
effective_rate_user = (5840 / user_mentioned) * 100
effective_rate_actual = (income_tax_shown / taxable_income) * 100
print(f"356,600円に対して5,840円の税率: {effective_rate_user:.2f}%")
print(f"{taxable_income:,}円に対して{income_tax_shown:,}円の税率: {effective_rate_actual:.2f}%")