Coverage for src/inheritance_calculator_core/services/inheritance_calculator.py: 0%

1"""相続計算サービス

2

3相続人の確定と相続割合の計算を統合して実行するサービス。

4"""

5 from typing import List, Dict, Optional

6 from fractions import Fraction

7

8 from ..models.person import Person

9 from ..models.relationship import BloodType

10 from ..models.inheritance import (

11 InheritanceResult,

12 HeritageRank,

13 SubstitutionType,

14 Heir,

15)

16 from ..utils.exceptions import RenunciationConflictError

17 from .heir_validator import HeirValidator

18 from .share_calculator import ShareCalculator

19 from .base import BaseService

20

21

22 class InheritanceCalculator(BaseService[InheritanceResult]):

23 """

24 相続計算サービス

25

26 相続人の資格確定、相続割合の計算、結果の生成を統合的に実行する。

27 """

28

29 def __init__(self) -> None:

30 """初期化"""

31 super().__init__()

32 self.validator = HeirValidator()

33 self.calculator = ShareCalculator()

34

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 相続計算を実行

52

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次相続のみを承認できない

69

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 = {}

88

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

94

95 # 結果オブジェクトの作成

96 result = InheritanceResult(decedent=decedent)

97

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 )

105

106 # 相続割合を計算

107 shares = self.calculator.calculate_shares(

108 valid_spouses,

109 valid_children,

110 valid_parents,

111 valid_siblings,

112 sibling_blood_types

113 )

114

115 # 相続人を結果に追加

116 self._add_heirs_to_result(

117 result,

118 valid_spouses,

119 valid_children,

120 valid_parents,

121 valid_siblings,

122 shares

123 )

124

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

130

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 )

137

138 self.log_operation(

139 "Inheritance calculation completed",

140 total_heirs=result.total_heirs

141 )

142

143 return result

144

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条（配偶者の相続権）")

154

155 return valid_spouses

156

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)

165

166 if valid_children:

167 result.add_calculation_basis("民法887条1項（子の相続権）")

168

169 return valid_children

170

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)

182

183 if valid_parents:

184 result.add_calculation_basis("民法889条1項1号（直系尊属の相続権）")

185

186 return valid_parents

187

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)

202

203 if valid_siblings:

204 result.add_calculation_basis("民法889条1項2号（兄弟姉妹の相続権）")

205

206 return valid_siblings

207

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)

222

223 # 子

224 for child in children:

225 share = shares.get(str(child.id), Fraction(0, 1))

226 result.add_heir(child, HeritageRank.FIRST, share)

227

228 # 直系尊属

229 for parent in parents:

230 share = shares.get(str(parent.id), Fraction(0, 1))

231 result.add_heir(parent, HeritageRank.SECOND, share)

232

233 # 兄弟姉妹

234 for sibling in siblings:

235 share = shares.get(str(sibling.id), Fraction(0, 1))

236 result.add_heir(sibling, HeritageRank.THIRD, share)

237

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）")

245

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 再転相続の処理（情報付き版）

255

256 Args:

257 result: 現在の相続計算結果

258 retransfer_info: 再転相続先の情報（相続人ID: 再転相続先リスト）

259 retransfer_relationships: 再転相続先の関係情報（相続人ID: {人物ID: 関係タイプ}）

260 second_inheritance_renounced: 第2次相続の放棄者情報（死亡相続人ID: 放棄者リスト）

261

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 ]

270

271 if not retransfer_heirs:

272 return result

273

274 # 再転相続が発生した旨を記録

275 result.add_calculation_basis("民法第896条（相続人の相続、再転相続）")

276

277 new_heirs = []

278

279 # 再転相続が発生しない相続人をそのまま追加

280 for heir in result.heirs:

281 if not heir.person.died_before_division:

282 new_heirs.append(heir)

283

284 # 再転相続の処理

285 for original_heir in retransfer_heirs:

286 heir_id = str(original_heir.person.id)

287 retransfer_targets = retransfer_info.get(heir_id, [])

288

289 if not retransfer_targets:

290 # 再転相続先がいない場合は元の相続人をそのまま

291 new_heirs.append(original_heir)

292 continue

293

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 )

301

302 # 再転相続先を相続順位別に分類

303 # retransfer_relationships から該当する相続人の関係情報を取得

304 relationship_hints = retransfer_relationships.get(heir_id, None)

305

306 classified_heirs = self._classify_retransfer_heirs(

307 retransfer_targets,

308 original_heir.person,

309 relationship_hints

310 )

311

312 # 再転相続分を計算（法定相続分に基づく）

313 retransfer_shares = self._calculate_retransfer_shares_classified(

314 original_heir.share,

315 classified_heirs

316 )

317

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)

330

331 # 相続人リストを更新

332 result.heirs = new_heirs

333

334 return result

335

336 def _process_retransfer_inheritance(

337 self,

338 result: InheritanceResult,

339 all_persons: List[Person]

340 ) -> InheritanceResult:

341 """

342 再転相続の処理

343

344 遺産分割前に死亡した相続人がいる場合、

345 その相続分をその相続人の相続人に再転相続させる。

346

347 Args:

348 result: 現在の相続計算結果

349 all_persons: 再転相続先の候補となる全人物リスト

350

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 ]

359

360 if not retransfer_heirs:

361 return result

362

363 # 再転相続が発生した旨を記録

364 result.add_calculation_basis("民法第896条（相続人の相続、再転相続）")

365

366 new_heirs = []

367

368 # 再転相続が発生しない相続人をそのまま追加

369 for heir in result.heirs:

370 if not heir.person.died_before_division:

371 new_heirs.append(heir)

372

373 # 再転相続の処理

374 for original_heir in retransfer_heirs:

375 # この相続人の相続人を探す

376 retransfer_targets = self._find_retransfer_heirs(

377 original_heir.person,

378 all_persons

379 )

380

381 if not retransfer_targets:

382 # 再転相続先がいない場合は元の相続人をそのまま

383 new_heirs.append(original_heir)

384 continue

385

386 # 再転相続分を計算

387 retransfer_shares = self._calculate_retransfer_shares(

388 original_heir.share,

389 retransfer_targets

390 )

391

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)

404

405 # 相続人リストを更新

406 result.heirs = new_heirs

407

408 return result

409

410 def _find_retransfer_heirs(

411 self,

412 deceased_heir: Person,

413 all_persons: List[Person]

414 ) -> List[Person]:

415 """

416 再転相続先の相続人を探す

417

418 Args:

419 deceased_heir: 遺産分割前に死亡した相続人

420 all_persons: 全人物リスト

421

422 Returns:

423 再転相続先の相続人リスト

424 """

425 # 簡易実装: 配偶者と子を再転相続先とする

426 # 実際にはHeirValidatorを使って正確に判定すべき

427 retransfer_heirs = []

428

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)

435

436 return retransfer_heirs

437

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 再転相続における相続放棄の制約を検証

447

448 判例（最高裁昭和63年6月21日判決）により、再転相続において

449 第2次相続（相続人の相続）を放棄した者は、第1次相続（被相続人の相続）

450 のみを承認することはできない。

451

452 Args:

453 decedent: 被相続人（第1次相続の被相続人）

454 deceased_heir: 遺産分割前に死亡した相続人（第2次相続の被相続人）

455 retransfer_targets: 再転相続先（第1次相続を承認しようとしている者）

456 second_inheritance_renounced: 第2次相続の放棄者リスト

457

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 )

469

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 再転相続先を相続順位別に分類

478

479 relationship_hintsが提供されている場合は、それを使用して分類する。

480 提供されていない場合は、暫定的に全員を子として扱う（後方互換性）。

481

482 Args:

483 retransfer_targets: 再転相続先のリスト

484 deceased_heir: 遺産分割前に死亡した相続人

485 relationship_hints: 人物IDから関係タイプへのマッピング

486 キー: 人物ID（str(person.id)）

487 値: 'spouse' | 'child' | 'parent' | 'sibling'

488

489 Returns:

490 相続順位別の分類 {'spouses': [...], 'children': [...], ...}

491 """

492 classified: Dict[str, List[Person]] = {

493 'spouses': [],

494 'children': [],

495 'parents': [],

496 'siblings': []

497 }

498

499 # relationship_hintsが提供されていない場合は全員を子として扱う（後方互換性）

500 if relationship_hints is None:

501 classified['children'] = retransfer_targets

502 return classified

503

504 # relationship_hintsを使って分類

505 for person in retransfer_targets:

506 person_id = str(person.id)

507 relationship = relationship_hints.get(person_id, 'child') # デフォルトは子

508

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)

520

521 return classified

522

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条に基づく）

530

531 遺産分割前に死亡した相続人の相続分を、その相続人の法定相続人に

532 法定相続分に従って分配する。

533

534 計算フロー:

535 1. 分類済みの相続人リストを使用

536 2. ShareCalculatorで法定相続分を計算

537 3. 元の相続分に法定相続分を乗じて最終的な相続分を算出

538

539 Args:

540 original_share: 元の相続分（遺産分割前に死亡した相続人の相続分）

541 classified_heirs: 相続順位別に分類された再転相続先

542

543 Returns:

544 各再転相続人と相続分のタプルのリスト

545

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', [])

556

557 # 再転相続先がいない場合

558 if not (spouses or children or parents or siblings):

559 return []

560

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 )

569

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))

579

580 return result

581

582 def _calculate_retransfer_shares(

583 self,

584 original_share: Fraction,

585 retransfer_targets: List[Person]

586 ) -> List[tuple[Person, Fraction]]:

587 """

588 再転相続分を計算（後方互換性のためのラッパー）

589

590 Args:

591 original_share: 元の相続分

592 retransfer_targets: 再転相続先のリスト

593

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)