Coverage for src/inheritance_calculator_core/services/inheritance_calculator.py: 0%
190 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 05:31 +0900
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 05:31 +0900
1"""相続計算サービス
3相続人の確定と相続割合の計算を統合して実行するサービス。
4"""
5from typing import List, Dict, Optional
6from fractions import Fraction
8from ..models.person import Person
9from ..models.relationship import BloodType
10from ..models.inheritance import (
11 InheritanceResult,
12 HeritageRank,
13 SubstitutionType,
14 Heir,
15)
16from ..utils.exceptions import RenunciationConflictError
17from .heir_validator import HeirValidator
18from .share_calculator import ShareCalculator
19from .base import BaseService
22class InheritanceCalculator(BaseService[InheritanceResult]):
23 """
24 相続計算サービス
26 相続人の資格確定、相続割合の計算、結果の生成を統合的に実行する。
27 """
29 def __init__(self) -> None:
30 """初期化"""
31 super().__init__()
32 self.validator = HeirValidator()
33 self.calculator = ShareCalculator()
35 def calculate(
36 self,
37 decedent: Person,
38 spouses: List[Person],
39 children: List[Person],
40 parents: List[Person],
41 siblings: List[Person],
42 renounced: Optional[List[Person]] = None,
43 disqualified: Optional[List[Person]] = None,
44 disinherited: Optional[List[Person]] = None,
45 sibling_blood_types: Optional[Dict[str, BloodType]] = None,
46 retransfer_heirs_info: Optional[Dict[str, List[Person]]] = None,
47 retransfer_relationships: Optional[Dict[str, Dict[str, str]]] = None,
48 second_inheritance_renounced: Optional[Dict[str, List[Person]]] = None,
49 ) -> InheritanceResult:
50 """
51 相続計算を実行
53 Args:
54 decedent: 被相続人
55 spouses: 配偶者候補
56 children: 子候補
57 parents: 直系尊属候補
58 siblings: 兄弟姉妹候補
59 renounced: 相続放棄者
60 disqualified: 相続欠格者
61 disinherited: 相続廃除者
62 sibling_blood_types: 兄弟姉妹の血縁タイプ
63 retransfer_heirs_info: 再転相続先の情報(相続人ID: 再転相続先リスト)
64 retransfer_relationships: 再転相続先の関係情報(相続人ID: {人物ID: 関係タイプ})
65 例: {"deceased_heir_id": {"person1_id": "spouse", "person2_id": "child"}}
66 second_inheritance_renounced: 第2次相続の放棄者情報(死亡相続人ID: 放棄者リスト)
67 例: {"deceased_heir_id": [person1, person2]}
68 判例により、第2次相続を放棄した者は第1次相続のみを承認できない
70 Returns:
71 相続計算結果
72 """
73 # デフォルト値の設定
74 if renounced is None:
75 renounced = []
76 if disqualified is None:
77 disqualified = []
78 if disinherited is None:
79 disinherited = []
80 if sibling_blood_types is None:
81 sibling_blood_types = {}
82 if retransfer_heirs_info is None:
83 retransfer_heirs_info = {}
84 if retransfer_relationships is None:
85 retransfer_relationships = {}
86 if second_inheritance_renounced is None:
87 second_inheritance_renounced = {}
89 # バリデータの初期化
90 self.validator.set_decedent(decedent)
91 self.validator.renounced_persons = renounced
92 self.validator.disqualified_persons = disqualified
93 self.validator.disinherited_persons = disinherited
95 # 結果オブジェクトの作成
96 result = InheritanceResult(decedent=decedent)
98 # 各順位の相続人を確定
99 valid_spouses = self._validate_spouses(spouses, result)
100 valid_children = self._validate_children(children, result)
101 valid_parents = self._validate_parents(parents, result, bool(valid_children))
102 valid_siblings = self._validate_siblings(
103 siblings, result, bool(valid_children), bool(valid_parents)
104 )
106 # 相続割合を計算
107 shares = self.calculator.calculate_shares(
108 valid_spouses,
109 valid_children,
110 valid_parents,
111 valid_siblings,
112 sibling_blood_types
113 )
115 # 相続人を結果に追加
116 self._add_heirs_to_result(
117 result,
118 valid_spouses,
119 valid_children,
120 valid_parents,
121 valid_siblings,
122 shares
123 )
125 # フラグを設定
126 result.has_spouse = len(valid_spouses) > 0
127 result.has_children = len(valid_children) > 0
128 result.has_parents = len(valid_parents) > 0
129 result.has_siblings = len(valid_siblings) > 0
131 # 再転相続の処理
132 if retransfer_heirs_info:
133 all_persons = spouses + children + parents + siblings
134 result = self._process_retransfer_inheritance_with_info(
135 result, retransfer_heirs_info, retransfer_relationships, second_inheritance_renounced
136 )
138 self.log_operation(
139 "Inheritance calculation completed",
140 total_heirs=result.total_heirs
141 )
143 return result
145 def _validate_spouses(
146 self, spouses: List[Person], result: InheritanceResult
147 ) -> List[Person]:
148 """配偶者の資格検証"""
149 valid_spouses = []
150 for spouse in spouses:
151 if self.validator.validate_spouse(spouse):
152 valid_spouses.append(spouse)
153 result.add_calculation_basis("民法890条(配偶者の相続権)")
155 return valid_spouses
157 def _validate_children(
158 self, children: List[Person], result: InheritanceResult
159 ) -> List[Person]:
160 """子の資格検証"""
161 valid_children = []
162 for child in children:
163 if self.validator.validate_child(child):
164 valid_children.append(child)
166 if valid_children:
167 result.add_calculation_basis("民法887条1項(子の相続権)")
169 return valid_children
171 def _validate_parents(
172 self,
173 parents: List[Person],
174 result: InheritanceResult,
175 has_first_rank: bool
176 ) -> List[Person]:
177 """直系尊属の資格検証"""
178 valid_parents = []
179 for parent in parents:
180 if self.validator.validate_parent(parent, has_first_rank):
181 valid_parents.append(parent)
183 if valid_parents:
184 result.add_calculation_basis("民法889条1項1号(直系尊属の相続権)")
186 return valid_parents
188 def _validate_siblings(
189 self,
190 siblings: List[Person],
191 result: InheritanceResult,
192 has_first_rank: bool,
193 has_second_rank: bool
194 ) -> List[Person]:
195 """兄弟姉妹の資格検証"""
196 valid_siblings = []
197 for sibling in siblings:
198 if self.validator.validate_sibling(
199 sibling, has_first_rank, has_second_rank
200 ):
201 valid_siblings.append(sibling)
203 if valid_siblings:
204 result.add_calculation_basis("民法889条1項2号(兄弟姉妹の相続権)")
206 return valid_siblings
208 def _add_heirs_to_result(
209 self,
210 result: InheritanceResult,
211 spouses: List[Person],
212 children: List[Person],
213 parents: List[Person],
214 siblings: List[Person],
215 shares: Dict[str, Fraction]
216 ) -> None:
217 """相続人を結果に追加"""
218 # 配偶者
219 for spouse in spouses:
220 share = shares.get(str(spouse.id), Fraction(0, 1))
221 result.add_heir(spouse, HeritageRank.SPOUSE, share)
223 # 子
224 for child in children:
225 share = shares.get(str(child.id), Fraction(0, 1))
226 result.add_heir(child, HeritageRank.FIRST, share)
228 # 直系尊属
229 for parent in parents:
230 share = shares.get(str(parent.id), Fraction(0, 1))
231 result.add_heir(parent, HeritageRank.SECOND, share)
233 # 兄弟姉妹
234 for sibling in siblings:
235 share = shares.get(str(sibling.id), Fraction(0, 1))
236 result.add_heir(sibling, HeritageRank.THIRD, share)
238 # 相続分の計算根拠を追加
239 if spouses and children:
240 result.add_calculation_basis("民法900条1号(配偶者1/2、子1/2)")
241 elif spouses and parents:
242 result.add_calculation_basis("民法900条2号(配偶者2/3、直系尊属1/3)")
243 elif spouses and siblings:
244 result.add_calculation_basis("民法900条3号(配偶者3/4、兄弟姉妹1/4)")
246 def _process_retransfer_inheritance_with_info(
247 self,
248 result: InheritanceResult,
249 retransfer_info: Dict[str, List[Person]],
250 retransfer_relationships: Dict[str, Dict[str, str]],
251 second_inheritance_renounced: Dict[str, List[Person]]
252 ) -> InheritanceResult:
253 """
254 再転相続の処理(情報付き版)
256 Args:
257 result: 現在の相続計算結果
258 retransfer_info: 再転相続先の情報(相続人ID: 再転相続先リスト)
259 retransfer_relationships: 再転相続先の関係情報(相続人ID: {人物ID: 関係タイプ})
260 second_inheritance_renounced: 第2次相続の放棄者情報(死亡相続人ID: 放棄者リスト)
262 Returns:
263 再転相続処理後の相続計算結果
264 """
265 # 遺産分割前に死亡した相続人を特定
266 retransfer_heirs = [
267 heir for heir in result.heirs
268 if heir.person.died_before_division and not heir.person.is_alive
269 ]
271 if not retransfer_heirs:
272 return result
274 # 再転相続が発生した旨を記録
275 result.add_calculation_basis("民法第896条(相続人の相続、再転相続)")
277 new_heirs = []
279 # 再転相続が発生しない相続人をそのまま追加
280 for heir in result.heirs:
281 if not heir.person.died_before_division:
282 new_heirs.append(heir)
284 # 再転相続の処理
285 for original_heir in retransfer_heirs:
286 heir_id = str(original_heir.person.id)
287 retransfer_targets = retransfer_info.get(heir_id, [])
289 if not retransfer_targets:
290 # 再転相続先がいない場合は元の相続人をそのまま
291 new_heirs.append(original_heir)
292 continue
294 # 判例制約の検証: 第2次相続を放棄した者が第1次相続の再転相続先に含まれていないか
295 self._validate_retransfer_renunciation(
296 result.decedent,
297 original_heir.person,
298 retransfer_targets,
299 second_inheritance_renounced.get(heir_id, [])
300 )
302 # 再転相続先を相続順位別に分類
303 # retransfer_relationships から該当する相続人の関係情報を取得
304 relationship_hints = retransfer_relationships.get(heir_id, None)
306 classified_heirs = self._classify_retransfer_heirs(
307 retransfer_targets,
308 original_heir.person,
309 relationship_hints
310 )
312 # 再転相続分を計算(法定相続分に基づく)
313 retransfer_shares = self._calculate_retransfer_shares_classified(
314 original_heir.share,
315 classified_heirs
316 )
318 # 再転相続人を追加
319 for target, share in retransfer_shares:
320 new_heir = Heir(
321 person=target,
322 rank=original_heir.rank,
323 share=share,
324 share_percentage=float(share) * 100,
325 is_retransfer=True,
326 retransfer_from=original_heir.person,
327 original_share=original_heir.share
328 )
329 new_heirs.append(new_heir)
331 # 相続人リストを更新
332 result.heirs = new_heirs
334 return result
336 def _process_retransfer_inheritance(
337 self,
338 result: InheritanceResult,
339 all_persons: List[Person]
340 ) -> InheritanceResult:
341 """
342 再転相続の処理
344 遺産分割前に死亡した相続人がいる場合、
345 その相続分をその相続人の相続人に再転相続させる。
347 Args:
348 result: 現在の相続計算結果
349 all_persons: 再転相続先の候補となる全人物リスト
351 Returns:
352 再転相続処理後の相続計算結果
353 """
354 # 遺産分割前に死亡した相続人を特定
355 retransfer_heirs = [
356 heir for heir in result.heirs
357 if heir.person.died_before_division and not heir.person.is_alive
358 ]
360 if not retransfer_heirs:
361 return result
363 # 再転相続が発生した旨を記録
364 result.add_calculation_basis("民法第896条(相続人の相続、再転相続)")
366 new_heirs = []
368 # 再転相続が発生しない相続人をそのまま追加
369 for heir in result.heirs:
370 if not heir.person.died_before_division:
371 new_heirs.append(heir)
373 # 再転相続の処理
374 for original_heir in retransfer_heirs:
375 # この相続人の相続人を探す
376 retransfer_targets = self._find_retransfer_heirs(
377 original_heir.person,
378 all_persons
379 )
381 if not retransfer_targets:
382 # 再転相続先がいない場合は元の相続人をそのまま
383 new_heirs.append(original_heir)
384 continue
386 # 再転相続分を計算
387 retransfer_shares = self._calculate_retransfer_shares(
388 original_heir.share,
389 retransfer_targets
390 )
392 # 再転相続人を追加
393 for target, share in retransfer_shares:
394 new_heir = Heir(
395 person=target,
396 rank=original_heir.rank,
397 share=share,
398 share_percentage=float(share) * 100,
399 is_retransfer=True,
400 retransfer_from=original_heir.person,
401 original_share=original_heir.share
402 )
403 new_heirs.append(new_heir)
405 # 相続人リストを更新
406 result.heirs = new_heirs
408 return result
410 def _find_retransfer_heirs(
411 self,
412 deceased_heir: Person,
413 all_persons: List[Person]
414 ) -> List[Person]:
415 """
416 再転相続先の相続人を探す
418 Args:
419 deceased_heir: 遺産分割前に死亡した相続人
420 all_persons: 全人物リスト
422 Returns:
423 再転相続先の相続人リスト
424 """
425 # 簡易実装: 配偶者と子を再転相続先とする
426 # 実際にはHeirValidatorを使って正確に判定すべき
427 retransfer_heirs = []
429 for person in all_persons:
430 if person.is_alive and person.id != deceased_heir.id:
431 # この実装では、配偶者・子・親などの関係を
432 # 外部から渡される必要がある
433 # 簡易実装として、すべての生存者を候補とする
434 retransfer_heirs.append(person)
436 return retransfer_heirs
438 def _validate_retransfer_renunciation(
439 self,
440 decedent: Person,
441 deceased_heir: Person,
442 retransfer_targets: List[Person],
443 second_inheritance_renounced: List[Person]
444 ) -> None:
445 """
446 再転相続における相続放棄の制約を検証
448 判例(最高裁昭和63年6月21日判決)により、再転相続において
449 第2次相続(相続人の相続)を放棄した者は、第1次相続(被相続人の相続)
450 のみを承認することはできない。
452 Args:
453 decedent: 被相続人(第1次相続の被相続人)
454 deceased_heir: 遺産分割前に死亡した相続人(第2次相続の被相続人)
455 retransfer_targets: 再転相続先(第1次相続を承認しようとしている者)
456 second_inheritance_renounced: 第2次相続の放棄者リスト
458 Raises:
459 RenunciationConflictError: 第2次相続を放棄した者が第1次相続を承認しようとしている場合
460 """
461 for renounced_person in second_inheritance_renounced:
462 if renounced_person in retransfer_targets:
463 raise RenunciationConflictError(
464 f"{renounced_person.name}は{deceased_heir.name}の相続を放棄しているため、"
465 f"{decedent.name}の相続のみを承認することはできません "
466 f"(最高裁昭和63年6月21日判決)。\n"
467 f"再転相続においては、第2次相続を放棄した者は第1次相続のみを単独で承認できません。"
468 )
470 def _classify_retransfer_heirs(
471 self,
472 retransfer_targets: List[Person],
473 deceased_heir: Person,
474 relationship_hints: Optional[Dict[str, str]] = None
475 ) -> Dict[str, List[Person]]:
476 """
477 再転相続先を相続順位別に分類
479 relationship_hintsが提供されている場合は、それを使用して分類する。
480 提供されていない場合は、暫定的に全員を子として扱う(後方互換性)。
482 Args:
483 retransfer_targets: 再転相続先のリスト
484 deceased_heir: 遺産分割前に死亡した相続人
485 relationship_hints: 人物IDから関係タイプへのマッピング
486 キー: 人物ID(str(person.id))
487 値: 'spouse' | 'child' | 'parent' | 'sibling'
489 Returns:
490 相続順位別の分類 {'spouses': [...], 'children': [...], ...}
491 """
492 classified: Dict[str, List[Person]] = {
493 'spouses': [],
494 'children': [],
495 'parents': [],
496 'siblings': []
497 }
499 # relationship_hintsが提供されていない場合は全員を子として扱う(後方互換性)
500 if relationship_hints is None:
501 classified['children'] = retransfer_targets
502 return classified
504 # relationship_hintsを使って分類
505 for person in retransfer_targets:
506 person_id = str(person.id)
507 relationship = relationship_hints.get(person_id, 'child') # デフォルトは子
509 if relationship == 'spouse':
510 classified['spouses'].append(person)
511 elif relationship == 'child':
512 classified['children'].append(person)
513 elif relationship == 'parent':
514 classified['parents'].append(person)
515 elif relationship == 'sibling':
516 classified['siblings'].append(person)
517 else:
518 # 不明な関係タイプの場合は子として扱う
519 classified['children'].append(person)
521 return classified
523 def _calculate_retransfer_shares_classified(
524 self,
525 original_share: Fraction,
526 classified_heirs: Dict[str, List[Person]]
527 ) -> List[tuple[Person, Fraction]]:
528 """
529 分類済み再転相続先の相続分を計算(民法第896条に基づく)
531 遺産分割前に死亡した相続人の相続分を、その相続人の法定相続人に
532 法定相続分に従って分配する。
534 計算フロー:
535 1. 分類済みの相続人リストを使用
536 2. ShareCalculatorで法定相続分を計算
537 3. 元の相続分に法定相続分を乗じて最終的な相続分を算出
539 Args:
540 original_share: 元の相続分(遺産分割前に死亡した相続人の相続分)
541 classified_heirs: 相続順位別に分類された再転相続先
543 Returns:
544 各再転相続人と相続分のタプルのリスト
546 Example:
547 元の相続分が1/1で、配偶者1人・子2人の場合:
548 - 配偶者: 1/1 × 1/2 = 1/2(民法900条1号)
549 - 子1: 1/1 × 1/4 = 1/4(民法900条1号)
550 - 子2: 1/1 × 1/4 = 1/4(民法900条1号)
551 """
552 spouses = classified_heirs.get('spouses', [])
553 children = classified_heirs.get('children', [])
554 parents = classified_heirs.get('parents', [])
555 siblings = classified_heirs.get('siblings', [])
557 # 再転相続先がいない場合
558 if not (spouses or children or parents or siblings):
559 return []
561 # ShareCalculatorを使って法定相続分を計算
562 statutory_shares = self.calculator.calculate_shares(
563 spouses=spouses,
564 first_rank=children,
565 second_rank=parents,
566 third_rank=siblings,
567 third_rank_blood_types={} # TODO: 必要に応じて血縁タイプ情報を渡す
568 )
570 # 元の相続分を法定相続分で按分
571 result = []
572 all_heirs = spouses + children + parents + siblings
573 for person in all_heirs:
574 person_id = str(person.id)
575 if person_id in statutory_shares:
576 # 元の相続分 × 再転相続先の法定相続分
577 final_share = original_share * statutory_shares[person_id]
578 result.append((person, final_share))
580 return result
582 def _calculate_retransfer_shares(
583 self,
584 original_share: Fraction,
585 retransfer_targets: List[Person]
586 ) -> List[tuple[Person, Fraction]]:
587 """
588 再転相続分を計算(後方互換性のためのラッパー)
590 Args:
591 original_share: 元の相続分
592 retransfer_targets: 再転相続先のリスト
594 Returns:
595 各再転相続人と相続分のタプルのリスト
596 """
597 # 暫定的な分類(全員を子として扱う)
598 classified: Dict[str, List[Person]] = {
599 'spouses': [],
600 'children': retransfer_targets,
601 'parents': [],
602 'siblings': []
603 }
604 return self._calculate_retransfer_shares_classified(original_share, classified)