@k2works/claude-code-booster 4.13.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (752) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +42 -42
  3. package/bin/claude-code-booster +113 -90
  4. package/lib/assets/.claude/README.md +287 -283
  5. package/lib/assets/.claude/agents/xp-architect.md +250 -250
  6. package/lib/assets/.claude/agents/xp-executive.md +207 -207
  7. package/lib/assets/.claude/agents/xp-interaction-designer.md +239 -239
  8. package/lib/assets/.claude/agents/xp-product-manager.md +245 -245
  9. package/lib/assets/.claude/agents/xp-programmer.md +268 -268
  10. package/lib/assets/.claude/agents/xp-project-manager.md +229 -229
  11. package/lib/assets/.claude/agents/xp-technical-writer.md +224 -224
  12. package/lib/assets/.claude/agents/xp-tester.md +265 -265
  13. package/lib/assets/.claude/agents/xp-user-representative.md +204 -204
  14. package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
  15. package/lib/assets/.claude/settings.json +11 -11
  16. package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
  17. package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
  18. package/lib/assets/.claude/skills/analyzing-business-architecture/SKILL.md +122 -122
  19. package/lib/assets/.claude/skills/analyzing-business-case/SKILL.md +148 -148
  20. package/lib/assets/.claude/skills/analyzing-business-strategy/SKILL.md +277 -277
  21. package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
  22. package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -117
  23. package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
  24. package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
  25. package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
  26. package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
  27. package/lib/assets/.claude/skills/analyzing-review/SKILL.md +174 -174
  28. package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
  29. package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
  30. package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
  31. package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
  32. package/lib/assets/.claude/skills/closing-iteration/SKILL.md +160 -146
  33. package/lib/assets/.claude/skills/creating-adr/SKILL.md +113 -113
  34. package/lib/assets/.claude/skills/creating-article/SKILL.md +156 -0
  35. package/lib/assets/.claude/skills/creating-development-strategy/SKILL.md +127 -127
  36. package/lib/assets/.claude/skills/creating-iteration-report/SKILL.md +210 -210
  37. package/lib/assets/.claude/skills/creating-journal/SKILL.md +129 -129
  38. package/lib/assets/.claude/skills/creating-manual/SKILL.md +199 -0
  39. package/lib/assets/.claude/skills/creating-release-report/SKILL.md +161 -161
  40. package/lib/assets/.claude/skills/creating-slides/SKILL.md +86 -86
  41. package/lib/assets/.claude/skills/creating-slides/agents/openai.yaml +4 -4
  42. package/lib/assets/.claude/skills/creating-slides/references/manga-css.md +45 -45
  43. package/lib/assets/.claude/skills/creating-slides/scripts/slide_tools.py +239 -239
  44. package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
  45. package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
  46. package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
  47. package/lib/assets/.claude/skills/developing-review/SKILL.md +175 -175
  48. package/lib/assets/.claude/skills/developing-uiux-review/SKILL.md +207 -207
  49. package/lib/assets/.claude/skills/generating-bmc/SKILL.md +123 -123
  50. package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
  51. package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
  52. package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
  53. package/lib/assets/.claude/skills/opening-iteration/SKILL.md +109 -105
  54. package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
  55. package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
  56. package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
  57. package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
  58. package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
  59. package/lib/assets/.claude/skills/operating-qt/SKILL.md +147 -147
  60. package/lib/assets/.claude/skills/operating-review/SKILL.md +171 -171
  61. package/lib/assets/.claude/skills/operating-script/SKILL.md +145 -145
  62. package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
  63. package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +104 -104
  64. package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +168 -168
  65. package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +158 -158
  66. package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +144 -144
  67. package/lib/assets/.claude/skills/planning-releases/SKILL.md +119 -119
  68. package/lib/assets/.claude/skills/practicing-getting-start-tdd/SKILL.md +266 -266
  69. package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
  70. package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
  71. package/lib/assets/.claude/skills/validating-design/SKILL.md +130 -130
  72. package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +238 -238
  73. package/lib/assets/.claude/skills/validating-operating-setup/SKILL.md +118 -118
  74. package/lib/assets/.devcontainer/devcontainer.json +37 -37
  75. package/lib/assets/.env.example +23 -17
  76. package/lib/assets/.gitattributes +4 -4
  77. package/lib/assets/.github/workflows/docker-publish.yml +77 -77
  78. package/lib/assets/.github/workflows/mkdocs.yml +39 -39
  79. package/lib/assets/AGENTS.md +94 -94
  80. package/lib/assets/CLAUDE.md +201 -197
  81. package/lib/assets/README.md +304 -291
  82. package/lib/assets/docker-compose.yml +33 -33
  83. package/lib/assets/docs/adr/index.md +10 -10
  84. package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -475
  85. package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -519
  86. package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -537
  87. package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -300
  88. package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -320
  89. package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -498
  90. package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -298
  91. package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -291
  92. package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -336
  93. package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -303
  94. package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -286
  95. package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -322
  96. package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -319
  97. package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -365
  98. package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -156
  99. package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -178
  100. package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -312
  101. package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -287
  102. package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -286
  103. package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -274
  104. package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -294
  105. package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -337
  106. package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -388
  107. package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +273 -273
  108. package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -380
  109. package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -384
  110. package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -350
  111. package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -352
  112. package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -383
  113. package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -529
  114. package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -395
  115. package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -399
  116. package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -485
  117. package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -566
  118. package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -567
  119. package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -475
  120. package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -462
  121. package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +325 -325
  122. package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -401
  123. package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -450
  124. package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -475
  125. package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -739
  126. package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +567 -567
  127. package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +518 -518
  128. package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +532 -532
  129. package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +241 -241
  130. package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +383 -383
  131. package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -374
  132. package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -375
  133. package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -195
  134. package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -268
  135. package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -294
  136. package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -164
  137. package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -168
  138. package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -254
  139. package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -269
  140. package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -148
  141. package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -176
  142. package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -604
  143. package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -729
  144. package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -291
  145. package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -420
  146. package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -319
  147. package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -466
  148. package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -523
  149. package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -287
  150. package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -340
  151. package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -395
  152. package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +248 -248
  153. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +384 -384
  154. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -452
  155. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -495
  156. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -416
  157. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -382
  158. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -687
  159. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -442
  160. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -479
  161. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -479
  162. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -427
  163. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -428
  164. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -339
  165. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -309
  166. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -596
  167. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +355 -355
  168. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -350
  169. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +414 -414
  170. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -367
  171. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +403 -403
  172. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -291
  173. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +324 -324
  174. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +332 -332
  175. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +274 -274
  176. package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -298
  177. package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -304
  178. package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -362
  179. package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -257
  180. package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -254
  181. package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -283
  182. package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -395
  183. package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -319
  184. package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -382
  185. package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -287
  186. package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -303
  187. package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -326
  188. package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -332
  189. package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -379
  190. package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +177 -177
  191. package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -219
  192. package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -244
  193. package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -363
  194. package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -438
  195. package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +325 -325
  196. package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -403
  197. package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -469
  198. package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -174
  199. package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -90
  200. package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +450 -450
  201. package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -463
  202. package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -425
  203. package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -273
  204. package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -247
  205. package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -841
  206. package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -384
  207. package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -383
  208. package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -339
  209. package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -331
  210. package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -356
  211. package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -379
  212. package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -361
  213. package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -392
  214. package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -300
  215. package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -297
  216. package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -304
  217. package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -315
  218. package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -311
  219. package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -304
  220. package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -336
  221. package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -349
  222. package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +243 -243
  223. package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +328 -328
  224. package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -348
  225. package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -357
  226. package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -364
  227. package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -515
  228. package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -557
  229. package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -363
  230. package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -327
  231. package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -517
  232. package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -441
  233. package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -407
  234. package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -379
  235. package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -398
  236. package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -476
  237. package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +391 -391
  238. package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -342
  239. package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -324
  240. package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -730
  241. package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -624
  242. package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -512
  243. package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +433 -433
  244. package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +688 -688
  245. package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +243 -243
  246. package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -166
  247. package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -162
  248. package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -135
  249. package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -88
  250. package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -299
  251. package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -241
  252. package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -131
  253. package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -130
  254. package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -127
  255. package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -114
  256. package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -138
  257. package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -161
  258. package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -65
  259. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -232
  260. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -244
  261. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -202
  262. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -92
  263. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -256
  264. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -195
  265. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -214
  266. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -249
  267. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -174
  268. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -166
  269. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -192
  270. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -211
  271. package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -83
  272. package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -87
  273. package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -95
  274. package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -109
  275. package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -96
  276. package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -88
  277. package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -71
  278. package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -110
  279. package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -108
  280. package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -104
  281. package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -178
  282. package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -142
  283. package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -145
  284. package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -35
  285. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -202
  286. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -246
  287. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -218
  288. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -179
  289. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -267
  290. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -190
  291. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -161
  292. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -175
  293. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -222
  294. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -189
  295. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -212
  296. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -215
  297. package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -71
  298. package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -213
  299. package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -302
  300. package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -339
  301. package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -112
  302. package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -272
  303. package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -233
  304. package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -394
  305. package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -422
  306. package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -400
  307. package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -226
  308. package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -296
  309. package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -411
  310. package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -83
  311. package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -279
  312. package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -337
  313. package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -257
  314. package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -182
  315. package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -313
  316. package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -309
  317. package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -412
  318. package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -390
  319. package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -461
  320. package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -434
  321. package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -392
  322. package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -631
  323. package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -49
  324. package/lib/assets/docs/article/getting-start-tdd/index.md +93 -93
  325. package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -375
  326. package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -349
  327. package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -445
  328. package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +409 -409
  329. package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -330
  330. package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +290 -290
  331. package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -69
  332. package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -234
  333. package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -261
  334. package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -185
  335. package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -115
  336. package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -382
  337. package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -272
  338. package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -626
  339. package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -393
  340. package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -310
  341. package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -188
  342. package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -167
  343. package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -205
  344. package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -61
  345. package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -244
  346. package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -262
  347. package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -169
  348. package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -112
  349. package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -314
  350. package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -235
  351. package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -327
  352. package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -322
  353. package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -285
  354. package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -199
  355. package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -207
  356. package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -295
  357. package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -56
  358. package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -259
  359. package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -200
  360. package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -248
  361. package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -141
  362. package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -410
  363. package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -321
  364. package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -372
  365. package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -453
  366. package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -460
  367. package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -182
  368. package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -266
  369. package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -308
  370. package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -84
  371. package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -201
  372. package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -247
  373. package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -199
  374. package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -87
  375. package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -274
  376. package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -190
  377. package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -208
  378. package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -172
  379. package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -130
  380. package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -122
  381. package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -116
  382. package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -126
  383. package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -55
  384. package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -231
  385. package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -238
  386. package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -228
  387. package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -112
  388. package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -287
  389. package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -248
  390. package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -279
  391. package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -329
  392. package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -196
  393. package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -175
  394. package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +237 -237
  395. package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -398
  396. package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -83
  397. package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -211
  398. package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -264
  399. package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -233
  400. package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -92
  401. package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -212
  402. package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -164
  403. package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -142
  404. package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -145
  405. package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -110
  406. package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -94
  407. package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -105
  408. package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -112
  409. package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -83
  410. package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -111
  411. package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -107
  412. package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -99
  413. package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -123
  414. package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -196
  415. package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -186
  416. package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -139
  417. package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -106
  418. package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -75
  419. package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -104
  420. package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -94
  421. package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -92
  422. package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -65
  423. package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -404
  424. package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -554
  425. package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -469
  426. package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -520
  427. package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -420
  428. package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -510
  429. package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -435
  430. package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -465
  431. package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -377
  432. package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -116
  433. package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -108
  434. package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -101
  435. package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -122
  436. package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -123
  437. package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -118
  438. package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -89
  439. package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -100
  440. package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -120
  441. package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -101
  442. package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -97
  443. package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -123
  444. package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -101
  445. package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -112
  446. package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -99
  447. package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -61
  448. package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -84
  449. package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -92
  450. package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -65
  451. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -80
  452. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -103
  453. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -94
  454. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -110
  455. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -104
  456. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -93
  457. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -121
  458. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -107
  459. package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -248
  460. package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -96
  461. package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -96
  462. package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -91
  463. package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -106
  464. package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -99
  465. package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -95
  466. package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -111
  467. package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -118
  468. package/lib/assets/docs/article/grokking-concurrency/index.md +66 -66
  469. package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -102
  470. package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -308
  471. package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -334
  472. package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -221
  473. package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -213
  474. package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -112
  475. package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -69
  476. package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -101
  477. package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -101
  478. package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -313
  479. package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -239
  480. package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -418
  481. package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -227
  482. package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -299
  483. package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -315
  484. package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -297
  485. package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -314
  486. package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -360
  487. package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -270
  488. package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -108
  489. package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -120
  490. package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -126
  491. package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -175
  492. package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -158
  493. package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -94
  494. package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -133
  495. package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -155
  496. package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -69
  497. package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -78
  498. package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -112
  499. package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -93
  500. package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -110
  501. package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -119
  502. package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -83
  503. package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -131
  504. package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -129
  505. package/lib/assets/docs/article/grokkingfp/all/index.md +368 -368
  506. package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -530
  507. package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -923
  508. package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1128 -1128
  509. package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -1104
  510. package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -1026
  511. package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +785 -785
  512. package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -871
  513. package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -972
  514. package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -926
  515. package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -870
  516. package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -715
  517. package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -626
  518. package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +712 -712
  519. package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -276
  520. package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -667
  521. package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -643
  522. package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -620
  523. package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -697
  524. package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -751
  525. package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -721
  526. package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -246
  527. package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -811
  528. package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -971
  529. package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -981
  530. package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -949
  531. package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -947
  532. package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -739
  533. package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -203
  534. package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +712 -712
  535. package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -838
  536. package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -985
  537. package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -974
  538. package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1286 -1286
  539. package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1049 -1049
  540. package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -210
  541. package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -714
  542. package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -961
  543. package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -972
  544. package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -832
  545. package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -911
  546. package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +922 -922
  547. package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -234
  548. package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -591
  549. package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -866
  550. package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -915
  551. package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +878 -878
  552. package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -845
  553. package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +844 -844
  554. package/lib/assets/docs/article/grokkingfp/index.md +143 -143
  555. package/lib/assets/docs/article/grokkingfp/java/index.md +211 -211
  556. package/lib/assets/docs/article/grokkingfp/java/part-1.md +648 -648
  557. package/lib/assets/docs/article/grokkingfp/java/part-2.md +675 -675
  558. package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -672
  559. package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -771
  560. package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -959
  561. package/lib/assets/docs/article/grokkingfp/java/part-6.md +1328 -1328
  562. package/lib/assets/docs/article/grokkingfp/python/index.md +258 -258
  563. package/lib/assets/docs/article/grokkingfp/python/part-1.md +443 -443
  564. package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -958
  565. package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -1004
  566. package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -765
  567. package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -747
  568. package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -861
  569. package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -330
  570. package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +755 -755
  571. package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -938
  572. package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -946
  573. package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -921
  574. package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -908
  575. package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1412 -1412
  576. package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -242
  577. package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -634
  578. package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -1060
  579. package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -994
  580. package/lib/assets/docs/article/grokkingfp/rust/part-4.md +573 -573
  581. package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -705
  582. package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -508
  583. package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -171
  584. package/lib/assets/docs/article/grokkingfp/scala/part-1.md +543 -543
  585. package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -946
  586. package/lib/assets/docs/article/grokkingfp/scala/part-3.md +919 -919
  587. package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -742
  588. package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -722
  589. package/lib/assets/docs/article/grokkingfp/scala/part-6.md +867 -867
  590. package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -273
  591. package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +561 -561
  592. package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -1129
  593. package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -842
  594. package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1087 -1087
  595. package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -717
  596. package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +982 -982
  597. package/lib/assets/docs/article/index.md +41 -41
  598. package/lib/assets/docs/article/practical-database-design/index.md +121 -121
  599. package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +288 -288
  600. package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +518 -518
  601. package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +557 -557
  602. package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +924 -924
  603. package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +1627 -1627
  604. package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +2716 -2716
  605. package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +2082 -2082
  606. package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +2105 -2105
  607. package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +2031 -2031
  608. package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +1387 -1387
  609. package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +1677 -1677
  610. package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +1417 -1417
  611. package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +1434 -1434
  612. package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +667 -667
  613. package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +1625 -1625
  614. package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +1915 -1915
  615. package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +1708 -1708
  616. package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +2095 -2095
  617. package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +1123 -1123
  618. package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +1031 -1031
  619. package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +1382 -1382
  620. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +991 -991
  621. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +1300 -1300
  622. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +1166 -1166
  623. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +1584 -1584
  624. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +1183 -1183
  625. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +1016 -1016
  626. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +1753 -1753
  627. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +1447 -1447
  628. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +1878 -1878
  629. package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +965 -965
  630. package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +2069 -2069
  631. package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +2439 -2439
  632. package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +3661 -3661
  633. package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +2916 -2916
  634. package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +3105 -3105
  635. package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +2697 -2697
  636. package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +2544 -2544
  637. package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +2180 -2180
  638. package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +1192 -1192
  639. package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +2101 -2101
  640. package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +1032 -1032
  641. package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +1609 -1609
  642. package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +1453 -1453
  643. package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +1292 -1292
  644. package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +1470 -1470
  645. package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +1698 -1698
  646. package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +2334 -2334
  647. package/lib/assets/docs/article/practical-database-design/study/study2-1.md +1693 -1693
  648. package/lib/assets/docs/article/practical-database-design/study/study2-2.md +1347 -1347
  649. package/lib/assets/docs/article/practical-database-design/study/study2-3.md +2044 -2044
  650. package/lib/assets/docs/article/practical-database-design/study/study2-4.md +2229 -2229
  651. package/lib/assets/docs/article/practical-database-design/study/study2-5.md +2418 -2418
  652. package/lib/assets/docs/article/practical-database-design/study/study3-1.md +2205 -2205
  653. package/lib/assets/docs/article/practical-database-design/study/study3-2.md +2221 -2221
  654. package/lib/assets/docs/article/practical-database-design/study/study3-3.md +2253 -2253
  655. package/lib/assets/docs/article/practical-database-design/study/study3-4.md +2106 -2106
  656. package/lib/assets/docs/article/practical-database-design/study/study3-5.md +2507 -2507
  657. package/lib/assets/docs/article/practical-database-design/study/study4-1.md +2587 -2587
  658. package/lib/assets/docs/article/practical-database-design/study/study4-2.md +2075 -2075
  659. package/lib/assets/docs/article/practical-database-design/study/study4-3.md +1805 -1805
  660. package/lib/assets/docs/article/practical-database-design/study/study4-4.md +1895 -1895
  661. package/lib/assets/docs/article/practical-database-design/study/study4-5.md +2878 -2878
  662. package/lib/assets/docs/assets/css/extra.css +29 -29
  663. package/lib/assets/docs/assets/js/extra.js +44 -44
  664. package/lib/assets/docs/design/index.md +44 -44
  665. package/lib/assets/docs/development/index.md +39 -39
  666. package/lib/assets/docs/index.md +33 -33
  667. package/lib/assets/docs/operation/index.md +27 -27
  668. package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +546 -546
  669. package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
  670. package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +581 -581
  671. package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +642 -642
  672. package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
  673. package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +450 -450
  674. package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
  675. package/lib/assets/docs/reference/images/BMC.drawio.svg +3 -3
  676. package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
  677. package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
  678. package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
  679. package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
  680. package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
  681. package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
  682. package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
  683. package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
  684. package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
  685. package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
  686. package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
  687. package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
  688. package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
  689. package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
  690. package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
  691. package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
  692. package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
  693. package/lib/assets/docs/reference/index.md +53 -53
  694. package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -250
  695. package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
  696. package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
  697. package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +550 -550
  698. package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
  699. package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
  700. package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
  701. package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
  702. package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
  703. package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +689 -689
  704. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +461 -461
  705. package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -580
  706. package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1367 -1367
  707. package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2637 -2637
  708. package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +665 -665
  709. package/lib/assets/docs/reference//347/265/214/345/226/266/346/210/246/347/225/245/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +566 -566
  710. package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
  711. package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +518 -518
  712. package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1482 -1482
  713. package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +421 -421
  714. package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
  715. package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +305 -305
  716. package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
  717. package/lib/assets/docs/requirements/index.md +17 -17
  718. package/lib/assets/docs/review/index.md +5 -5
  719. package/lib/assets/docs/strategy/index.md +17 -17
  720. package/lib/assets/docs/template/ADR.md +30 -30
  721. package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1366 -1366
  722. package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +634 -634
  723. package/lib/assets/docs/template/README.md +50 -50
  724. package/lib/assets/docs/template/index.md +24 -24
  725. package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
  726. package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +547 -547
  727. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
  728. package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273.md +327 -327
  729. package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
  730. package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
  731. package/lib/assets/docs/template//343/203/252/343/203/252/343/203/274/343/202/271/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +275 -275
  732. package/lib/assets/docs/template//344/272/213/344/276/213/345/210/206/346/236/220.md +513 -513
  733. package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +573 -573
  734. package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -69
  735. package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
  736. package/lib/assets/docs/template//350/250/255/350/250/210.md +173 -173
  737. package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +688 -688
  738. package/lib/assets/gulpfile.js +27 -25
  739. package/lib/assets/mkdocs.yml +139 -139
  740. package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
  741. package/lib/assets/ops/docker/sonarqube-local/docker-compose.yml +57 -57
  742. package/lib/assets/ops/scripts/journal.js +180 -180
  743. package/lib/assets/ops/scripts/manual.js +198 -0
  744. package/lib/assets/ops/scripts/mkdocs.js +82 -82
  745. package/lib/assets/ops/scripts/release.js +431 -431
  746. package/lib/assets/ops/scripts/sonar_local.js +726 -726
  747. package/lib/assets/ops/scripts/ssh.js +190 -190
  748. package/lib/assets/ops/scripts/vault.js +299 -299
  749. package/lib/assets/package-lock.json +1673 -1653
  750. package/lib/assets/package.json +43 -40
  751. package/lib/gulpfile.js +37 -37
  752. package/package.json +41 -41
@@ -1,1915 +1,1915 @@
1
- # 第16章:仕訳の設計
2
-
3
- 財務会計システムの核心部分である「仕訳処理」を TDD で設計していきます。複式簿記の原則に基づき、仕訳データの登録・管理を実現するテーブル構造を設計します。
4
-
5
- ---
6
-
7
- ## 16.1 仕訳テーブルの設計
8
-
9
- ### 複式簿記と仕訳の概念
10
-
11
- 複式簿記は、すべての取引を「借方(かりかた)」と「貸方(かしかた)」の2つの側面から記録する方式です。
12
-
13
- ```plantuml
14
- @startuml
15
-
16
- title 複式簿記の原則
17
-
18
- rectangle "取引" as transaction {
19
- rectangle "借方(Debit)\n資産の増加\n負債の減少\n費用の発生" as debit
20
- rectangle "貸方(Credit)\n資産の減少\n負債の増加\n収益の発生" as credit
21
- }
22
-
23
- debit -[hidden]right- credit
24
-
25
- note bottom of transaction
26
- **貸借一致の原則**
27
- 借方合計 = 貸方合計
28
- 必ず一致する
29
- end note
30
-
31
- @enduml
32
- ```
33
-
34
- #### 仕訳の具体例
35
-
36
- 商品を現金で販売した場合の仕訳:
37
-
38
- | 借方(Debit) | 貸方(Credit) |
39
- |--------------|---------------|
40
- | 現金 100,000円 | 売上 100,000円 |
41
-
42
- 売掛金を現金と受取手形で回収した場合の仕訳(複合仕訳):
43
-
44
- | 借方(Debit) | 貸方(Credit) |
45
- |--------------|---------------|
46
- | 現金 80,000円 | 売掛金 100,000円 |
47
- | 受取手形 20,000円 | |
48
-
49
- ### 仕訳処理の流れ
50
-
51
- ```plantuml
52
- @startuml
53
-
54
- title 仕訳の流れ
55
-
56
- |営業部門|
57
- start
58
- :売上取引発生;
59
- :売上伝票作成;
60
-
61
- |経理部門|
62
- :仕訳入力;
63
- :貸借バランスチェック;
64
-
65
- if (借方合計 = 貸方合計?) then (はい)
66
- :仕訳登録;
67
- :残高更新;
68
- else (いいえ)
69
- :エラー表示;
70
- :修正入力;
71
- stop
72
- endif
73
-
74
- :仕訳完了;
75
- stop
76
-
77
- @enduml
78
- ```
79
-
80
- ### 仕訳テーブルの項目設計
81
-
82
- | 項目 | 説明 |
83
- |-----|------|
84
- | **仕訳伝票番号** | 仕訳を一意に識別するキー |
85
- | **起票日** | 会計上の取引発生日 |
86
- | **入力日** | システムへの登録日 |
87
- | **決算仕訳フラグ** | 決算整理仕訳かどうか(0:通常、1:決算) |
88
- | **単振フラグ** | 単一仕訳か複合仕訳か(0:複合、1:単一) |
89
- | **仕訳伝票区分** | 仕訳の種類(通常/決算/自動/振替) |
90
- | **定期計上フラグ** | 定期的な計上か否か |
91
- | **社員コード** | 起票担当者 |
92
- | **部門コード** | 所属部門 |
93
- | **赤伝フラグ** | 赤伝(取消)かどうか(0:黒伝、1:赤伝) |
94
- | **赤黒伝票番号** | 関連する赤黒伝票の番号 |
95
-
96
- ### 仕訳テーブルのER図
97
-
98
- ```plantuml
99
- @startuml
100
-
101
- entity "仕訳" as Journal {
102
- * **仕訳伝票番号**: VARCHAR(10) <<PK>>
103
- --
104
- * **起票日**: DATE
105
- * **入力日**: DATE
106
- * **決算仕訳フラグ**: SMALLINT
107
- * **単振フラグ**: SMALLINT
108
- * **仕訳伝票区分**: 仕訳伝票区分
109
- * **定期計上フラグ**: SMALLINT
110
- **社員コード**: VARCHAR(10) <<FK>>
111
- **部門コード**: VARCHAR(5) <<FK>>
112
- * **赤伝フラグ**: SMALLINT
113
- **赤黒伝票番号**: INTEGER
114
- * **作成日時**: TIMESTAMP
115
- * **更新日時**: TIMESTAMP
116
- }
117
-
118
- entity "仕訳明細" as JournalDetail {
119
- * **仕訳伝票番号**: VARCHAR(10) <<PK>> <<FK>>
120
- * **仕訳行番号**: SMALLINT <<PK>>
121
- --
122
- **行摘要**: VARCHAR(1000)
123
- * **作成日時**: TIMESTAMP
124
- * **更新日時**: TIMESTAMP
125
- }
126
-
127
- entity "仕訳貸借明細" as JournalDebitCreditDetail {
128
- * **仕訳伝票番号**: VARCHAR(10) <<PK>> <<FK>>
129
- * **仕訳行番号**: SMALLINT <<PK>>
130
- * **仕訳行貸借区分**: 仕訳行貸借区分 <<PK>>
131
- --
132
- * **勘定科目コード**: VARCHAR(5) <<FK>>
133
- **補助科目コード**: VARCHAR(10)
134
- **部門コード**: VARCHAR(5) <<FK>>
135
- **プロジェクトコード**: VARCHAR(10)
136
- * **仕訳金額**: DECIMAL(14,2)
137
- **通貨コード**: VARCHAR(3)
138
- **為替レート**: DECIMAL(8,2)
139
- **基軸換算仕訳金額**: DECIMAL(14,2)
140
- **消費税区分**: 消費税区分
141
- **消費税率**: SMALLINT
142
- **消費税計算区分**: 消費税計算区分
143
- **期日**: DATE
144
- **資金繰フラグ**: SMALLINT
145
- **セグメントコード**: VARCHAR(10)
146
- **相手勘定科目コード**: VARCHAR(5)
147
- **相手補助科目コード**: VARCHAR(10)
148
- **付箋コード**: VARCHAR(1)
149
- **付箋内容**: VARCHAR(60)
150
- * **作成日時**: TIMESTAMP
151
- * **更新日時**: TIMESTAMP
152
- }
153
-
154
- Journal ||--o{ JournalDetail : "含む"
155
- JournalDetail ||--o{ JournalDebitCreditDetail : "含む"
156
-
157
- note right of Journal
158
- 仕訳伝票のヘッダ情報
159
- 起票日、入力日、各種フラグを管理
160
- end note
161
-
162
- note right of JournalDetail
163
- 仕訳の行単位の情報
164
- 1つの仕訳に複数の明細行
165
- end note
166
-
167
- note right of JournalDebitCreditDetail
168
- 借方・貸方の詳細情報
169
- 勘定科目、金額、補助情報を管理
170
- end note
171
-
172
- @enduml
173
- ```
174
-
175
- ### マイグレーション:仕訳テーブルの作成
176
-
177
- <details>
178
- <summary>マイグレーションSQL</summary>
179
-
180
- ```sql
181
- -- src/main/resources/db/migration/V006__create_journal_tables.sql
182
-
183
- -- 仕訳伝票区分
184
- CREATE TYPE 仕訳伝票区分 AS ENUM ('通常', '決算', '自動', '振替');
185
-
186
- -- 仕訳行貸借区分
187
- CREATE TYPE 仕訳行貸借区分 AS ENUM ('借方', '貸方');
188
-
189
- -- 消費税区分(仕訳用)
190
- CREATE TYPE 消費税区分 AS ENUM ('課税', '非課税', '免税', '不課税', '対象外');
191
-
192
- -- 消費税計算区分(仕訳用)
193
- CREATE TYPE 消費税計算区分 AS ENUM ('外税', '内税', '税なし');
194
-
195
- -- 仕訳ヘッダ
196
- CREATE TABLE "仕訳" (
197
- "仕訳伝票番号" VARCHAR(10) PRIMARY KEY,
198
- "起票日" DATE NOT NULL,
199
- "入力日" DATE NOT NULL DEFAULT CURRENT_DATE,
200
- "決算仕訳フラグ" SMALLINT DEFAULT 0 NOT NULL,
201
- "単振フラグ" SMALLINT DEFAULT 0 NOT NULL,
202
- "仕訳伝票区分" 仕訳伝票区分 DEFAULT '通常' NOT NULL,
203
- "定期計上フラグ" SMALLINT DEFAULT 0 NOT NULL,
204
- "社員コード" VARCHAR(10),
205
- "部門コード" VARCHAR(5),
206
- "赤伝フラグ" SMALLINT DEFAULT 0 NOT NULL,
207
- "赤黒伝票番号" INTEGER,
208
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
209
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
210
- CONSTRAINT "fk_仕訳_部門"
211
- FOREIGN KEY ("部門コード") REFERENCES "部門マスタ"("部門コード")
212
- );
213
-
214
- -- 仕訳明細
215
- CREATE TABLE "仕訳明細" (
216
- "仕訳伝票番号" VARCHAR(10) NOT NULL,
217
- "仕訳行番号" SMALLINT NOT NULL,
218
- "行摘要" VARCHAR(1000),
219
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
220
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
221
- PRIMARY KEY ("仕訳伝票番号", "仕訳行番号"),
222
- CONSTRAINT "fk_仕訳明細_仕訳"
223
- FOREIGN KEY ("仕訳伝票番号") REFERENCES "仕訳"("仕訳伝票番号") ON DELETE CASCADE
224
- );
225
-
226
- -- 仕訳貸借明細
227
- CREATE TABLE "仕訳貸借明細" (
228
- "仕訳伝票番号" VARCHAR(10) NOT NULL,
229
- "仕訳行番号" SMALLINT NOT NULL,
230
- "仕訳行貸借区分" 仕訳行貸借区分 NOT NULL,
231
- "勘定科目コード" VARCHAR(5) NOT NULL,
232
- "補助科目コード" VARCHAR(10),
233
- "部門コード" VARCHAR(5),
234
- "プロジェクトコード" VARCHAR(10),
235
- "仕訳金額" DECIMAL(14,2) NOT NULL,
236
- "通貨コード" VARCHAR(3) DEFAULT 'JPY',
237
- "為替レート" DECIMAL(8,2) DEFAULT 1.00,
238
- "基軸換算仕訳金額" DECIMAL(14,2),
239
- "消費税区分" 消費税区分 DEFAULT '課税',
240
- "消費税率" SMALLINT DEFAULT 10,
241
- "消費税計算区分" 消費税計算区分 DEFAULT '外税',
242
- "期日" DATE,
243
- "資金繰フラグ" SMALLINT DEFAULT 0,
244
- "セグメントコード" VARCHAR(10),
245
- "相手勘定科目コード" VARCHAR(5),
246
- "相手補助科目コード" VARCHAR(10),
247
- "付箋コード" VARCHAR(1),
248
- "付箋内容" VARCHAR(60),
249
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
250
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
251
- PRIMARY KEY ("仕訳伝票番号", "仕訳行番号", "仕訳行貸借区分"),
252
- CONSTRAINT "fk_仕訳貸借明細_仕訳明細"
253
- FOREIGN KEY ("仕訳伝票番号", "仕訳行番号")
254
- REFERENCES "仕訳明細"("仕訳伝票番号", "仕訳行番号") ON DELETE CASCADE,
255
- CONSTRAINT "fk_仕訳貸借明細_勘定科目"
256
- FOREIGN KEY ("勘定科目コード") REFERENCES "勘定科目マスタ"("勘定科目コード"),
257
- CONSTRAINT "fk_仕訳貸借明細_部門"
258
- FOREIGN KEY ("部門コード") REFERENCES "部門マスタ"("部門コード")
259
- );
260
-
261
- -- インデックス
262
- CREATE INDEX "idx_仕訳_起票日" ON "仕訳"("起票日");
263
- CREATE INDEX "idx_仕訳_部門コード" ON "仕訳"("部門コード");
264
- CREATE INDEX "idx_仕訳_仕訳伝票区分" ON "仕訳"("仕訳伝票区分");
265
- CREATE INDEX "idx_仕訳貸借明細_勘定科目コード" ON "仕訳貸借明細"("勘定科目コード");
266
- CREATE INDEX "idx_仕訳貸借明細_部門コード" ON "仕訳貸借明細"("部門コード");
267
- ```
268
-
269
- </details>
270
-
271
- <details>
272
- <summary>貸借バランスチェック関数</summary>
273
-
274
- ```sql
275
- -- 貸借バランスチェック関数
276
- CREATE OR REPLACE FUNCTION check_journal_balance()
277
- RETURNS TRIGGER AS $$
278
- DECLARE
279
- debit_total DECIMAL(14,2);
280
- credit_total DECIMAL(14,2);
281
- BEGIN
282
- -- 借方合計を計算
283
- SELECT COALESCE(SUM("仕訳金額"), 0) INTO debit_total
284
- FROM "仕訳貸借明細"
285
- WHERE "仕訳伝票番号" = NEW."仕訳伝票番号"
286
- AND "仕訳行貸借区分" = '借方';
287
-
288
- -- 貸方合計を計算
289
- SELECT COALESCE(SUM("仕訳金額"), 0) INTO credit_total
290
- FROM "仕訳貸借明細"
291
- WHERE "仕訳伝票番号" = NEW."仕訳伝票番号"
292
- AND "仕訳行貸借区分" = '貸方';
293
-
294
- -- 貸借一致チェック(トリガー内では警告のみ、アプリ側で制御)
295
- IF debit_total <> credit_total THEN
296
- RAISE WARNING '仕訳伝票番号 % の貸借が一致していません: 借方=%, 貸方=%',
297
- NEW."仕訳伝票番号", debit_total, credit_total;
298
- END IF;
299
-
300
- RETURN NEW;
301
- END;
302
- $$ LANGUAGE plpgsql;
303
-
304
- -- 貸借バランスチェックトリガー(挿入・更新時)
305
- CREATE TRIGGER "trg_check_journal_balance"
306
- AFTER INSERT OR UPDATE ON "仕訳貸借明細"
307
- FOR EACH ROW
308
- EXECUTE FUNCTION check_journal_balance();
309
- ```
310
-
311
- </details>
312
-
313
- ---
314
-
315
- ## 16.2 仕訳明細の設計
316
-
317
- ### 仕訳明細の役割
318
-
319
- 仕訳明細は、仕訳伝票の行単位の情報を管理します。1つの仕訳伝票に複数の明細行を持つことができます。
320
-
321
- | 項目 | 説明 |
322
- |-----|------|
323
- | **仕訳伝票番号** | 親となる仕訳伝票の番号(外部キー) |
324
- | **仕訳行番号** | 明細行の連番(1から始まる) |
325
- | **行摘要** | その行の取引内容の説明(最大1000文字) |
326
-
327
- ### 単一仕訳と複合仕訳
328
-
329
- ```plantuml
330
- @startuml
331
-
332
- title 単一仕訳と複合仕訳
333
-
334
- package "単一仕訳(単振フラグ=1)" {
335
- rectangle "仕訳明細 行番号1" as single {
336
- rectangle "借方: 現金 100,000円" as sd
337
- rectangle "貸方: 売上 100,000円" as sc
338
- }
339
- }
340
-
341
- package "複合仕訳(単振フラグ=0)" {
342
- rectangle "仕訳明細 行番号1" as line1 {
343
- rectangle "借方: 現金 80,000円" as d1
344
- rectangle "貸方: 売掛金 100,000円" as c1
345
- }
346
- rectangle "仕訳明細 行番号2" as line2 {
347
- rectangle "借方: 受取手形 20,000円" as d2
348
- }
349
- }
350
-
351
- @enduml
352
- ```
353
-
354
- ---
355
-
356
- ## 16.3 仕訳貸借明細の設計
357
-
358
- ### 仕訳貸借明細の項目
359
-
360
- 仕訳貸借明細は、借方・貸方それぞれの詳細情報を管理する最も粒度の細かいテーブルです。
361
-
362
- #### 基本情報
363
-
364
- | 項目 | 説明 |
365
- |-----|------|
366
- | **仕訳伝票番号** | 親となる仕訳伝票の番号 |
367
- | **仕訳行番号** | 親となる仕訳明細の行番号 |
368
- | **仕訳行貸借区分** | 借方/貸方の区分 |
369
- | **勘定科目コード** | 使用する勘定科目 |
370
- | **補助科目コード** | 補助科目(得意先、仕入先など) |
371
- | **仕訳金額** | 取引金額 |
372
-
373
- #### 多通貨対応
374
-
375
- | 項目 | 説明 |
376
- |-----|------|
377
- | **通貨コード** | 取引通貨(デフォルト: JPY) |
378
- | **為替レート** | 適用為替レート |
379
- | **基軸換算仕訳金額** | 基軸通貨(円)換算後の金額 |
380
-
381
- #### 管理会計情報
382
-
383
- | 項目 | 説明 |
384
- |-----|------|
385
- | **部門コード** | 配賦先部門 |
386
- | **プロジェクトコード** | 関連プロジェクト |
387
- | **セグメントコード** | セグメント情報 |
388
-
389
- #### 消費税情報
390
-
391
- | 項目 | 説明 |
392
- |-----|------|
393
- | **消費税区分** | 課税/非課税/免税/不課税/対象外 |
394
- | **消費税率** | 適用税率(10, 8 など) |
395
- | **消費税計算区分** | 外税/内税/税なし |
396
-
397
- #### その他
398
-
399
- | 項目 | 説明 |
400
- |-----|------|
401
- | **期日** | 支払期日・入金期日 |
402
- | **資金繰フラグ** | 資金繰表への計上対象か |
403
- | **相手勘定科目コード** | 相手勘定科目(元帳表示用) |
404
- | **相手補助科目コード** | 相手補助科目 |
405
- | **付箋コード** | 付箋の種類 |
406
- | **付箋内容** | 付箋の内容(メモ) |
407
-
408
- ### TDD による仕訳テーブルの実装
409
-
410
- #### ドメイン層:Enum定義
411
-
412
- <details>
413
- <summary>仕訳伝票区分 Enum</summary>
414
-
415
- ```java
416
- // src/main/java/com/example/accounting/domain/model/journal/JournalVoucherType.java
417
- package com.example.fas.domain.model.journal;
418
-
419
- import lombok.Getter;
420
- import lombok.RequiredArgsConstructor;
421
-
422
- @Getter
423
- @RequiredArgsConstructor
424
- public enum JournalVoucherType {
425
- NORMAL("通常"),
426
- CLOSING("決算"),
427
- AUTO("自動"),
428
- TRANSFER("振替");
429
-
430
- private final String displayName;
431
-
432
- public static JournalVoucherType fromDisplayName(String displayName) {
433
- for (JournalVoucherType type : values()) {
434
- if (type.displayName.equals(displayName)) {
435
- return type;
436
- }
437
- }
438
- throw new IllegalArgumentException("不正な仕訳伝票区分: " + displayName);
439
- }
440
- }
441
- ```
442
-
443
- </details>
444
-
445
- <details>
446
- <summary>消費税区分 Enum</summary>
447
-
448
- ```java
449
- // src/main/java/com/example/accounting/domain/model/journal/TaxType.java
450
- package com.example.fas.domain.model.journal;
451
-
452
- import lombok.Getter;
453
- import lombok.RequiredArgsConstructor;
454
-
455
- @Getter
456
- @RequiredArgsConstructor
457
- public enum TaxType {
458
- TAXABLE("課税"),
459
- NON_TAXABLE("非課税"),
460
- TAX_EXEMPT("免税"),
461
- NOT_TAXABLE("不課税"),
462
- OUT_OF_SCOPE("対象外");
463
-
464
- private final String displayName;
465
-
466
- public static TaxType fromDisplayName(String displayName) {
467
- for (TaxType type : values()) {
468
- if (type.displayName.equals(displayName)) {
469
- return type;
470
- }
471
- }
472
- throw new IllegalArgumentException("不正な消費税区分: " + displayName);
473
- }
474
- }
475
- ```
476
-
477
- </details>
478
-
479
- <details>
480
- <summary>消費税計算区分 Enum</summary>
481
-
482
- ```java
483
- // src/main/java/com/example/accounting/domain/model/journal/TaxCalculationType.java
484
- package com.example.fas.domain.model.journal;
485
-
486
- import lombok.Getter;
487
- import lombok.RequiredArgsConstructor;
488
-
489
- @Getter
490
- @RequiredArgsConstructor
491
- public enum TaxCalculationType {
492
- EXCLUSIVE("外税"),
493
- INCLUSIVE("内税"),
494
- NO_TAX("税なし");
495
-
496
- private final String displayName;
497
-
498
- public static TaxCalculationType fromDisplayName(String displayName) {
499
- for (TaxCalculationType type : values()) {
500
- if (type.displayName.equals(displayName)) {
501
- return type;
502
- }
503
- }
504
- throw new IllegalArgumentException("不正な消費税計算区分: " + displayName);
505
- }
506
- }
507
- ```
508
-
509
- </details>
510
-
511
- #### ドメイン層:エンティティ
512
-
513
- <details>
514
- <summary>仕訳エンティティ</summary>
515
-
516
- ```java
517
- // src/main/java/com/example/accounting/domain/model/journal/Journal.java
518
- package com.example.fas.domain.model.journal;
519
-
520
- import lombok.AllArgsConstructor;
521
- import lombok.Builder;
522
- import lombok.Data;
523
- import lombok.NoArgsConstructor;
524
-
525
- import java.math.BigDecimal;
526
- import java.time.LocalDate;
527
- import java.time.LocalDateTime;
528
- import java.util.ArrayList;
529
- import java.util.List;
530
-
531
- @Data
532
- @Builder
533
- @NoArgsConstructor
534
- @AllArgsConstructor
535
- public class Journal {
536
- private String journalVoucherNumber; // 仕訳伝票番号
537
- private LocalDate postingDate; // 起票日
538
- private LocalDate entryDate; // 入力日
539
- private Boolean closingJournalFlag; // 決算仕訳フラグ
540
- private Boolean singleEntryFlag; // 単振フラグ
541
- private JournalVoucherType voucherType; // 仕訳伝票区分
542
- private Boolean periodicPostingFlag; // 定期計上フラグ
543
- private String employeeCode; // 社員コード
544
- private String departmentCode; // 部門コード
545
- private Boolean redSlipFlag; // 赤伝フラグ
546
- private Integer redBlackVoucherNumber; // 赤黒伝票番号
547
- private LocalDateTime createdAt; // 作成日時
548
- private LocalDateTime updatedAt; // 更新日時
549
-
550
- // リレーション
551
- @Builder.Default
552
- private List<JournalDetail> details = new ArrayList<>();
553
-
554
- /**
555
- * 貸借バランスチェック
556
- * @return 借方合計と貸方合計が一致する場合 true
557
- */
558
- public boolean isBalanced() {
559
- BigDecimal debitTotal = details.stream()
560
- .flatMap(d -> d.getDebitCreditDetails().stream())
561
- .filter(dc -> dc.getDebitCreditType() == DebitCreditType.DEBIT)
562
- .map(JournalDebitCreditDetail::getAmount)
563
- .reduce(BigDecimal.ZERO, BigDecimal::add);
564
-
565
- BigDecimal creditTotal = details.stream()
566
- .flatMap(d -> d.getDebitCreditDetails().stream())
567
- .filter(dc -> dc.getDebitCreditType() == DebitCreditType.CREDIT)
568
- .map(JournalDebitCreditDetail::getAmount)
569
- .reduce(BigDecimal.ZERO, BigDecimal::add);
570
-
571
- return debitTotal.compareTo(creditTotal) == 0;
572
- }
573
-
574
- /**
575
- * 借方合計を取得
576
- */
577
- public BigDecimal getDebitTotal() {
578
- return details.stream()
579
- .flatMap(d -> d.getDebitCreditDetails().stream())
580
- .filter(dc -> dc.getDebitCreditType() == DebitCreditType.DEBIT)
581
- .map(JournalDebitCreditDetail::getAmount)
582
- .reduce(BigDecimal.ZERO, BigDecimal::add);
583
- }
584
-
585
- /**
586
- * 貸方合計を取得
587
- */
588
- public BigDecimal getCreditTotal() {
589
- return details.stream()
590
- .flatMap(d -> d.getDebitCreditDetails().stream())
591
- .filter(dc -> dc.getDebitCreditType() == DebitCreditType.CREDIT)
592
- .map(JournalDebitCreditDetail::getAmount)
593
- .reduce(BigDecimal.ZERO, BigDecimal::add);
594
- }
595
- }
596
- ```
597
-
598
- </details>
599
-
600
- <details>
601
- <summary>仕訳明細エンティティ</summary>
602
-
603
- ```java
604
- // src/main/java/com/example/accounting/domain/model/journal/JournalDetail.java
605
- package com.example.fas.domain.model.journal;
606
-
607
- import lombok.AllArgsConstructor;
608
- import lombok.Builder;
609
- import lombok.Data;
610
- import lombok.NoArgsConstructor;
611
-
612
- import java.time.LocalDateTime;
613
- import java.util.ArrayList;
614
- import java.util.List;
615
-
616
- @Data
617
- @Builder
618
- @NoArgsConstructor
619
- @AllArgsConstructor
620
- public class JournalDetail {
621
- private String journalVoucherNumber; // 仕訳伝票番号
622
- private Integer lineNumber; // 仕訳行番号
623
- private String lineSummary; // 行摘要
624
- private LocalDateTime createdAt; // 作成日時
625
- private LocalDateTime updatedAt; // 更新日時
626
-
627
- // リレーション
628
- @Builder.Default
629
- private List<JournalDebitCreditDetail> debitCreditDetails = new ArrayList<>();
630
- }
631
- ```
632
-
633
- </details>
634
-
635
- <details>
636
- <summary>仕訳貸借明細エンティティ</summary>
637
-
638
- ```java
639
- // src/main/java/com/example/accounting/domain/model/journal/JournalDebitCreditDetail.java
640
- package com.example.fas.domain.model.journal;
641
-
642
- import com.example.fas.domain.model.account.DebitCreditType;
643
- import lombok.AllArgsConstructor;
644
- import lombok.Builder;
645
- import lombok.Data;
646
- import lombok.NoArgsConstructor;
647
-
648
- import java.math.BigDecimal;
649
- import java.time.LocalDate;
650
- import java.time.LocalDateTime;
651
-
652
- @Data
653
- @Builder
654
- @NoArgsConstructor
655
- @AllArgsConstructor
656
- public class JournalDebitCreditDetail {
657
- private String journalVoucherNumber; // 仕訳伝票番号
658
- private Integer lineNumber; // 仕訳行番号
659
- private DebitCreditType debitCreditType; // 仕訳行貸借区分
660
- private String accountCode; // 勘定科目コード
661
- private String subAccountCode; // 補助科目コード
662
- private String departmentCode; // 部門コード
663
- private String projectCode; // プロジェクトコード
664
- private BigDecimal amount; // 仕訳金額
665
- private String currencyCode; // 通貨コード
666
- private BigDecimal exchangeRate; // 為替レート
667
- private BigDecimal baseCurrencyAmount; // 基軸換算仕訳金額
668
- private TaxType taxType; // 消費税区分
669
- private Integer taxRate; // 消費税率
670
- private TaxCalculationType taxCalcType; // 消費税計算区分
671
- private LocalDate dueDate; // 期日
672
- private Boolean cashFlowFlag; // 資金繰フラグ
673
- private String segmentCode; // セグメントコード
674
- private String counterAccountCode; // 相手勘定科目コード
675
- private String counterSubAccountCode; // 相手補助科目コード
676
- private String tagCode; // 付箋コード
677
- private String tagContent; // 付箋内容
678
- private LocalDateTime createdAt; // 作成日時
679
- private LocalDateTime updatedAt; // 更新日時
680
- }
681
- ```
682
-
683
- </details>
684
-
685
- #### テストコード
686
-
687
- <details>
688
- <summary>仕訳リポジトリテスト</summary>
689
-
690
- ```java
691
- // src/test/java/com/example/accounting/infrastructure/persistence/repository/JournalRepositoryTest.java
692
- package com.example.fas.infrastructure.persistence.repository;
693
-
694
- import com.example.fas.application.port.out.JournalRepository;
695
- import com.example.fas.domain.model.account.DebitCreditType;
696
- import com.example.fas.domain.model.journal.*;
697
- import com.example.fas.testsetup.BaseIntegrationTest;
698
- import org.junit.jupiter.api.*;
699
- import org.springframework.beans.factory.annotation.Autowired;
700
-
701
- import java.math.BigDecimal;
702
- import java.time.LocalDate;
703
- import java.util.List;
704
-
705
- import static org.assertj.core.api.Assertions.*;
706
-
707
- @DisplayName("仕訳リポジトリ")
708
- class JournalRepositoryTest extends BaseIntegrationTest {
709
-
710
- @Autowired
711
- private JournalRepository journalRepository;
712
-
713
- @BeforeEach
714
- void setUp() {
715
- journalRepository.deleteAll();
716
- }
717
-
718
- @Nested
719
- @DisplayName("仕訳の登録")
720
- class Registration {
721
-
722
- @Test
723
- @DisplayName("単純仕訳(1行借方・1行貸方)を登録できる")
724
- void canRegisterSimpleJournal() {
725
- // Arrange: 現金売上の仕訳データ
726
- var journal = createSimpleSalesJournal("J0001");
727
-
728
- // Act: 仕訳を登録
729
- journalRepository.save(journal);
730
-
731
- // Assert: 登録されたデータを確認
732
- var savedJournal = journalRepository.findByVoucherNumber("J0001");
733
- assertThat(savedJournal).isPresent();
734
- assertThat(savedJournal.get().getPostingDate()).isEqualTo(LocalDate.of(2024, 4, 1));
735
- assertThat(savedJournal.get().isBalanced()).isTrue();
736
- }
737
-
738
- @Test
739
- @DisplayName("複合仕訳(複数行)を登録できる")
740
- void canRegisterCompoundJournal() {
741
- // Arrange: 売掛金回収の複合仕訳
742
- var journal = createCompoundJournal("J0002");
743
-
744
- // Act: 仕訳を登録
745
- journalRepository.save(journal);
746
-
747
- // Assert: 登録されたデータを確認
748
- var savedJournal = journalRepository.findByVoucherNumber("J0002");
749
- assertThat(savedJournal).isPresent();
750
- assertThat(savedJournal.get().getDetails()).hasSize(2);
751
- assertThat(savedJournal.get().isBalanced()).isTrue();
752
- }
753
-
754
- @Test
755
- @DisplayName("貸借が一致しない仕訳はバランスチェックでfalseを返す")
756
- void shouldReturnFalseForUnbalancedJournal() {
757
- // Arrange: 貸借不一致の仕訳データ
758
- var journal = createUnbalancedJournal("J0003");
759
-
760
- // Assert: バランスチェック
761
- assertThat(journal.isBalanced()).isFalse();
762
- }
763
- }
764
-
765
- @Nested
766
- @DisplayName("仕訳の検索")
767
- class Search {
768
-
769
- @BeforeEach
770
- void setUpTestData() {
771
- journalRepository.save(createSimpleSalesJournal("J0001"));
772
- journalRepository.save(createCompoundJournal("J0002"));
773
- }
774
-
775
- @Test
776
- @DisplayName("起票日範囲で仕訳を検索できる")
777
- void canFindByDateRange() {
778
- // Act
779
- var journals = journalRepository.findByPostingDateBetween(
780
- LocalDate.of(2024, 4, 1),
781
- LocalDate.of(2024, 4, 30)
782
- );
783
-
784
- // Assert
785
- assertThat(journals).hasSize(2);
786
- }
787
-
788
- @Test
789
- @DisplayName("勘定科目コードで仕訳を検索できる")
790
- void canFindByAccountCode() {
791
- // Act
792
- var journals = journalRepository.findByAccountCode("11110");
793
-
794
- // Assert
795
- assertThat(journals).isNotEmpty();
796
- }
797
- }
798
-
799
- private Journal createSimpleSalesJournal(String voucherNumber) {
800
- var debitDetail = JournalDebitCreditDetail.builder()
801
- .journalVoucherNumber(voucherNumber)
802
- .lineNumber(1)
803
- .debitCreditType(DebitCreditType.DEBIT)
804
- .accountCode("11110") // 現金
805
- .amount(new BigDecimal("100000"))
806
- .currencyCode("JPY")
807
- .exchangeRate(BigDecimal.ONE)
808
- .build();
809
-
810
- var creditDetail = JournalDebitCreditDetail.builder()
811
- .journalVoucherNumber(voucherNumber)
812
- .lineNumber(1)
813
- .debitCreditType(DebitCreditType.CREDIT)
814
- .accountCode("41100") // 売上
815
- .amount(new BigDecimal("100000"))
816
- .currencyCode("JPY")
817
- .exchangeRate(BigDecimal.ONE)
818
- .build();
819
-
820
- var journalDetail = JournalDetail.builder()
821
- .journalVoucherNumber(voucherNumber)
822
- .lineNumber(1)
823
- .lineSummary("現金売上")
824
- .debitCreditDetails(List.of(debitDetail, creditDetail))
825
- .build();
826
-
827
- return Journal.builder()
828
- .journalVoucherNumber(voucherNumber)
829
- .postingDate(LocalDate.of(2024, 4, 1))
830
- .entryDate(LocalDate.now())
831
- .closingJournalFlag(false)
832
- .singleEntryFlag(true)
833
- .voucherType(JournalVoucherType.NORMAL)
834
- .periodicPostingFlag(false)
835
- .redSlipFlag(false)
836
- .details(List.of(journalDetail))
837
- .build();
838
- }
839
-
840
- private Journal createCompoundJournal(String voucherNumber) {
841
- // 売掛金100,000円を現金80,000円と受取手形20,000円で回収
842
- var debit1 = JournalDebitCreditDetail.builder()
843
- .journalVoucherNumber(voucherNumber)
844
- .lineNumber(1)
845
- .debitCreditType(DebitCreditType.DEBIT)
846
- .accountCode("11110") // 現金
847
- .amount(new BigDecimal("80000"))
848
- .build();
849
-
850
- var credit1 = JournalDebitCreditDetail.builder()
851
- .journalVoucherNumber(voucherNumber)
852
- .lineNumber(1)
853
- .debitCreditType(DebitCreditType.CREDIT)
854
- .accountCode("11300") // 売掛金
855
- .amount(new BigDecimal("100000"))
856
- .build();
857
-
858
- var debit2 = JournalDebitCreditDetail.builder()
859
- .journalVoucherNumber(voucherNumber)
860
- .lineNumber(2)
861
- .debitCreditType(DebitCreditType.DEBIT)
862
- .accountCode("11200") // 受取手形
863
- .amount(new BigDecimal("20000"))
864
- .build();
865
-
866
- var detail1 = JournalDetail.builder()
867
- .journalVoucherNumber(voucherNumber)
868
- .lineNumber(1)
869
- .lineSummary("現金入金")
870
- .debitCreditDetails(List.of(debit1, credit1))
871
- .build();
872
-
873
- var detail2 = JournalDetail.builder()
874
- .journalVoucherNumber(voucherNumber)
875
- .lineNumber(2)
876
- .lineSummary("手形入金")
877
- .debitCreditDetails(List.of(debit2))
878
- .build();
879
-
880
- return Journal.builder()
881
- .journalVoucherNumber(voucherNumber)
882
- .postingDate(LocalDate.of(2024, 4, 5))
883
- .entryDate(LocalDate.now())
884
- .closingJournalFlag(false)
885
- .singleEntryFlag(false) // 複合仕訳
886
- .voucherType(JournalVoucherType.NORMAL)
887
- .details(List.of(detail1, detail2))
888
- .build();
889
- }
890
-
891
- private Journal createUnbalancedJournal(String voucherNumber) {
892
- var debitDetail = JournalDebitCreditDetail.builder()
893
- .journalVoucherNumber(voucherNumber)
894
- .lineNumber(1)
895
- .debitCreditType(DebitCreditType.DEBIT)
896
- .accountCode("11110")
897
- .amount(new BigDecimal("100000"))
898
- .build();
899
-
900
- var creditDetail = JournalDebitCreditDetail.builder()
901
- .journalVoucherNumber(voucherNumber)
902
- .lineNumber(1)
903
- .debitCreditType(DebitCreditType.CREDIT)
904
- .accountCode("41100")
905
- .amount(new BigDecimal("90000")) // 不一致
906
- .build();
907
-
908
- var journalDetail = JournalDetail.builder()
909
- .journalVoucherNumber(voucherNumber)
910
- .lineNumber(1)
911
- .debitCreditDetails(List.of(debitDetail, creditDetail))
912
- .build();
913
-
914
- return Journal.builder()
915
- .journalVoucherNumber(voucherNumber)
916
- .postingDate(LocalDate.of(2024, 4, 1))
917
- .details(List.of(journalDetail))
918
- .build();
919
- }
920
- }
921
- ```
922
-
923
- </details>
924
-
925
- #### アプリケーション層(Output Port)
926
-
927
- <details>
928
- <summary>仕訳リポジトリインターフェース</summary>
929
-
930
- ```java
931
- // src/main/java/com/example/accounting/application/port/out/JournalRepository.java
932
- package com.example.fas.application.port.out;
933
-
934
- import com.example.fas.domain.model.journal.Journal;
935
-
936
- import java.time.LocalDate;
937
- import java.util.List;
938
- import java.util.Optional;
939
-
940
- /**
941
- * 仕訳リポジトリ(Output Port)
942
- */
943
- public interface JournalRepository {
944
-
945
- void save(Journal journal);
946
-
947
- Optional<Journal> findByVoucherNumber(String voucherNumber);
948
-
949
- List<Journal> findByPostingDateBetween(LocalDate fromDate, LocalDate toDate);
950
-
951
- List<Journal> findByAccountCode(String accountCode);
952
-
953
- List<Journal> findByDepartmentCode(String departmentCode);
954
-
955
- void delete(String voucherNumber);
956
-
957
- void deleteAll();
958
- }
959
- ```
960
-
961
- </details>
962
-
963
- #### インフラストラクチャ層
964
-
965
- <details>
966
- <summary>MyBatis XML Mapper</summary>
967
-
968
- ```xml
969
- <?xml version="1.0" encoding="UTF-8" ?>
970
- <!DOCTYPE mapper
971
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
972
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
973
- <mapper namespace="com.example.fas.infrastructure.persistence.mapper.JournalMapper">
974
-
975
- <!-- 仕訳 ResultMap -->
976
- <resultMap id="JournalResultMap" type="com.example.fas.domain.model.journal.Journal">
977
- <result property="journalVoucherNumber" column="仕訳伝票番号"/>
978
- <result property="postingDate" column="起票日"/>
979
- <result property="entryDate" column="入力日"/>
980
- <result property="closingJournalFlag" column="決算仕訳フラグ"/>
981
- <result property="singleEntryFlag" column="単振フラグ"/>
982
- <result property="voucherType" column="仕訳伝票区分"
983
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler"/>
984
- <result property="periodicPostingFlag" column="定期計上フラグ"/>
985
- <result property="employeeCode" column="社員コード"/>
986
- <result property="departmentCode" column="部門コード"/>
987
- <result property="redSlipFlag" column="赤伝フラグ"/>
988
- <result property="redBlackVoucherNumber" column="赤黒伝票番号"/>
989
- <result property="createdAt" column="作成日時"/>
990
- <result property="updatedAt" column="更新日時"/>
991
- </resultMap>
992
-
993
- <!-- 仕訳明細 ResultMap -->
994
- <resultMap id="JournalDetailResultMap" type="com.example.fas.domain.model.journal.JournalDetail">
995
- <result property="journalVoucherNumber" column="仕訳伝票番号"/>
996
- <result property="lineNumber" column="仕訳行番号"/>
997
- <result property="lineSummary" column="行摘要"/>
998
- <result property="createdAt" column="作成日時"/>
999
- <result property="updatedAt" column="更新日時"/>
1000
- </resultMap>
1001
-
1002
- <!-- 仕訳貸借明細 ResultMap -->
1003
- <resultMap id="JournalDebitCreditDetailResultMap" type="com.example.fas.domain.model.journal.JournalDebitCreditDetail">
1004
- <result property="journalVoucherNumber" column="仕訳伝票番号"/>
1005
- <result property="lineNumber" column="仕訳行番号"/>
1006
- <result property="debitCreditType" column="仕訳行貸借区分"
1007
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.DebitCreditTypeHandler"/>
1008
- <result property="accountCode" column="勘定科目コード"/>
1009
- <result property="subAccountCode" column="補助科目コード"/>
1010
- <result property="departmentCode" column="部門コード"/>
1011
- <result property="projectCode" column="プロジェクトコード"/>
1012
- <result property="amount" column="仕訳金額"/>
1013
- <result property="currencyCode" column="通貨コード"/>
1014
- <result property="exchangeRate" column="為替レート"/>
1015
- <result property="baseCurrencyAmount" column="基軸換算仕訳金額"/>
1016
- <result property="taxType" column="消費税区分"
1017
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxTypeHandler"/>
1018
- <result property="taxRate" column="消費税率"/>
1019
- <result property="taxCalcType" column="消費税計算区分"
1020
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxCalculationTypeHandler"/>
1021
- <result property="dueDate" column="期日"/>
1022
- <result property="cashFlowFlag" column="資金繰フラグ"/>
1023
- <result property="segmentCode" column="セグメントコード"/>
1024
- <result property="counterAccountCode" column="相手勘定科目コード"/>
1025
- <result property="counterSubAccountCode" column="相手補助科目コード"/>
1026
- <result property="tagCode" column="付箋コード"/>
1027
- <result property="tagContent" column="付箋内容"/>
1028
- <result property="createdAt" column="作成日時"/>
1029
- <result property="updatedAt" column="更新日時"/>
1030
- </resultMap>
1031
-
1032
- <!-- 仕訳ヘッダ登録 -->
1033
- <insert id="insertJournal" parameterType="com.example.fas.domain.model.journal.Journal">
1034
- INSERT INTO "仕訳" (
1035
- "仕訳伝票番号", "起票日", "入力日", "決算仕訳フラグ", "単振フラグ",
1036
- "仕訳伝票区分", "定期計上フラグ", "社員コード", "部門コード",
1037
- "赤伝フラグ", "赤黒伝票番号", "作成日時", "更新日時"
1038
- ) VALUES (
1039
- #{journalVoucherNumber}, #{postingDate}, #{entryDate},
1040
- #{closingJournalFlag}, #{singleEntryFlag},
1041
- #{voucherType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler}::仕訳伝票区分,
1042
- #{periodicPostingFlag}, #{employeeCode}, #{departmentCode},
1043
- #{redSlipFlag}, #{redBlackVoucherNumber},
1044
- CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
1045
- )
1046
- </insert>
1047
-
1048
- <!-- 仕訳明細登録 -->
1049
- <insert id="insertJournalDetail" parameterType="com.example.fas.domain.model.journal.JournalDetail">
1050
- INSERT INTO "仕訳明細" (
1051
- "仕訳伝票番号", "仕訳行番号", "行摘要", "作成日時", "更新日時"
1052
- ) VALUES (
1053
- #{journalVoucherNumber}, #{lineNumber}, #{lineSummary},
1054
- CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
1055
- )
1056
- </insert>
1057
-
1058
- <!-- 仕訳貸借明細登録 -->
1059
- <insert id="insertJournalDebitCreditDetail" parameterType="com.example.fas.domain.model.journal.JournalDebitCreditDetail">
1060
- INSERT INTO "仕訳貸借明細" (
1061
- "仕訳伝票番号", "仕訳行番号", "仕訳行貸借区分",
1062
- "勘定科目コード", "補助科目コード", "部門コード", "プロジェクトコード",
1063
- "仕訳金額", "通貨コード", "為替レート", "基軸換算仕訳金額",
1064
- "消費税区分", "消費税率", "消費税計算区分",
1065
- "期日", "資金繰フラグ", "セグメントコード",
1066
- "相手勘定科目コード", "相手補助科目コード",
1067
- "付箋コード", "付箋内容", "作成日時", "更新日時"
1068
- ) VALUES (
1069
- #{journalVoucherNumber}, #{lineNumber},
1070
- #{debitCreditType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.DebitCreditTypeHandler}::仕訳行貸借区分,
1071
- #{accountCode}, #{subAccountCode}, #{departmentCode}, #{projectCode},
1072
- #{amount}, #{currencyCode}, #{exchangeRate}, #{baseCurrencyAmount},
1073
- #{taxType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.TaxTypeHandler}::消費税区分,
1074
- #{taxRate},
1075
- #{taxCalcType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.TaxCalculationTypeHandler}::消費税計算区分,
1076
- #{dueDate}, #{cashFlowFlag}, #{segmentCode},
1077
- #{counterAccountCode}, #{counterSubAccountCode},
1078
- #{tagCode}, #{tagContent}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
1079
- )
1080
- </insert>
1081
-
1082
- <!-- 仕訳伝票番号で検索 -->
1083
- <select id="findByVoucherNumber" resultMap="JournalResultMap">
1084
- SELECT * FROM "仕訳"
1085
- WHERE "仕訳伝票番号" = #{voucherNumber}
1086
- </select>
1087
-
1088
- <!-- 仕訳明細検索 -->
1089
- <select id="findDetailsByVoucherNumber" resultMap="JournalDetailResultMap">
1090
- SELECT * FROM "仕訳明細"
1091
- WHERE "仕訳伝票番号" = #{voucherNumber}
1092
- ORDER BY "仕訳行番号"
1093
- </select>
1094
-
1095
- <!-- 仕訳貸借明細検索 -->
1096
- <select id="findDCDetailsByVoucherAndLine" resultMap="JournalDebitCreditDetailResultMap">
1097
- SELECT * FROM "仕訳貸借明細"
1098
- WHERE "仕訳伝票番号" = #{voucherNumber}
1099
- AND "仕訳行番号" = #{lineNumber}
1100
- </select>
1101
-
1102
- <!-- 起票日範囲で検索 -->
1103
- <select id="findVoucherNumbersByDateRange" resultType="String">
1104
- SELECT "仕訳伝票番号" FROM "仕訳"
1105
- WHERE "起票日" BETWEEN #{fromDate} AND #{toDate}
1106
- ORDER BY "仕訳伝票番号"
1107
- </select>
1108
-
1109
- <!-- 勘定科目コードで検索 -->
1110
- <select id="findVoucherNumbersByAccountCode" resultType="String">
1111
- SELECT DISTINCT "仕訳伝票番号" FROM "仕訳貸借明細"
1112
- WHERE "勘定科目コード" = #{accountCode}
1113
- ORDER BY "仕訳伝票番号"
1114
- </select>
1115
-
1116
- <!-- 削除 -->
1117
- <delete id="deleteJournal">
1118
- DELETE FROM "仕訳" WHERE "仕訳伝票番号" = #{voucherNumber}
1119
- </delete>
1120
-
1121
- <delete id="deleteAll">
1122
- TRUNCATE TABLE "仕訳" CASCADE
1123
- </delete>
1124
- </mapper>
1125
- ```
1126
-
1127
- </details>
1128
-
1129
- <details>
1130
- <summary>仕訳リポジトリ実装</summary>
1131
-
1132
- ```java
1133
- // src/main/java/com/example/accounting/infrastructure/persistence/repository/JournalRepositoryImpl.java
1134
- package com.example.fas.infrastructure.persistence.repository;
1135
-
1136
- import com.example.fas.application.port.out.JournalRepository;
1137
- import com.example.fas.domain.model.journal.Journal;
1138
- import com.example.fas.infrastructure.persistence.mapper.JournalMapper;
1139
- import lombok.RequiredArgsConstructor;
1140
- import org.springframework.stereotype.Repository;
1141
- import org.springframework.transaction.annotation.Transactional;
1142
-
1143
- import java.time.LocalDate;
1144
- import java.util.List;
1145
- import java.util.Optional;
1146
- import java.util.stream.Collectors;
1147
-
1148
- @Repository
1149
- @RequiredArgsConstructor
1150
- public class JournalRepositoryImpl implements JournalRepository {
1151
-
1152
- private final JournalMapper journalMapper;
1153
-
1154
- @Override
1155
- @Transactional
1156
- public void save(Journal journal) {
1157
- // 1. 仕訳ヘッダを登録
1158
- journalMapper.insertJournal(journal);
1159
-
1160
- // 2. 仕訳明細を登録
1161
- journal.getDetails().forEach(detail -> {
1162
- journalMapper.insertJournalDetail(detail);
1163
-
1164
- // 3. 仕訳貸借明細を登録
1165
- detail.getDebitCreditDetails().forEach(dcDetail -> {
1166
- journalMapper.insertJournalDebitCreditDetail(dcDetail);
1167
- });
1168
- });
1169
- }
1170
-
1171
- @Override
1172
- public Optional<Journal> findByVoucherNumber(String voucherNumber) {
1173
- return journalMapper.findByVoucherNumber(voucherNumber)
1174
- .map(journal -> {
1175
- var details = journalMapper.findDetailsByVoucherNumber(voucherNumber);
1176
- details.forEach(detail -> {
1177
- var dcDetails = journalMapper.findDCDetailsByVoucherAndLine(
1178
- voucherNumber, detail.getLineNumber());
1179
- detail.setDebitCreditDetails(dcDetails);
1180
- });
1181
- journal.setDetails(details);
1182
- return journal;
1183
- });
1184
- }
1185
-
1186
- @Override
1187
- public List<Journal> findByPostingDateBetween(LocalDate fromDate, LocalDate toDate) {
1188
- return journalMapper.findVoucherNumbersByDateRange(fromDate, toDate).stream()
1189
- .map(this::findByVoucherNumber)
1190
- .filter(Optional::isPresent)
1191
- .map(Optional::get)
1192
- .collect(Collectors.toList());
1193
- }
1194
-
1195
- @Override
1196
- public List<Journal> findByAccountCode(String accountCode) {
1197
- return journalMapper.findVoucherNumbersByAccountCode(accountCode).stream()
1198
- .map(this::findByVoucherNumber)
1199
- .filter(Optional::isPresent)
1200
- .map(Optional::get)
1201
- .collect(Collectors.toList());
1202
- }
1203
-
1204
- @Override
1205
- public List<Journal> findByDepartmentCode(String departmentCode) {
1206
- return journalMapper.findVoucherNumbersByDepartmentCode(departmentCode).stream()
1207
- .map(this::findByVoucherNumber)
1208
- .filter(Optional::isPresent)
1209
- .map(Optional::get)
1210
- .collect(Collectors.toList());
1211
- }
1212
-
1213
- @Override
1214
- @Transactional
1215
- public void delete(String voucherNumber) {
1216
- journalMapper.deleteJournal(voucherNumber);
1217
- }
1218
-
1219
- @Override
1220
- @Transactional
1221
- public void deleteAll() {
1222
- journalMapper.deleteAll();
1223
- }
1224
- }
1225
- ```
1226
-
1227
- </details>
1228
-
1229
- ---
1230
-
1231
- ## 16.4 リレーションと楽観ロックの設計
1232
-
1233
- ### MyBatis ネストした ResultMap によるリレーション設定
1234
-
1235
- 仕訳データは、仕訳(ヘッダ)→ 仕訳明細 → 仕訳貸借明細の3層構造を持ちます。MyBatis でこの親子関係を効率的に取得するためのリレーション設定を実装します。
1236
-
1237
- #### ネストした ResultMap の定義
1238
-
1239
- <details>
1240
- <summary>JournalMapper.xml(リレーション設定)</summary>
1241
-
1242
- ```xml
1243
- <?xml version="1.0" encoding="UTF-8" ?>
1244
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
1245
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
1246
-
1247
- <!-- src/main/resources/mapper/JournalMapper.xml -->
1248
- <mapper namespace="com.example.fas.infrastructure.persistence.mapper.JournalMapper">
1249
-
1250
- <!-- 仕訳(ヘッダ)の ResultMap(リレーション込み) -->
1251
- <resultMap id="journalWithDetailsResultMap" type="com.example.fas.domain.model.journal.Journal">
1252
- <id property="journalVoucherNumber" column="j_仕訳伝票番号"/>
1253
- <result property="postingDate" column="j_起票日"/>
1254
- <result property="entryDate" column="j_入力日"/>
1255
- <result property="closingJournalFlag" column="j_決算仕訳フラグ"/>
1256
- <result property="singleEntryFlag" column="j_単振フラグ"/>
1257
- <result property="voucherType" column="j_仕訳伝票区分"
1258
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler"/>
1259
- <result property="periodicPostingFlag" column="j_定期計上フラグ"/>
1260
- <result property="employeeCode" column="j_社員コード"/>
1261
- <result property="departmentCode" column="j_部門コード"/>
1262
- <result property="redSlipFlag" column="j_赤伝フラグ"/>
1263
- <result property="redBlackVoucherNumber" column="j_赤黒伝票番号"/>
1264
- <result property="version" column="j_バージョン"/>
1265
- <result property="createdAt" column="j_作成日時"/>
1266
- <result property="updatedAt" column="j_更新日時"/>
1267
- <!-- 仕訳明細との1:N関連 -->
1268
- <collection property="details" ofType="com.example.fas.domain.model.journal.JournalDetail"
1269
- resultMap="journalDetailNestedResultMap"/>
1270
- </resultMap>
1271
-
1272
- <!-- 仕訳明細のネスト ResultMap -->
1273
- <resultMap id="journalDetailNestedResultMap" type="com.example.fas.domain.model.journal.JournalDetail">
1274
- <id property="journalVoucherNumber" column="d_仕訳伝票番号"/>
1275
- <id property="lineNumber" column="d_仕訳行番号"/>
1276
- <result property="lineSummary" column="d_行摘要"/>
1277
- <result property="version" column="d_バージョン"/>
1278
- <result property="createdAt" column="d_作成日時"/>
1279
- <result property="updatedAt" column="d_更新日時"/>
1280
- <!-- 仕訳貸借明細との1:N関連 -->
1281
- <collection property="debitCreditDetails" ofType="com.example.fas.domain.model.journal.JournalDebitCreditDetail"
1282
- resultMap="journalDebitCreditDetailNestedResultMap"/>
1283
- </resultMap>
1284
-
1285
- <!-- 仕訳貸借明細のネスト ResultMap -->
1286
- <resultMap id="journalDebitCreditDetailNestedResultMap" type="com.example.fas.domain.model.journal.JournalDebitCreditDetail">
1287
- <id property="journalVoucherNumber" column="dc_仕訳伝票番号"/>
1288
- <id property="lineNumber" column="dc_仕訳行番号"/>
1289
- <id property="debitCreditType" column="dc_仕訳行貸借区分"
1290
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.DebitCreditTypeHandler"/>
1291
- <result property="accountCode" column="dc_勘定科目コード"/>
1292
- <result property="subAccountCode" column="dc_補助科目コード"/>
1293
- <result property="departmentCode" column="dc_部門コード"/>
1294
- <result property="projectCode" column="dc_プロジェクトコード"/>
1295
- <result property="amount" column="dc_仕訳金額"/>
1296
- <result property="currencyCode" column="dc_通貨コード"/>
1297
- <result property="exchangeRate" column="dc_為替レート"/>
1298
- <result property="baseCurrencyAmount" column="dc_基軸換算仕訳金額"/>
1299
- <result property="taxType" column="dc_消費税区分"
1300
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxTypeHandler"/>
1301
- <result property="taxRate" column="dc_消費税率"/>
1302
- <result property="taxCalcType" column="dc_消費税計算区分"
1303
- typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxCalculationTypeHandler"/>
1304
- <result property="dueDate" column="dc_期日"/>
1305
- <result property="cashFlowFlag" column="dc_資金繰フラグ"/>
1306
- <result property="segmentCode" column="dc_セグメントコード"/>
1307
- <result property="counterAccountCode" column="dc_相手勘定科目コード"/>
1308
- <result property="counterSubAccountCode" column="dc_相手補助科目コード"/>
1309
- <result property="tagCode" column="dc_付箋コード"/>
1310
- <result property="tagContent" column="dc_付箋内容"/>
1311
- <result property="version" column="dc_バージョン"/>
1312
- <result property="createdAt" column="dc_作成日時"/>
1313
- <result property="updatedAt" column="dc_更新日時"/>
1314
- </resultMap>
1315
-
1316
- <!-- JOIN による一括取得クエリ(3層構造) -->
1317
- <select id="findWithDetailsByVoucherNumber" resultMap="journalWithDetailsResultMap">
1318
- SELECT
1319
- j."仕訳伝票番号" AS j_仕訳伝票番号,
1320
- j."起票日" AS j_起票日,
1321
- j."入力日" AS j_入力日,
1322
- j."決算仕訳フラグ" AS j_決算仕訳フラグ,
1323
- j."単振フラグ" AS j_単振フラグ,
1324
- j."仕訳伝票区分" AS j_仕訳伝票区分,
1325
- j."定期計上フラグ" AS j_定期計上フラグ,
1326
- j."社員コード" AS j_社員コード,
1327
- j."部門コード" AS j_部門コード,
1328
- j."赤伝フラグ" AS j_赤伝フラグ,
1329
- j."赤黒伝票番号" AS j_赤黒伝票番号,
1330
- j."バージョン" AS j_バージョン,
1331
- j."作成日時" AS j_作成日時,
1332
- j."更新日時" AS j_更新日時,
1333
- d."仕訳伝票番号" AS d_仕訳伝票番号,
1334
- d."仕訳行番号" AS d_仕訳行番号,
1335
- d."行摘要" AS d_行摘要,
1336
- d."バージョン" AS d_バージョン,
1337
- d."作成日時" AS d_作成日時,
1338
- d."更新日時" AS d_更新日時,
1339
- dc."仕訳伝票番号" AS dc_仕訳伝票番号,
1340
- dc."仕訳行番号" AS dc_仕訳行番号,
1341
- dc."仕訳行貸借区分" AS dc_仕訳行貸借区分,
1342
- dc."勘定科目コード" AS dc_勘定科目コード,
1343
- dc."補助科目コード" AS dc_補助科目コード,
1344
- dc."部門コード" AS dc_部門コード,
1345
- dc."プロジェクトコード" AS dc_プロジェクトコード,
1346
- dc."仕訳金額" AS dc_仕訳金額,
1347
- dc."通貨コード" AS dc_通貨コード,
1348
- dc."為替レート" AS dc_為替レート,
1349
- dc."基軸換算仕訳金額" AS dc_基軸換算仕訳金額,
1350
- dc."消費税区分" AS dc_消費税区分,
1351
- dc."消費税率" AS dc_消費税率,
1352
- dc."消費税計算区分" AS dc_消費税計算区分,
1353
- dc."期日" AS dc_期日,
1354
- dc."資金繰フラグ" AS dc_資金繰フラグ,
1355
- dc."セグメントコード" AS dc_セグメントコード,
1356
- dc."相手勘定科目コード" AS dc_相手勘定科目コード,
1357
- dc."相手補助科目コード" AS dc_相手補助科目コード,
1358
- dc."付箋コード" AS dc_付箋コード,
1359
- dc."付箋内容" AS dc_付箋内容,
1360
- dc."バージョン" AS dc_バージョン,
1361
- dc."作成日時" AS dc_作成日時,
1362
- dc."更新日時" AS dc_更新日時
1363
- FROM "仕訳" j
1364
- LEFT JOIN "仕訳明細" d ON j."仕訳伝票番号" = d."仕訳伝票番号"
1365
- LEFT JOIN "仕訳貸借明細" dc ON d."仕訳伝票番号" = dc."仕訳伝票番号"
1366
- AND d."仕訳行番号" = dc."仕訳行番号"
1367
- WHERE j."仕訳伝票番号" = #{voucherNumber}
1368
- ORDER BY d."仕訳行番号", dc."仕訳行貸借区分"
1369
- </select>
1370
-
1371
- </mapper>
1372
- ```
1373
-
1374
- </details>
1375
-
1376
- #### リレーション設定のポイント
1377
-
1378
- | 設定項目 | 説明 |
1379
- |---------|------|
1380
- | `<collection>` | 1:N 関連のマッピング |
1381
- | `<id>` | 主キーの識別(MyBatis が重複排除に使用) |
1382
- | 複合主キー | 仕訳明細・仕訳貸借明細は複数の `<id>` で複合主キーを表現 |
1383
- | `resultMap` | ネストした ResultMap の参照 |
1384
- | エイリアス(AS) | カラム名の重複を避けるためのプレフィックス(j_, d_, dc_) |
1385
- | `ORDER BY` | コレクションの順序を保証 |
1386
-
1387
- ### 楽観ロックの実装
1388
-
1389
- 複数ユーザーが同時に仕訳データを編集する場合、データの整合性を保つために楽観ロック(Optimistic Locking)を実装します。
1390
-
1391
- #### Flyway マイグレーション: バージョンカラム追加
1392
-
1393
- <details>
1394
- <summary>V006__add_journal_version_columns.sql</summary>
1395
-
1396
- ```sql
1397
- -- src/main/resources/db/migration/V006__add_journal_version_columns.sql
1398
-
1399
- -- 仕訳テーブルにバージョンカラムを追加
1400
- ALTER TABLE "仕訳" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
1401
-
1402
- -- 仕訳明細テーブルにバージョンカラムを追加
1403
- ALTER TABLE "仕訳明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
1404
-
1405
- -- 仕訳貸借明細テーブルにバージョンカラムを追加
1406
- ALTER TABLE "仕訳貸借明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
1407
-
1408
- -- コメント追加
1409
- COMMENT ON COLUMN "仕訳"."バージョン" IS '楽観ロック用バージョン番号';
1410
- COMMENT ON COLUMN "仕訳明細"."バージョン" IS '楽観ロック用バージョン番号';
1411
- COMMENT ON COLUMN "仕訳貸借明細"."バージョン" IS '楽観ロック用バージョン番号';
1412
- ```
1413
-
1414
- </details>
1415
-
1416
- #### エンティティへのバージョンフィールド追加
1417
-
1418
- <details>
1419
- <summary>Journal.java(バージョンフィールド追加)</summary>
1420
-
1421
- ```java
1422
- // src/main/java/com/example/fas/domain/model/journal/Journal.java
1423
- package com.example.fas.domain.model.journal;
1424
-
1425
- import lombok.AllArgsConstructor;
1426
- import lombok.Builder;
1427
- import lombok.Data;
1428
- import lombok.NoArgsConstructor;
1429
-
1430
- import java.math.BigDecimal;
1431
- import java.time.LocalDate;
1432
- import java.time.LocalDateTime;
1433
- import java.util.ArrayList;
1434
- import java.util.List;
1435
-
1436
- @Data
1437
- @Builder
1438
- @NoArgsConstructor
1439
- @AllArgsConstructor
1440
- public class Journal {
1441
- private String journalVoucherNumber; // 仕訳伝票番号
1442
- private LocalDate postingDate; // 起票日
1443
- private LocalDate entryDate; // 入力日
1444
- private Boolean closingJournalFlag; // 決算仕訳フラグ
1445
- private Boolean singleEntryFlag; // 単振フラグ
1446
- private JournalVoucherType voucherType; // 仕訳伝票区分
1447
- private Boolean periodicPostingFlag; // 定期計上フラグ
1448
- private String employeeCode; // 社員コード
1449
- private String departmentCode; // 部門コード
1450
- private Boolean redSlipFlag; // 赤伝フラグ
1451
- private Integer redBlackVoucherNumber; // 赤黒伝票番号
1452
- private LocalDateTime createdAt; // 作成日時
1453
- private LocalDateTime updatedAt; // 更新日時
1454
-
1455
- // 楽観ロック用バージョン
1456
- @Builder.Default
1457
- private Integer version = 1;
1458
-
1459
- // リレーション
1460
- @Builder.Default
1461
- private List<JournalDetail> details = new ArrayList<>();
1462
-
1463
- // 以下、既存のビジネスロジック...
1464
- }
1465
- ```
1466
-
1467
- </details>
1468
-
1469
- #### MyBatis Mapper: 楽観ロック対応の更新
1470
-
1471
- <details>
1472
- <summary>JournalMapper.xml(楽観ロック対応 UPDATE)</summary>
1473
-
1474
- ```xml
1475
- <!-- 楽観ロック対応の更新(バージョンチェック付き) -->
1476
- <update id="updateWithOptimisticLock" parameterType="com.example.fas.domain.model.journal.Journal">
1477
- UPDATE "仕訳"
1478
- SET
1479
- "起票日" = #{postingDate},
1480
- "入力日" = #{entryDate},
1481
- "決算仕訳フラグ" = #{closingJournalFlag},
1482
- "単振フラグ" = #{singleEntryFlag},
1483
- "仕訳伝票区分" = #{voucherType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler}::仕訳伝票区分,
1484
- "定期計上フラグ" = #{periodicPostingFlag},
1485
- "社員コード" = #{employeeCode},
1486
- "部門コード" = #{departmentCode},
1487
- "赤伝フラグ" = #{redSlipFlag},
1488
- "赤黒伝票番号" = #{redBlackVoucherNumber},
1489
- "更新日時" = CURRENT_TIMESTAMP,
1490
- "バージョン" = "バージョン" + 1
1491
- WHERE "仕訳伝票番号" = #{journalVoucherNumber}
1492
- AND "バージョン" = #{version}
1493
- </update>
1494
-
1495
- <!-- バージョンのみ取得 -->
1496
- <select id="findVersionByVoucherNumber" resultType="Integer">
1497
- SELECT "バージョン" FROM "仕訳"
1498
- WHERE "仕訳伝票番号" = #{voucherNumber}
1499
- </select>
1500
- ```
1501
-
1502
- </details>
1503
-
1504
- #### 楽観ロック例外クラス
1505
-
1506
- <details>
1507
- <summary>OptimisticLockException.java</summary>
1508
-
1509
- ```java
1510
- // src/main/java/com/example/fas/domain/exception/OptimisticLockException.java
1511
- package com.example.fas.domain.exception;
1512
-
1513
- public class OptimisticLockException extends RuntimeException {
1514
-
1515
- private final String entityName;
1516
- private final String entityId;
1517
- private final Integer expectedVersion;
1518
- private final Integer actualVersion;
1519
-
1520
- public OptimisticLockException(String entityName, String entityId) {
1521
- super(String.format("%s (ID: %s) は既に削除されています", entityName, entityId));
1522
- this.entityName = entityName;
1523
- this.entityId = entityId;
1524
- this.expectedVersion = null;
1525
- this.actualVersion = null;
1526
- }
1527
-
1528
- public OptimisticLockException(String entityName, String entityId,
1529
- Integer expectedVersion, Integer actualVersion) {
1530
- super(String.format("%s (ID: %s) は他のユーザーによって更新されています。" +
1531
- "期待バージョン: %d, 実際のバージョン: %d",
1532
- entityName, entityId, expectedVersion, actualVersion));
1533
- this.entityName = entityName;
1534
- this.entityId = entityId;
1535
- this.expectedVersion = expectedVersion;
1536
- this.actualVersion = actualVersion;
1537
- }
1538
-
1539
- public String getEntityName() {
1540
- return entityName;
1541
- }
1542
-
1543
- public String getEntityId() {
1544
- return entityId;
1545
- }
1546
-
1547
- public Integer getExpectedVersion() {
1548
- return expectedVersion;
1549
- }
1550
-
1551
- public Integer getActualVersion() {
1552
- return actualVersion;
1553
- }
1554
- }
1555
- ```
1556
-
1557
- </details>
1558
-
1559
- #### Repository 実装: 楽観ロック対応
1560
-
1561
- <details>
1562
- <summary>JournalRepositoryImpl.java(楽観ロック対応)</summary>
1563
-
1564
- ```java
1565
- // src/main/java/com/example/fas/infrastructure/persistence/repository/JournalRepositoryImpl.java
1566
- package com.example.fas.infrastructure.persistence.repository;
1567
-
1568
- import com.example.fas.application.port.out.JournalRepository;
1569
- import com.example.fas.domain.exception.OptimisticLockException;
1570
- import com.example.fas.domain.model.journal.Journal;
1571
- import com.example.fas.infrastructure.persistence.mapper.JournalMapper;
1572
- import lombok.RequiredArgsConstructor;
1573
- import org.springframework.stereotype.Repository;
1574
- import org.springframework.transaction.annotation.Transactional;
1575
-
1576
- import java.time.LocalDate;
1577
- import java.util.List;
1578
- import java.util.Optional;
1579
- import java.util.stream.Collectors;
1580
-
1581
- @Repository
1582
- @RequiredArgsConstructor
1583
- public class JournalRepositoryImpl implements JournalRepository {
1584
-
1585
- private final JournalMapper journalMapper;
1586
-
1587
- @Override
1588
- @Transactional
1589
- public void update(Journal journal) {
1590
- int updatedCount = journalMapper.updateWithOptimisticLock(journal);
1591
-
1592
- if (updatedCount == 0) {
1593
- // バージョン不一致または削除済み
1594
- Integer currentVersion = journalMapper.findVersionByVoucherNumber(
1595
- journal.getJournalVoucherNumber());
1596
- if (currentVersion == null) {
1597
- throw new OptimisticLockException("仕訳", journal.getJournalVoucherNumber());
1598
- } else {
1599
- throw new OptimisticLockException("仕訳", journal.getJournalVoucherNumber(),
1600
- journal.getVersion(), currentVersion);
1601
- }
1602
- }
1603
- }
1604
-
1605
- @Override
1606
- public Optional<Journal> findByVoucherNumberWithDetails(String voucherNumber) {
1607
- return Optional.ofNullable(journalMapper.findWithDetailsByVoucherNumber(voucherNumber));
1608
- }
1609
-
1610
- // その他のメソッド...
1611
- }
1612
- ```
1613
-
1614
- </details>
1615
-
1616
- #### TDD: 楽観ロックのテスト
1617
-
1618
- <details>
1619
- <summary>JournalRepositoryOptimisticLockTest.java</summary>
1620
-
1621
- ```java
1622
- // src/test/java/com/example/fas/infrastructure/persistence/repository/JournalRepositoryOptimisticLockTest.java
1623
- package com.example.fas.infrastructure.persistence.repository;
1624
-
1625
- import com.example.fas.application.port.out.JournalRepository;
1626
- import com.example.fas.domain.exception.OptimisticLockException;
1627
- import com.example.fas.domain.model.account.DebitCreditType;
1628
- import com.example.fas.domain.model.journal.*;
1629
- import com.example.fas.testsetup.BaseIntegrationTest;
1630
- import org.junit.jupiter.api.*;
1631
- import org.springframework.beans.factory.annotation.Autowired;
1632
-
1633
- import java.math.BigDecimal;
1634
- import java.time.LocalDate;
1635
- import java.util.List;
1636
-
1637
- import static org.assertj.core.api.Assertions.*;
1638
-
1639
- @DisplayName("仕訳リポジトリ - 楽観ロック")
1640
- class JournalRepositoryOptimisticLockTest extends BaseIntegrationTest {
1641
-
1642
- @Autowired
1643
- private JournalRepository journalRepository;
1644
-
1645
- @BeforeEach
1646
- void setUp() {
1647
- journalRepository.deleteAll();
1648
- }
1649
-
1650
- @Nested
1651
- @DisplayName("楽観ロック")
1652
- class OptimisticLocking {
1653
-
1654
- @Test
1655
- @DisplayName("同じバージョンで更新できる")
1656
- void canUpdateWithSameVersion() {
1657
- // Arrange
1658
- var journal = createTestJournal("J0001");
1659
- journalRepository.save(journal);
1660
-
1661
- // Act
1662
- var fetched = journalRepository.findByVoucherNumber("J0001").get();
1663
- fetched.setDepartmentCode("DEPT-002");
1664
- journalRepository.update(fetched);
1665
-
1666
- // Assert
1667
- var updated = journalRepository.findByVoucherNumber("J0001").get();
1668
- assertThat(updated.getDepartmentCode()).isEqualTo("DEPT-002");
1669
- assertThat(updated.getVersion()).isEqualTo(2); // バージョンがインクリメント
1670
- }
1671
-
1672
- @Test
1673
- @DisplayName("異なるバージョンで更新すると楽観ロック例外が発生する")
1674
- void throwsExceptionWhenVersionMismatch() {
1675
- // Arrange
1676
- var journal = createTestJournal("J0002");
1677
- journalRepository.save(journal);
1678
-
1679
- // ユーザーAが取得
1680
- var journalA = journalRepository.findByVoucherNumber("J0002").get();
1681
- // ユーザーBが取得
1682
- var journalB = journalRepository.findByVoucherNumber("J0002").get();
1683
-
1684
- // ユーザーAが更新(成功)
1685
- journalA.setDepartmentCode("DEPT-A");
1686
- journalRepository.update(journalA);
1687
-
1688
- // Act & Assert: ユーザーBが古いバージョンで更新(失敗)
1689
- journalB.setDepartmentCode("DEPT-B");
1690
- assertThatThrownBy(() -> journalRepository.update(journalB))
1691
- .isInstanceOf(OptimisticLockException.class)
1692
- .hasMessageContaining("他のユーザーによって更新されています");
1693
- }
1694
-
1695
- @Test
1696
- @DisplayName("削除済みデータを更新すると楽観ロック例外が発生する")
1697
- void throwsExceptionWhenAlreadyDeleted() {
1698
- // Arrange
1699
- var journal = createTestJournal("J0003");
1700
- journalRepository.save(journal);
1701
-
1702
- var fetched = journalRepository.findByVoucherNumber("J0003").get();
1703
- journalRepository.delete("J0003");
1704
-
1705
- // Act & Assert
1706
- assertThatThrownBy(() -> journalRepository.update(fetched))
1707
- .isInstanceOf(OptimisticLockException.class)
1708
- .hasMessageContaining("既に削除されています");
1709
- }
1710
- }
1711
-
1712
- private Journal createTestJournal(String voucherNumber) {
1713
- var debitDetail = JournalDebitCreditDetail.builder()
1714
- .journalVoucherNumber(voucherNumber)
1715
- .lineNumber(1)
1716
- .debitCreditType(DebitCreditType.DEBIT)
1717
- .accountCode("11110")
1718
- .amount(new BigDecimal("100000"))
1719
- .build();
1720
-
1721
- var creditDetail = JournalDebitCreditDetail.builder()
1722
- .journalVoucherNumber(voucherNumber)
1723
- .lineNumber(1)
1724
- .debitCreditType(DebitCreditType.CREDIT)
1725
- .accountCode("41100")
1726
- .amount(new BigDecimal("100000"))
1727
- .build();
1728
-
1729
- var journalDetail = JournalDetail.builder()
1730
- .journalVoucherNumber(voucherNumber)
1731
- .lineNumber(1)
1732
- .lineSummary("テスト仕訳")
1733
- .debitCreditDetails(List.of(debitDetail, creditDetail))
1734
- .build();
1735
-
1736
- return Journal.builder()
1737
- .journalVoucherNumber(voucherNumber)
1738
- .postingDate(LocalDate.of(2024, 4, 1))
1739
- .entryDate(LocalDate.now())
1740
- .closingJournalFlag(false)
1741
- .singleEntryFlag(true)
1742
- .voucherType(JournalVoucherType.NORMAL)
1743
- .periodicPostingFlag(false)
1744
- .departmentCode("DEPT-001")
1745
- .redSlipFlag(false)
1746
- .details(List.of(journalDetail))
1747
- .build();
1748
- }
1749
- }
1750
- ```
1751
-
1752
- </details>
1753
-
1754
- #### 楽観ロックのベストプラクティス
1755
-
1756
- | ポイント | 説明 |
1757
- |---------|------|
1758
- | **バージョンカラム** | INTEGER 型で十分(オーバーフローは実用上問題なし) |
1759
- | **WHERE 条件** | 必ず `AND "バージョン" = #{version}` を含める |
1760
- | **インクリメント** | `"バージョン" = "バージョン" + 1` でアトミックに更新 |
1761
- | **例外処理** | 更新件数が0の場合は楽観ロック例外をスロー |
1762
- | **リトライ** | 必要に応じて再取得・再更新のリトライロジックを実装 |
1763
- | **3層構造の考慮** | 仕訳は通常ヘッダ単位でロック、明細の個別ロックは必要に応じて追加 |
1764
-
1765
- ### 3層構造におけるリレーションと整合性
1766
-
1767
- 仕訳データは3層構造(仕訳 → 仕訳明細 → 仕訳貸借明細)を持つため、更新時の整合性確保が重要です。
1768
-
1769
- #### 更新パターン
1770
-
1771
- | パターン | 説明 | 推奨 |
1772
- |---------|------|------|
1773
- | **ヘッダ単位ロック** | 仕訳ヘッダのバージョンのみチェック | ○ シンプルで実用的 |
1774
- | **全層ロック** | 仕訳・明細・貸借明細すべてのバージョンをチェック | △ 複雑だが厳密 |
1775
- | **カスケード更新** | ヘッダ更新時に明細も一括削除・再挿入 | △ 実装が簡単だが履歴管理に注意 |
1776
-
1777
- #### 推奨アプローチ
1778
-
1779
- 仕訳データは通常、伝票単位で一括編集されるため、**ヘッダ単位ロック**が推奨されます。明細の部分更新が必要な場合は、明細レベルのバージョンチェックを追加します。
1780
-
1781
- ```plantuml
1782
- @startuml
1783
-
1784
- title 仕訳更新の楽観ロックフロー
1785
-
1786
- actor ユーザーA
1787
- actor ユーザーB
1788
- database DB
1789
-
1790
- == 取得フェーズ ==
1791
- ユーザーA -> DB : 仕訳J0001取得
1792
- DB --> ユーザーA : Journal(version=1)
1793
- ユーザーB -> DB : 仕訳J0001取得
1794
- DB --> ユーザーB : Journal(version=1)
1795
-
1796
- == 更新フェーズ ==
1797
- ユーザーA -> DB : UPDATE WHERE version=1
1798
- DB --> ユーザーA : 更新成功 (version=2)
1799
-
1800
- ユーザーB -> DB : UPDATE WHERE version=1
1801
- DB --> ユーザーB : 更新件数=0
1802
- ユーザーB -> ユーザーB : OptimisticLockException
1803
-
1804
- @enduml
1805
- ```
1806
-
1807
- ---
1808
-
1809
- ## 第16章のまとめ
1810
-
1811
- ### 作成したテーブル
1812
-
1813
- | テーブル名 | 説明 |
1814
- |----------|------|
1815
- | `仕訳` | 仕訳伝票のヘッダ情報(起票日、入力日、各種フラグ) |
1816
- | `仕訳明細` | 仕訳の行単位の情報(行摘要) |
1817
- | `仕訳貸借明細` | 借方・貸方の詳細(勘定科目、金額、補助情報) |
1818
-
1819
- ### ER図
1820
-
1821
- ```plantuml
1822
- @startuml
1823
-
1824
- title 仕訳テーブル ER図
1825
-
1826
- entity 仕訳 {
1827
- 仕訳伝票番号 <<PK>>
1828
- --
1829
- 起票日
1830
- 入力日
1831
- 決算仕訳フラグ
1832
- 単振フラグ
1833
- 仕訳伝票区分
1834
- 定期計上フラグ
1835
- 社員コード
1836
- 部門コード
1837
- 赤伝フラグ
1838
- 赤黒伝票番号
1839
- 作成日時
1840
- 更新日時
1841
- }
1842
-
1843
- entity 仕訳明細 {
1844
- 仕訳伝票番号 <<PK,FK>>
1845
- 仕訳行番号 <<PK>>
1846
- --
1847
- 行摘要
1848
- 作成日時
1849
- 更新日時
1850
- }
1851
-
1852
- entity 仕訳貸借明細 {
1853
- 仕訳伝票番号 <<PK,FK>>
1854
- 仕訳行番号 <<PK>>
1855
- 仕訳行貸借区分 <<PK>>
1856
- --
1857
- 勘定科目コード <<FK>>
1858
- 補助科目コード
1859
- 部門コード <<FK>>
1860
- プロジェクトコード
1861
- 仕訳金額
1862
- 通貨コード
1863
- 為替レート
1864
- 基軸換算仕訳金額
1865
- 消費税区分
1866
- 消費税率
1867
- 消費税計算区分
1868
- 期日
1869
- ...
1870
- }
1871
-
1872
- entity 勘定科目マスタ {
1873
- 勘定科目コード <<PK>>
1874
- --
1875
- ...
1876
- }
1877
-
1878
- entity 部門マスタ {
1879
- 部門コード <<PK>>
1880
- --
1881
- ...
1882
- }
1883
-
1884
- 仕訳 ||--o{ 仕訳明細 : "含む"
1885
- 仕訳明細 ||--o{ 仕訳貸借明細 : "含む"
1886
- 仕訳貸借明細 }o..|| 勘定科目マスタ : "参照"
1887
- 仕訳貸借明細 }o..o| 部門マスタ : "参照"
1888
- 仕訳 }o..o| 部門マスタ : "参照"
1889
-
1890
- @enduml
1891
- ```
1892
-
1893
- ### 設計のポイント
1894
-
1895
- 1. **3階層構造**: 仕訳 → 仕訳明細 → 仕訳貸借明細の親子関係
1896
- 2. **複式簿記の原則**: 借方合計 = 貸方合計の貸借一致チェック
1897
- 3. **単一仕訳と複合仕訳**: 単振フラグで区別
1898
- 4. **赤黒処理対応**: 赤伝フラグと赤黒伝票番号で訂正履歴を管理
1899
- 5. **多通貨対応**: 通貨コード、為替レート、基軸換算金額
1900
- 6. **消費税対応**: 消費税区分、消費税率、消費税計算区分
1901
- 7. **管理会計対応**: 部門コード、プロジェクトコード、セグメントコード
1902
-
1903
- ### ドメインロジック
1904
-
1905
- | メソッド | 説明 |
1906
- |---------|------|
1907
- | `isBalanced()` | 借方合計と貸方合計が一致するかチェック |
1908
- | `getDebitTotal()` | 借方合計を取得 |
1909
- | `getCreditTotal()` | 貸方合計を取得 |
1910
-
1911
- ---
1912
-
1913
- ## 次章の予告
1914
-
1915
- 第17章では、自動仕訳の設計に進みます。販売管理システムと会計システムの連携、自動仕訳パターンマスタの設計、売上伝票から仕訳伝票への転記ロジックを TDD で実装していきます。
1
+ # 第16章:仕訳の設計
2
+
3
+ 財務会計システムの核心部分である「仕訳処理」を TDD で設計していきます。複式簿記の原則に基づき、仕訳データの登録・管理を実現するテーブル構造を設計します。
4
+
5
+ ---
6
+
7
+ ## 16.1 仕訳テーブルの設計
8
+
9
+ ### 複式簿記と仕訳の概念
10
+
11
+ 複式簿記は、すべての取引を「借方(かりかた)」と「貸方(かしかた)」の2つの側面から記録する方式です。
12
+
13
+ ```plantuml
14
+ @startuml
15
+
16
+ title 複式簿記の原則
17
+
18
+ rectangle "取引" as transaction {
19
+ rectangle "借方(Debit)\n資産の増加\n負債の減少\n費用の発生" as debit
20
+ rectangle "貸方(Credit)\n資産の減少\n負債の増加\n収益の発生" as credit
21
+ }
22
+
23
+ debit -[hidden]right- credit
24
+
25
+ note bottom of transaction
26
+ **貸借一致の原則**
27
+ 借方合計 = 貸方合計
28
+ 必ず一致する
29
+ end note
30
+
31
+ @enduml
32
+ ```
33
+
34
+ #### 仕訳の具体例
35
+
36
+ 商品を現金で販売した場合の仕訳:
37
+
38
+ | 借方(Debit) | 貸方(Credit) |
39
+ |--------------|---------------|
40
+ | 現金 100,000円 | 売上 100,000円 |
41
+
42
+ 売掛金を現金と受取手形で回収した場合の仕訳(複合仕訳):
43
+
44
+ | 借方(Debit) | 貸方(Credit) |
45
+ |--------------|---------------|
46
+ | 現金 80,000円 | 売掛金 100,000円 |
47
+ | 受取手形 20,000円 | |
48
+
49
+ ### 仕訳処理の流れ
50
+
51
+ ```plantuml
52
+ @startuml
53
+
54
+ title 仕訳の流れ
55
+
56
+ |営業部門|
57
+ start
58
+ :売上取引発生;
59
+ :売上伝票作成;
60
+
61
+ |経理部門|
62
+ :仕訳入力;
63
+ :貸借バランスチェック;
64
+
65
+ if (借方合計 = 貸方合計?) then (はい)
66
+ :仕訳登録;
67
+ :残高更新;
68
+ else (いいえ)
69
+ :エラー表示;
70
+ :修正入力;
71
+ stop
72
+ endif
73
+
74
+ :仕訳完了;
75
+ stop
76
+
77
+ @enduml
78
+ ```
79
+
80
+ ### 仕訳テーブルの項目設計
81
+
82
+ | 項目 | 説明 |
83
+ |-----|------|
84
+ | **仕訳伝票番号** | 仕訳を一意に識別するキー |
85
+ | **起票日** | 会計上の取引発生日 |
86
+ | **入力日** | システムへの登録日 |
87
+ | **決算仕訳フラグ** | 決算整理仕訳かどうか(0:通常、1:決算) |
88
+ | **単振フラグ** | 単一仕訳か複合仕訳か(0:複合、1:単一) |
89
+ | **仕訳伝票区分** | 仕訳の種類(通常/決算/自動/振替) |
90
+ | **定期計上フラグ** | 定期的な計上か否か |
91
+ | **社員コード** | 起票担当者 |
92
+ | **部門コード** | 所属部門 |
93
+ | **赤伝フラグ** | 赤伝(取消)かどうか(0:黒伝、1:赤伝) |
94
+ | **赤黒伝票番号** | 関連する赤黒伝票の番号 |
95
+
96
+ ### 仕訳テーブルのER図
97
+
98
+ ```plantuml
99
+ @startuml
100
+
101
+ entity "仕訳" as Journal {
102
+ * **仕訳伝票番号**: VARCHAR(10) <<PK>>
103
+ --
104
+ * **起票日**: DATE
105
+ * **入力日**: DATE
106
+ * **決算仕訳フラグ**: SMALLINT
107
+ * **単振フラグ**: SMALLINT
108
+ * **仕訳伝票区分**: 仕訳伝票区分
109
+ * **定期計上フラグ**: SMALLINT
110
+ **社員コード**: VARCHAR(10) <<FK>>
111
+ **部門コード**: VARCHAR(5) <<FK>>
112
+ * **赤伝フラグ**: SMALLINT
113
+ **赤黒伝票番号**: INTEGER
114
+ * **作成日時**: TIMESTAMP
115
+ * **更新日時**: TIMESTAMP
116
+ }
117
+
118
+ entity "仕訳明細" as JournalDetail {
119
+ * **仕訳伝票番号**: VARCHAR(10) <<PK>> <<FK>>
120
+ * **仕訳行番号**: SMALLINT <<PK>>
121
+ --
122
+ **行摘要**: VARCHAR(1000)
123
+ * **作成日時**: TIMESTAMP
124
+ * **更新日時**: TIMESTAMP
125
+ }
126
+
127
+ entity "仕訳貸借明細" as JournalDebitCreditDetail {
128
+ * **仕訳伝票番号**: VARCHAR(10) <<PK>> <<FK>>
129
+ * **仕訳行番号**: SMALLINT <<PK>>
130
+ * **仕訳行貸借区分**: 仕訳行貸借区分 <<PK>>
131
+ --
132
+ * **勘定科目コード**: VARCHAR(5) <<FK>>
133
+ **補助科目コード**: VARCHAR(10)
134
+ **部門コード**: VARCHAR(5) <<FK>>
135
+ **プロジェクトコード**: VARCHAR(10)
136
+ * **仕訳金額**: DECIMAL(14,2)
137
+ **通貨コード**: VARCHAR(3)
138
+ **為替レート**: DECIMAL(8,2)
139
+ **基軸換算仕訳金額**: DECIMAL(14,2)
140
+ **消費税区分**: 消費税区分
141
+ **消費税率**: SMALLINT
142
+ **消費税計算区分**: 消費税計算区分
143
+ **期日**: DATE
144
+ **資金繰フラグ**: SMALLINT
145
+ **セグメントコード**: VARCHAR(10)
146
+ **相手勘定科目コード**: VARCHAR(5)
147
+ **相手補助科目コード**: VARCHAR(10)
148
+ **付箋コード**: VARCHAR(1)
149
+ **付箋内容**: VARCHAR(60)
150
+ * **作成日時**: TIMESTAMP
151
+ * **更新日時**: TIMESTAMP
152
+ }
153
+
154
+ Journal ||--o{ JournalDetail : "含む"
155
+ JournalDetail ||--o{ JournalDebitCreditDetail : "含む"
156
+
157
+ note right of Journal
158
+ 仕訳伝票のヘッダ情報
159
+ 起票日、入力日、各種フラグを管理
160
+ end note
161
+
162
+ note right of JournalDetail
163
+ 仕訳の行単位の情報
164
+ 1つの仕訳に複数の明細行
165
+ end note
166
+
167
+ note right of JournalDebitCreditDetail
168
+ 借方・貸方の詳細情報
169
+ 勘定科目、金額、補助情報を管理
170
+ end note
171
+
172
+ @enduml
173
+ ```
174
+
175
+ ### マイグレーション:仕訳テーブルの作成
176
+
177
+ <details>
178
+ <summary>マイグレーションSQL</summary>
179
+
180
+ ```sql
181
+ -- src/main/resources/db/migration/V006__create_journal_tables.sql
182
+
183
+ -- 仕訳伝票区分
184
+ CREATE TYPE 仕訳伝票区分 AS ENUM ('通常', '決算', '自動', '振替');
185
+
186
+ -- 仕訳行貸借区分
187
+ CREATE TYPE 仕訳行貸借区分 AS ENUM ('借方', '貸方');
188
+
189
+ -- 消費税区分(仕訳用)
190
+ CREATE TYPE 消費税区分 AS ENUM ('課税', '非課税', '免税', '不課税', '対象外');
191
+
192
+ -- 消費税計算区分(仕訳用)
193
+ CREATE TYPE 消費税計算区分 AS ENUM ('外税', '内税', '税なし');
194
+
195
+ -- 仕訳ヘッダ
196
+ CREATE TABLE "仕訳" (
197
+ "仕訳伝票番号" VARCHAR(10) PRIMARY KEY,
198
+ "起票日" DATE NOT NULL,
199
+ "入力日" DATE NOT NULL DEFAULT CURRENT_DATE,
200
+ "決算仕訳フラグ" SMALLINT DEFAULT 0 NOT NULL,
201
+ "単振フラグ" SMALLINT DEFAULT 0 NOT NULL,
202
+ "仕訳伝票区分" 仕訳伝票区分 DEFAULT '通常' NOT NULL,
203
+ "定期計上フラグ" SMALLINT DEFAULT 0 NOT NULL,
204
+ "社員コード" VARCHAR(10),
205
+ "部門コード" VARCHAR(5),
206
+ "赤伝フラグ" SMALLINT DEFAULT 0 NOT NULL,
207
+ "赤黒伝票番号" INTEGER,
208
+ "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
209
+ "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
210
+ CONSTRAINT "fk_仕訳_部門"
211
+ FOREIGN KEY ("部門コード") REFERENCES "部門マスタ"("部門コード")
212
+ );
213
+
214
+ -- 仕訳明細
215
+ CREATE TABLE "仕訳明細" (
216
+ "仕訳伝票番号" VARCHAR(10) NOT NULL,
217
+ "仕訳行番号" SMALLINT NOT NULL,
218
+ "行摘要" VARCHAR(1000),
219
+ "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
220
+ "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
221
+ PRIMARY KEY ("仕訳伝票番号", "仕訳行番号"),
222
+ CONSTRAINT "fk_仕訳明細_仕訳"
223
+ FOREIGN KEY ("仕訳伝票番号") REFERENCES "仕訳"("仕訳伝票番号") ON DELETE CASCADE
224
+ );
225
+
226
+ -- 仕訳貸借明細
227
+ CREATE TABLE "仕訳貸借明細" (
228
+ "仕訳伝票番号" VARCHAR(10) NOT NULL,
229
+ "仕訳行番号" SMALLINT NOT NULL,
230
+ "仕訳行貸借区分" 仕訳行貸借区分 NOT NULL,
231
+ "勘定科目コード" VARCHAR(5) NOT NULL,
232
+ "補助科目コード" VARCHAR(10),
233
+ "部門コード" VARCHAR(5),
234
+ "プロジェクトコード" VARCHAR(10),
235
+ "仕訳金額" DECIMAL(14,2) NOT NULL,
236
+ "通貨コード" VARCHAR(3) DEFAULT 'JPY',
237
+ "為替レート" DECIMAL(8,2) DEFAULT 1.00,
238
+ "基軸換算仕訳金額" DECIMAL(14,2),
239
+ "消費税区分" 消費税区分 DEFAULT '課税',
240
+ "消費税率" SMALLINT DEFAULT 10,
241
+ "消費税計算区分" 消費税計算区分 DEFAULT '外税',
242
+ "期日" DATE,
243
+ "資金繰フラグ" SMALLINT DEFAULT 0,
244
+ "セグメントコード" VARCHAR(10),
245
+ "相手勘定科目コード" VARCHAR(5),
246
+ "相手補助科目コード" VARCHAR(10),
247
+ "付箋コード" VARCHAR(1),
248
+ "付箋内容" VARCHAR(60),
249
+ "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
250
+ "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
251
+ PRIMARY KEY ("仕訳伝票番号", "仕訳行番号", "仕訳行貸借区分"),
252
+ CONSTRAINT "fk_仕訳貸借明細_仕訳明細"
253
+ FOREIGN KEY ("仕訳伝票番号", "仕訳行番号")
254
+ REFERENCES "仕訳明細"("仕訳伝票番号", "仕訳行番号") ON DELETE CASCADE,
255
+ CONSTRAINT "fk_仕訳貸借明細_勘定科目"
256
+ FOREIGN KEY ("勘定科目コード") REFERENCES "勘定科目マスタ"("勘定科目コード"),
257
+ CONSTRAINT "fk_仕訳貸借明細_部門"
258
+ FOREIGN KEY ("部門コード") REFERENCES "部門マスタ"("部門コード")
259
+ );
260
+
261
+ -- インデックス
262
+ CREATE INDEX "idx_仕訳_起票日" ON "仕訳"("起票日");
263
+ CREATE INDEX "idx_仕訳_部門コード" ON "仕訳"("部門コード");
264
+ CREATE INDEX "idx_仕訳_仕訳伝票区分" ON "仕訳"("仕訳伝票区分");
265
+ CREATE INDEX "idx_仕訳貸借明細_勘定科目コード" ON "仕訳貸借明細"("勘定科目コード");
266
+ CREATE INDEX "idx_仕訳貸借明細_部門コード" ON "仕訳貸借明細"("部門コード");
267
+ ```
268
+
269
+ </details>
270
+
271
+ <details>
272
+ <summary>貸借バランスチェック関数</summary>
273
+
274
+ ```sql
275
+ -- 貸借バランスチェック関数
276
+ CREATE OR REPLACE FUNCTION check_journal_balance()
277
+ RETURNS TRIGGER AS $$
278
+ DECLARE
279
+ debit_total DECIMAL(14,2);
280
+ credit_total DECIMAL(14,2);
281
+ BEGIN
282
+ -- 借方合計を計算
283
+ SELECT COALESCE(SUM("仕訳金額"), 0) INTO debit_total
284
+ FROM "仕訳貸借明細"
285
+ WHERE "仕訳伝票番号" = NEW."仕訳伝票番号"
286
+ AND "仕訳行貸借区分" = '借方';
287
+
288
+ -- 貸方合計を計算
289
+ SELECT COALESCE(SUM("仕訳金額"), 0) INTO credit_total
290
+ FROM "仕訳貸借明細"
291
+ WHERE "仕訳伝票番号" = NEW."仕訳伝票番号"
292
+ AND "仕訳行貸借区分" = '貸方';
293
+
294
+ -- 貸借一致チェック(トリガー内では警告のみ、アプリ側で制御)
295
+ IF debit_total <> credit_total THEN
296
+ RAISE WARNING '仕訳伝票番号 % の貸借が一致していません: 借方=%, 貸方=%',
297
+ NEW."仕訳伝票番号", debit_total, credit_total;
298
+ END IF;
299
+
300
+ RETURN NEW;
301
+ END;
302
+ $$ LANGUAGE plpgsql;
303
+
304
+ -- 貸借バランスチェックトリガー(挿入・更新時)
305
+ CREATE TRIGGER "trg_check_journal_balance"
306
+ AFTER INSERT OR UPDATE ON "仕訳貸借明細"
307
+ FOR EACH ROW
308
+ EXECUTE FUNCTION check_journal_balance();
309
+ ```
310
+
311
+ </details>
312
+
313
+ ---
314
+
315
+ ## 16.2 仕訳明細の設計
316
+
317
+ ### 仕訳明細の役割
318
+
319
+ 仕訳明細は、仕訳伝票の行単位の情報を管理します。1つの仕訳伝票に複数の明細行を持つことができます。
320
+
321
+ | 項目 | 説明 |
322
+ |-----|------|
323
+ | **仕訳伝票番号** | 親となる仕訳伝票の番号(外部キー) |
324
+ | **仕訳行番号** | 明細行の連番(1から始まる) |
325
+ | **行摘要** | その行の取引内容の説明(最大1000文字) |
326
+
327
+ ### 単一仕訳と複合仕訳
328
+
329
+ ```plantuml
330
+ @startuml
331
+
332
+ title 単一仕訳と複合仕訳
333
+
334
+ package "単一仕訳(単振フラグ=1)" {
335
+ rectangle "仕訳明細 行番号1" as single {
336
+ rectangle "借方: 現金 100,000円" as sd
337
+ rectangle "貸方: 売上 100,000円" as sc
338
+ }
339
+ }
340
+
341
+ package "複合仕訳(単振フラグ=0)" {
342
+ rectangle "仕訳明細 行番号1" as line1 {
343
+ rectangle "借方: 現金 80,000円" as d1
344
+ rectangle "貸方: 売掛金 100,000円" as c1
345
+ }
346
+ rectangle "仕訳明細 行番号2" as line2 {
347
+ rectangle "借方: 受取手形 20,000円" as d2
348
+ }
349
+ }
350
+
351
+ @enduml
352
+ ```
353
+
354
+ ---
355
+
356
+ ## 16.3 仕訳貸借明細の設計
357
+
358
+ ### 仕訳貸借明細の項目
359
+
360
+ 仕訳貸借明細は、借方・貸方それぞれの詳細情報を管理する最も粒度の細かいテーブルです。
361
+
362
+ #### 基本情報
363
+
364
+ | 項目 | 説明 |
365
+ |-----|------|
366
+ | **仕訳伝票番号** | 親となる仕訳伝票の番号 |
367
+ | **仕訳行番号** | 親となる仕訳明細の行番号 |
368
+ | **仕訳行貸借区分** | 借方/貸方の区分 |
369
+ | **勘定科目コード** | 使用する勘定科目 |
370
+ | **補助科目コード** | 補助科目(得意先、仕入先など) |
371
+ | **仕訳金額** | 取引金額 |
372
+
373
+ #### 多通貨対応
374
+
375
+ | 項目 | 説明 |
376
+ |-----|------|
377
+ | **通貨コード** | 取引通貨(デフォルト: JPY) |
378
+ | **為替レート** | 適用為替レート |
379
+ | **基軸換算仕訳金額** | 基軸通貨(円)換算後の金額 |
380
+
381
+ #### 管理会計情報
382
+
383
+ | 項目 | 説明 |
384
+ |-----|------|
385
+ | **部門コード** | 配賦先部門 |
386
+ | **プロジェクトコード** | 関連プロジェクト |
387
+ | **セグメントコード** | セグメント情報 |
388
+
389
+ #### 消費税情報
390
+
391
+ | 項目 | 説明 |
392
+ |-----|------|
393
+ | **消費税区分** | 課税/非課税/免税/不課税/対象外 |
394
+ | **消費税率** | 適用税率(10, 8 など) |
395
+ | **消費税計算区分** | 外税/内税/税なし |
396
+
397
+ #### その他
398
+
399
+ | 項目 | 説明 |
400
+ |-----|------|
401
+ | **期日** | 支払期日・入金期日 |
402
+ | **資金繰フラグ** | 資金繰表への計上対象か |
403
+ | **相手勘定科目コード** | 相手勘定科目(元帳表示用) |
404
+ | **相手補助科目コード** | 相手補助科目 |
405
+ | **付箋コード** | 付箋の種類 |
406
+ | **付箋内容** | 付箋の内容(メモ) |
407
+
408
+ ### TDD による仕訳テーブルの実装
409
+
410
+ #### ドメイン層:Enum定義
411
+
412
+ <details>
413
+ <summary>仕訳伝票区分 Enum</summary>
414
+
415
+ ```java
416
+ // src/main/java/com/example/accounting/domain/model/journal/JournalVoucherType.java
417
+ package com.example.fas.domain.model.journal;
418
+
419
+ import lombok.Getter;
420
+ import lombok.RequiredArgsConstructor;
421
+
422
+ @Getter
423
+ @RequiredArgsConstructor
424
+ public enum JournalVoucherType {
425
+ NORMAL("通常"),
426
+ CLOSING("決算"),
427
+ AUTO("自動"),
428
+ TRANSFER("振替");
429
+
430
+ private final String displayName;
431
+
432
+ public static JournalVoucherType fromDisplayName(String displayName) {
433
+ for (JournalVoucherType type : values()) {
434
+ if (type.displayName.equals(displayName)) {
435
+ return type;
436
+ }
437
+ }
438
+ throw new IllegalArgumentException("不正な仕訳伝票区分: " + displayName);
439
+ }
440
+ }
441
+ ```
442
+
443
+ </details>
444
+
445
+ <details>
446
+ <summary>消費税区分 Enum</summary>
447
+
448
+ ```java
449
+ // src/main/java/com/example/accounting/domain/model/journal/TaxType.java
450
+ package com.example.fas.domain.model.journal;
451
+
452
+ import lombok.Getter;
453
+ import lombok.RequiredArgsConstructor;
454
+
455
+ @Getter
456
+ @RequiredArgsConstructor
457
+ public enum TaxType {
458
+ TAXABLE("課税"),
459
+ NON_TAXABLE("非課税"),
460
+ TAX_EXEMPT("免税"),
461
+ NOT_TAXABLE("不課税"),
462
+ OUT_OF_SCOPE("対象外");
463
+
464
+ private final String displayName;
465
+
466
+ public static TaxType fromDisplayName(String displayName) {
467
+ for (TaxType type : values()) {
468
+ if (type.displayName.equals(displayName)) {
469
+ return type;
470
+ }
471
+ }
472
+ throw new IllegalArgumentException("不正な消費税区分: " + displayName);
473
+ }
474
+ }
475
+ ```
476
+
477
+ </details>
478
+
479
+ <details>
480
+ <summary>消費税計算区分 Enum</summary>
481
+
482
+ ```java
483
+ // src/main/java/com/example/accounting/domain/model/journal/TaxCalculationType.java
484
+ package com.example.fas.domain.model.journal;
485
+
486
+ import lombok.Getter;
487
+ import lombok.RequiredArgsConstructor;
488
+
489
+ @Getter
490
+ @RequiredArgsConstructor
491
+ public enum TaxCalculationType {
492
+ EXCLUSIVE("外税"),
493
+ INCLUSIVE("内税"),
494
+ NO_TAX("税なし");
495
+
496
+ private final String displayName;
497
+
498
+ public static TaxCalculationType fromDisplayName(String displayName) {
499
+ for (TaxCalculationType type : values()) {
500
+ if (type.displayName.equals(displayName)) {
501
+ return type;
502
+ }
503
+ }
504
+ throw new IllegalArgumentException("不正な消費税計算区分: " + displayName);
505
+ }
506
+ }
507
+ ```
508
+
509
+ </details>
510
+
511
+ #### ドメイン層:エンティティ
512
+
513
+ <details>
514
+ <summary>仕訳エンティティ</summary>
515
+
516
+ ```java
517
+ // src/main/java/com/example/accounting/domain/model/journal/Journal.java
518
+ package com.example.fas.domain.model.journal;
519
+
520
+ import lombok.AllArgsConstructor;
521
+ import lombok.Builder;
522
+ import lombok.Data;
523
+ import lombok.NoArgsConstructor;
524
+
525
+ import java.math.BigDecimal;
526
+ import java.time.LocalDate;
527
+ import java.time.LocalDateTime;
528
+ import java.util.ArrayList;
529
+ import java.util.List;
530
+
531
+ @Data
532
+ @Builder
533
+ @NoArgsConstructor
534
+ @AllArgsConstructor
535
+ public class Journal {
536
+ private String journalVoucherNumber; // 仕訳伝票番号
537
+ private LocalDate postingDate; // 起票日
538
+ private LocalDate entryDate; // 入力日
539
+ private Boolean closingJournalFlag; // 決算仕訳フラグ
540
+ private Boolean singleEntryFlag; // 単振フラグ
541
+ private JournalVoucherType voucherType; // 仕訳伝票区分
542
+ private Boolean periodicPostingFlag; // 定期計上フラグ
543
+ private String employeeCode; // 社員コード
544
+ private String departmentCode; // 部門コード
545
+ private Boolean redSlipFlag; // 赤伝フラグ
546
+ private Integer redBlackVoucherNumber; // 赤黒伝票番号
547
+ private LocalDateTime createdAt; // 作成日時
548
+ private LocalDateTime updatedAt; // 更新日時
549
+
550
+ // リレーション
551
+ @Builder.Default
552
+ private List<JournalDetail> details = new ArrayList<>();
553
+
554
+ /**
555
+ * 貸借バランスチェック
556
+ * @return 借方合計と貸方合計が一致する場合 true
557
+ */
558
+ public boolean isBalanced() {
559
+ BigDecimal debitTotal = details.stream()
560
+ .flatMap(d -> d.getDebitCreditDetails().stream())
561
+ .filter(dc -> dc.getDebitCreditType() == DebitCreditType.DEBIT)
562
+ .map(JournalDebitCreditDetail::getAmount)
563
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
564
+
565
+ BigDecimal creditTotal = details.stream()
566
+ .flatMap(d -> d.getDebitCreditDetails().stream())
567
+ .filter(dc -> dc.getDebitCreditType() == DebitCreditType.CREDIT)
568
+ .map(JournalDebitCreditDetail::getAmount)
569
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
570
+
571
+ return debitTotal.compareTo(creditTotal) == 0;
572
+ }
573
+
574
+ /**
575
+ * 借方合計を取得
576
+ */
577
+ public BigDecimal getDebitTotal() {
578
+ return details.stream()
579
+ .flatMap(d -> d.getDebitCreditDetails().stream())
580
+ .filter(dc -> dc.getDebitCreditType() == DebitCreditType.DEBIT)
581
+ .map(JournalDebitCreditDetail::getAmount)
582
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
583
+ }
584
+
585
+ /**
586
+ * 貸方合計を取得
587
+ */
588
+ public BigDecimal getCreditTotal() {
589
+ return details.stream()
590
+ .flatMap(d -> d.getDebitCreditDetails().stream())
591
+ .filter(dc -> dc.getDebitCreditType() == DebitCreditType.CREDIT)
592
+ .map(JournalDebitCreditDetail::getAmount)
593
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
594
+ }
595
+ }
596
+ ```
597
+
598
+ </details>
599
+
600
+ <details>
601
+ <summary>仕訳明細エンティティ</summary>
602
+
603
+ ```java
604
+ // src/main/java/com/example/accounting/domain/model/journal/JournalDetail.java
605
+ package com.example.fas.domain.model.journal;
606
+
607
+ import lombok.AllArgsConstructor;
608
+ import lombok.Builder;
609
+ import lombok.Data;
610
+ import lombok.NoArgsConstructor;
611
+
612
+ import java.time.LocalDateTime;
613
+ import java.util.ArrayList;
614
+ import java.util.List;
615
+
616
+ @Data
617
+ @Builder
618
+ @NoArgsConstructor
619
+ @AllArgsConstructor
620
+ public class JournalDetail {
621
+ private String journalVoucherNumber; // 仕訳伝票番号
622
+ private Integer lineNumber; // 仕訳行番号
623
+ private String lineSummary; // 行摘要
624
+ private LocalDateTime createdAt; // 作成日時
625
+ private LocalDateTime updatedAt; // 更新日時
626
+
627
+ // リレーション
628
+ @Builder.Default
629
+ private List<JournalDebitCreditDetail> debitCreditDetails = new ArrayList<>();
630
+ }
631
+ ```
632
+
633
+ </details>
634
+
635
+ <details>
636
+ <summary>仕訳貸借明細エンティティ</summary>
637
+
638
+ ```java
639
+ // src/main/java/com/example/accounting/domain/model/journal/JournalDebitCreditDetail.java
640
+ package com.example.fas.domain.model.journal;
641
+
642
+ import com.example.fas.domain.model.account.DebitCreditType;
643
+ import lombok.AllArgsConstructor;
644
+ import lombok.Builder;
645
+ import lombok.Data;
646
+ import lombok.NoArgsConstructor;
647
+
648
+ import java.math.BigDecimal;
649
+ import java.time.LocalDate;
650
+ import java.time.LocalDateTime;
651
+
652
+ @Data
653
+ @Builder
654
+ @NoArgsConstructor
655
+ @AllArgsConstructor
656
+ public class JournalDebitCreditDetail {
657
+ private String journalVoucherNumber; // 仕訳伝票番号
658
+ private Integer lineNumber; // 仕訳行番号
659
+ private DebitCreditType debitCreditType; // 仕訳行貸借区分
660
+ private String accountCode; // 勘定科目コード
661
+ private String subAccountCode; // 補助科目コード
662
+ private String departmentCode; // 部門コード
663
+ private String projectCode; // プロジェクトコード
664
+ private BigDecimal amount; // 仕訳金額
665
+ private String currencyCode; // 通貨コード
666
+ private BigDecimal exchangeRate; // 為替レート
667
+ private BigDecimal baseCurrencyAmount; // 基軸換算仕訳金額
668
+ private TaxType taxType; // 消費税区分
669
+ private Integer taxRate; // 消費税率
670
+ private TaxCalculationType taxCalcType; // 消費税計算区分
671
+ private LocalDate dueDate; // 期日
672
+ private Boolean cashFlowFlag; // 資金繰フラグ
673
+ private String segmentCode; // セグメントコード
674
+ private String counterAccountCode; // 相手勘定科目コード
675
+ private String counterSubAccountCode; // 相手補助科目コード
676
+ private String tagCode; // 付箋コード
677
+ private String tagContent; // 付箋内容
678
+ private LocalDateTime createdAt; // 作成日時
679
+ private LocalDateTime updatedAt; // 更新日時
680
+ }
681
+ ```
682
+
683
+ </details>
684
+
685
+ #### テストコード
686
+
687
+ <details>
688
+ <summary>仕訳リポジトリテスト</summary>
689
+
690
+ ```java
691
+ // src/test/java/com/example/accounting/infrastructure/persistence/repository/JournalRepositoryTest.java
692
+ package com.example.fas.infrastructure.persistence.repository;
693
+
694
+ import com.example.fas.application.port.out.JournalRepository;
695
+ import com.example.fas.domain.model.account.DebitCreditType;
696
+ import com.example.fas.domain.model.journal.*;
697
+ import com.example.fas.testsetup.BaseIntegrationTest;
698
+ import org.junit.jupiter.api.*;
699
+ import org.springframework.beans.factory.annotation.Autowired;
700
+
701
+ import java.math.BigDecimal;
702
+ import java.time.LocalDate;
703
+ import java.util.List;
704
+
705
+ import static org.assertj.core.api.Assertions.*;
706
+
707
+ @DisplayName("仕訳リポジトリ")
708
+ class JournalRepositoryTest extends BaseIntegrationTest {
709
+
710
+ @Autowired
711
+ private JournalRepository journalRepository;
712
+
713
+ @BeforeEach
714
+ void setUp() {
715
+ journalRepository.deleteAll();
716
+ }
717
+
718
+ @Nested
719
+ @DisplayName("仕訳の登録")
720
+ class Registration {
721
+
722
+ @Test
723
+ @DisplayName("単純仕訳(1行借方・1行貸方)を登録できる")
724
+ void canRegisterSimpleJournal() {
725
+ // Arrange: 現金売上の仕訳データ
726
+ var journal = createSimpleSalesJournal("J0001");
727
+
728
+ // Act: 仕訳を登録
729
+ journalRepository.save(journal);
730
+
731
+ // Assert: 登録されたデータを確認
732
+ var savedJournal = journalRepository.findByVoucherNumber("J0001");
733
+ assertThat(savedJournal).isPresent();
734
+ assertThat(savedJournal.get().getPostingDate()).isEqualTo(LocalDate.of(2024, 4, 1));
735
+ assertThat(savedJournal.get().isBalanced()).isTrue();
736
+ }
737
+
738
+ @Test
739
+ @DisplayName("複合仕訳(複数行)を登録できる")
740
+ void canRegisterCompoundJournal() {
741
+ // Arrange: 売掛金回収の複合仕訳
742
+ var journal = createCompoundJournal("J0002");
743
+
744
+ // Act: 仕訳を登録
745
+ journalRepository.save(journal);
746
+
747
+ // Assert: 登録されたデータを確認
748
+ var savedJournal = journalRepository.findByVoucherNumber("J0002");
749
+ assertThat(savedJournal).isPresent();
750
+ assertThat(savedJournal.get().getDetails()).hasSize(2);
751
+ assertThat(savedJournal.get().isBalanced()).isTrue();
752
+ }
753
+
754
+ @Test
755
+ @DisplayName("貸借が一致しない仕訳はバランスチェックでfalseを返す")
756
+ void shouldReturnFalseForUnbalancedJournal() {
757
+ // Arrange: 貸借不一致の仕訳データ
758
+ var journal = createUnbalancedJournal("J0003");
759
+
760
+ // Assert: バランスチェック
761
+ assertThat(journal.isBalanced()).isFalse();
762
+ }
763
+ }
764
+
765
+ @Nested
766
+ @DisplayName("仕訳の検索")
767
+ class Search {
768
+
769
+ @BeforeEach
770
+ void setUpTestData() {
771
+ journalRepository.save(createSimpleSalesJournal("J0001"));
772
+ journalRepository.save(createCompoundJournal("J0002"));
773
+ }
774
+
775
+ @Test
776
+ @DisplayName("起票日範囲で仕訳を検索できる")
777
+ void canFindByDateRange() {
778
+ // Act
779
+ var journals = journalRepository.findByPostingDateBetween(
780
+ LocalDate.of(2024, 4, 1),
781
+ LocalDate.of(2024, 4, 30)
782
+ );
783
+
784
+ // Assert
785
+ assertThat(journals).hasSize(2);
786
+ }
787
+
788
+ @Test
789
+ @DisplayName("勘定科目コードで仕訳を検索できる")
790
+ void canFindByAccountCode() {
791
+ // Act
792
+ var journals = journalRepository.findByAccountCode("11110");
793
+
794
+ // Assert
795
+ assertThat(journals).isNotEmpty();
796
+ }
797
+ }
798
+
799
+ private Journal createSimpleSalesJournal(String voucherNumber) {
800
+ var debitDetail = JournalDebitCreditDetail.builder()
801
+ .journalVoucherNumber(voucherNumber)
802
+ .lineNumber(1)
803
+ .debitCreditType(DebitCreditType.DEBIT)
804
+ .accountCode("11110") // 現金
805
+ .amount(new BigDecimal("100000"))
806
+ .currencyCode("JPY")
807
+ .exchangeRate(BigDecimal.ONE)
808
+ .build();
809
+
810
+ var creditDetail = JournalDebitCreditDetail.builder()
811
+ .journalVoucherNumber(voucherNumber)
812
+ .lineNumber(1)
813
+ .debitCreditType(DebitCreditType.CREDIT)
814
+ .accountCode("41100") // 売上
815
+ .amount(new BigDecimal("100000"))
816
+ .currencyCode("JPY")
817
+ .exchangeRate(BigDecimal.ONE)
818
+ .build();
819
+
820
+ var journalDetail = JournalDetail.builder()
821
+ .journalVoucherNumber(voucherNumber)
822
+ .lineNumber(1)
823
+ .lineSummary("現金売上")
824
+ .debitCreditDetails(List.of(debitDetail, creditDetail))
825
+ .build();
826
+
827
+ return Journal.builder()
828
+ .journalVoucherNumber(voucherNumber)
829
+ .postingDate(LocalDate.of(2024, 4, 1))
830
+ .entryDate(LocalDate.now())
831
+ .closingJournalFlag(false)
832
+ .singleEntryFlag(true)
833
+ .voucherType(JournalVoucherType.NORMAL)
834
+ .periodicPostingFlag(false)
835
+ .redSlipFlag(false)
836
+ .details(List.of(journalDetail))
837
+ .build();
838
+ }
839
+
840
+ private Journal createCompoundJournal(String voucherNumber) {
841
+ // 売掛金100,000円を現金80,000円と受取手形20,000円で回収
842
+ var debit1 = JournalDebitCreditDetail.builder()
843
+ .journalVoucherNumber(voucherNumber)
844
+ .lineNumber(1)
845
+ .debitCreditType(DebitCreditType.DEBIT)
846
+ .accountCode("11110") // 現金
847
+ .amount(new BigDecimal("80000"))
848
+ .build();
849
+
850
+ var credit1 = JournalDebitCreditDetail.builder()
851
+ .journalVoucherNumber(voucherNumber)
852
+ .lineNumber(1)
853
+ .debitCreditType(DebitCreditType.CREDIT)
854
+ .accountCode("11300") // 売掛金
855
+ .amount(new BigDecimal("100000"))
856
+ .build();
857
+
858
+ var debit2 = JournalDebitCreditDetail.builder()
859
+ .journalVoucherNumber(voucherNumber)
860
+ .lineNumber(2)
861
+ .debitCreditType(DebitCreditType.DEBIT)
862
+ .accountCode("11200") // 受取手形
863
+ .amount(new BigDecimal("20000"))
864
+ .build();
865
+
866
+ var detail1 = JournalDetail.builder()
867
+ .journalVoucherNumber(voucherNumber)
868
+ .lineNumber(1)
869
+ .lineSummary("現金入金")
870
+ .debitCreditDetails(List.of(debit1, credit1))
871
+ .build();
872
+
873
+ var detail2 = JournalDetail.builder()
874
+ .journalVoucherNumber(voucherNumber)
875
+ .lineNumber(2)
876
+ .lineSummary("手形入金")
877
+ .debitCreditDetails(List.of(debit2))
878
+ .build();
879
+
880
+ return Journal.builder()
881
+ .journalVoucherNumber(voucherNumber)
882
+ .postingDate(LocalDate.of(2024, 4, 5))
883
+ .entryDate(LocalDate.now())
884
+ .closingJournalFlag(false)
885
+ .singleEntryFlag(false) // 複合仕訳
886
+ .voucherType(JournalVoucherType.NORMAL)
887
+ .details(List.of(detail1, detail2))
888
+ .build();
889
+ }
890
+
891
+ private Journal createUnbalancedJournal(String voucherNumber) {
892
+ var debitDetail = JournalDebitCreditDetail.builder()
893
+ .journalVoucherNumber(voucherNumber)
894
+ .lineNumber(1)
895
+ .debitCreditType(DebitCreditType.DEBIT)
896
+ .accountCode("11110")
897
+ .amount(new BigDecimal("100000"))
898
+ .build();
899
+
900
+ var creditDetail = JournalDebitCreditDetail.builder()
901
+ .journalVoucherNumber(voucherNumber)
902
+ .lineNumber(1)
903
+ .debitCreditType(DebitCreditType.CREDIT)
904
+ .accountCode("41100")
905
+ .amount(new BigDecimal("90000")) // 不一致
906
+ .build();
907
+
908
+ var journalDetail = JournalDetail.builder()
909
+ .journalVoucherNumber(voucherNumber)
910
+ .lineNumber(1)
911
+ .debitCreditDetails(List.of(debitDetail, creditDetail))
912
+ .build();
913
+
914
+ return Journal.builder()
915
+ .journalVoucherNumber(voucherNumber)
916
+ .postingDate(LocalDate.of(2024, 4, 1))
917
+ .details(List.of(journalDetail))
918
+ .build();
919
+ }
920
+ }
921
+ ```
922
+
923
+ </details>
924
+
925
+ #### アプリケーション層(Output Port)
926
+
927
+ <details>
928
+ <summary>仕訳リポジトリインターフェース</summary>
929
+
930
+ ```java
931
+ // src/main/java/com/example/accounting/application/port/out/JournalRepository.java
932
+ package com.example.fas.application.port.out;
933
+
934
+ import com.example.fas.domain.model.journal.Journal;
935
+
936
+ import java.time.LocalDate;
937
+ import java.util.List;
938
+ import java.util.Optional;
939
+
940
+ /**
941
+ * 仕訳リポジトリ(Output Port)
942
+ */
943
+ public interface JournalRepository {
944
+
945
+ void save(Journal journal);
946
+
947
+ Optional<Journal> findByVoucherNumber(String voucherNumber);
948
+
949
+ List<Journal> findByPostingDateBetween(LocalDate fromDate, LocalDate toDate);
950
+
951
+ List<Journal> findByAccountCode(String accountCode);
952
+
953
+ List<Journal> findByDepartmentCode(String departmentCode);
954
+
955
+ void delete(String voucherNumber);
956
+
957
+ void deleteAll();
958
+ }
959
+ ```
960
+
961
+ </details>
962
+
963
+ #### インフラストラクチャ層
964
+
965
+ <details>
966
+ <summary>MyBatis XML Mapper</summary>
967
+
968
+ ```xml
969
+ <?xml version="1.0" encoding="UTF-8" ?>
970
+ <!DOCTYPE mapper
971
+ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
972
+ "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
973
+ <mapper namespace="com.example.fas.infrastructure.persistence.mapper.JournalMapper">
974
+
975
+ <!-- 仕訳 ResultMap -->
976
+ <resultMap id="JournalResultMap" type="com.example.fas.domain.model.journal.Journal">
977
+ <result property="journalVoucherNumber" column="仕訳伝票番号"/>
978
+ <result property="postingDate" column="起票日"/>
979
+ <result property="entryDate" column="入力日"/>
980
+ <result property="closingJournalFlag" column="決算仕訳フラグ"/>
981
+ <result property="singleEntryFlag" column="単振フラグ"/>
982
+ <result property="voucherType" column="仕訳伝票区分"
983
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler"/>
984
+ <result property="periodicPostingFlag" column="定期計上フラグ"/>
985
+ <result property="employeeCode" column="社員コード"/>
986
+ <result property="departmentCode" column="部門コード"/>
987
+ <result property="redSlipFlag" column="赤伝フラグ"/>
988
+ <result property="redBlackVoucherNumber" column="赤黒伝票番号"/>
989
+ <result property="createdAt" column="作成日時"/>
990
+ <result property="updatedAt" column="更新日時"/>
991
+ </resultMap>
992
+
993
+ <!-- 仕訳明細 ResultMap -->
994
+ <resultMap id="JournalDetailResultMap" type="com.example.fas.domain.model.journal.JournalDetail">
995
+ <result property="journalVoucherNumber" column="仕訳伝票番号"/>
996
+ <result property="lineNumber" column="仕訳行番号"/>
997
+ <result property="lineSummary" column="行摘要"/>
998
+ <result property="createdAt" column="作成日時"/>
999
+ <result property="updatedAt" column="更新日時"/>
1000
+ </resultMap>
1001
+
1002
+ <!-- 仕訳貸借明細 ResultMap -->
1003
+ <resultMap id="JournalDebitCreditDetailResultMap" type="com.example.fas.domain.model.journal.JournalDebitCreditDetail">
1004
+ <result property="journalVoucherNumber" column="仕訳伝票番号"/>
1005
+ <result property="lineNumber" column="仕訳行番号"/>
1006
+ <result property="debitCreditType" column="仕訳行貸借区分"
1007
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.DebitCreditTypeHandler"/>
1008
+ <result property="accountCode" column="勘定科目コード"/>
1009
+ <result property="subAccountCode" column="補助科目コード"/>
1010
+ <result property="departmentCode" column="部門コード"/>
1011
+ <result property="projectCode" column="プロジェクトコード"/>
1012
+ <result property="amount" column="仕訳金額"/>
1013
+ <result property="currencyCode" column="通貨コード"/>
1014
+ <result property="exchangeRate" column="為替レート"/>
1015
+ <result property="baseCurrencyAmount" column="基軸換算仕訳金額"/>
1016
+ <result property="taxType" column="消費税区分"
1017
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxTypeHandler"/>
1018
+ <result property="taxRate" column="消費税率"/>
1019
+ <result property="taxCalcType" column="消費税計算区分"
1020
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxCalculationTypeHandler"/>
1021
+ <result property="dueDate" column="期日"/>
1022
+ <result property="cashFlowFlag" column="資金繰フラグ"/>
1023
+ <result property="segmentCode" column="セグメントコード"/>
1024
+ <result property="counterAccountCode" column="相手勘定科目コード"/>
1025
+ <result property="counterSubAccountCode" column="相手補助科目コード"/>
1026
+ <result property="tagCode" column="付箋コード"/>
1027
+ <result property="tagContent" column="付箋内容"/>
1028
+ <result property="createdAt" column="作成日時"/>
1029
+ <result property="updatedAt" column="更新日時"/>
1030
+ </resultMap>
1031
+
1032
+ <!-- 仕訳ヘッダ登録 -->
1033
+ <insert id="insertJournal" parameterType="com.example.fas.domain.model.journal.Journal">
1034
+ INSERT INTO "仕訳" (
1035
+ "仕訳伝票番号", "起票日", "入力日", "決算仕訳フラグ", "単振フラグ",
1036
+ "仕訳伝票区分", "定期計上フラグ", "社員コード", "部門コード",
1037
+ "赤伝フラグ", "赤黒伝票番号", "作成日時", "更新日時"
1038
+ ) VALUES (
1039
+ #{journalVoucherNumber}, #{postingDate}, #{entryDate},
1040
+ #{closingJournalFlag}, #{singleEntryFlag},
1041
+ #{voucherType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler}::仕訳伝票区分,
1042
+ #{periodicPostingFlag}, #{employeeCode}, #{departmentCode},
1043
+ #{redSlipFlag}, #{redBlackVoucherNumber},
1044
+ CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
1045
+ )
1046
+ </insert>
1047
+
1048
+ <!-- 仕訳明細登録 -->
1049
+ <insert id="insertJournalDetail" parameterType="com.example.fas.domain.model.journal.JournalDetail">
1050
+ INSERT INTO "仕訳明細" (
1051
+ "仕訳伝票番号", "仕訳行番号", "行摘要", "作成日時", "更新日時"
1052
+ ) VALUES (
1053
+ #{journalVoucherNumber}, #{lineNumber}, #{lineSummary},
1054
+ CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
1055
+ )
1056
+ </insert>
1057
+
1058
+ <!-- 仕訳貸借明細登録 -->
1059
+ <insert id="insertJournalDebitCreditDetail" parameterType="com.example.fas.domain.model.journal.JournalDebitCreditDetail">
1060
+ INSERT INTO "仕訳貸借明細" (
1061
+ "仕訳伝票番号", "仕訳行番号", "仕訳行貸借区分",
1062
+ "勘定科目コード", "補助科目コード", "部門コード", "プロジェクトコード",
1063
+ "仕訳金額", "通貨コード", "為替レート", "基軸換算仕訳金額",
1064
+ "消費税区分", "消費税率", "消費税計算区分",
1065
+ "期日", "資金繰フラグ", "セグメントコード",
1066
+ "相手勘定科目コード", "相手補助科目コード",
1067
+ "付箋コード", "付箋内容", "作成日時", "更新日時"
1068
+ ) VALUES (
1069
+ #{journalVoucherNumber}, #{lineNumber},
1070
+ #{debitCreditType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.DebitCreditTypeHandler}::仕訳行貸借区分,
1071
+ #{accountCode}, #{subAccountCode}, #{departmentCode}, #{projectCode},
1072
+ #{amount}, #{currencyCode}, #{exchangeRate}, #{baseCurrencyAmount},
1073
+ #{taxType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.TaxTypeHandler}::消費税区分,
1074
+ #{taxRate},
1075
+ #{taxCalcType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.TaxCalculationTypeHandler}::消費税計算区分,
1076
+ #{dueDate}, #{cashFlowFlag}, #{segmentCode},
1077
+ #{counterAccountCode}, #{counterSubAccountCode},
1078
+ #{tagCode}, #{tagContent}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
1079
+ )
1080
+ </insert>
1081
+
1082
+ <!-- 仕訳伝票番号で検索 -->
1083
+ <select id="findByVoucherNumber" resultMap="JournalResultMap">
1084
+ SELECT * FROM "仕訳"
1085
+ WHERE "仕訳伝票番号" = #{voucherNumber}
1086
+ </select>
1087
+
1088
+ <!-- 仕訳明細検索 -->
1089
+ <select id="findDetailsByVoucherNumber" resultMap="JournalDetailResultMap">
1090
+ SELECT * FROM "仕訳明細"
1091
+ WHERE "仕訳伝票番号" = #{voucherNumber}
1092
+ ORDER BY "仕訳行番号"
1093
+ </select>
1094
+
1095
+ <!-- 仕訳貸借明細検索 -->
1096
+ <select id="findDCDetailsByVoucherAndLine" resultMap="JournalDebitCreditDetailResultMap">
1097
+ SELECT * FROM "仕訳貸借明細"
1098
+ WHERE "仕訳伝票番号" = #{voucherNumber}
1099
+ AND "仕訳行番号" = #{lineNumber}
1100
+ </select>
1101
+
1102
+ <!-- 起票日範囲で検索 -->
1103
+ <select id="findVoucherNumbersByDateRange" resultType="String">
1104
+ SELECT "仕訳伝票番号" FROM "仕訳"
1105
+ WHERE "起票日" BETWEEN #{fromDate} AND #{toDate}
1106
+ ORDER BY "仕訳伝票番号"
1107
+ </select>
1108
+
1109
+ <!-- 勘定科目コードで検索 -->
1110
+ <select id="findVoucherNumbersByAccountCode" resultType="String">
1111
+ SELECT DISTINCT "仕訳伝票番号" FROM "仕訳貸借明細"
1112
+ WHERE "勘定科目コード" = #{accountCode}
1113
+ ORDER BY "仕訳伝票番号"
1114
+ </select>
1115
+
1116
+ <!-- 削除 -->
1117
+ <delete id="deleteJournal">
1118
+ DELETE FROM "仕訳" WHERE "仕訳伝票番号" = #{voucherNumber}
1119
+ </delete>
1120
+
1121
+ <delete id="deleteAll">
1122
+ TRUNCATE TABLE "仕訳" CASCADE
1123
+ </delete>
1124
+ </mapper>
1125
+ ```
1126
+
1127
+ </details>
1128
+
1129
+ <details>
1130
+ <summary>仕訳リポジトリ実装</summary>
1131
+
1132
+ ```java
1133
+ // src/main/java/com/example/accounting/infrastructure/persistence/repository/JournalRepositoryImpl.java
1134
+ package com.example.fas.infrastructure.persistence.repository;
1135
+
1136
+ import com.example.fas.application.port.out.JournalRepository;
1137
+ import com.example.fas.domain.model.journal.Journal;
1138
+ import com.example.fas.infrastructure.persistence.mapper.JournalMapper;
1139
+ import lombok.RequiredArgsConstructor;
1140
+ import org.springframework.stereotype.Repository;
1141
+ import org.springframework.transaction.annotation.Transactional;
1142
+
1143
+ import java.time.LocalDate;
1144
+ import java.util.List;
1145
+ import java.util.Optional;
1146
+ import java.util.stream.Collectors;
1147
+
1148
+ @Repository
1149
+ @RequiredArgsConstructor
1150
+ public class JournalRepositoryImpl implements JournalRepository {
1151
+
1152
+ private final JournalMapper journalMapper;
1153
+
1154
+ @Override
1155
+ @Transactional
1156
+ public void save(Journal journal) {
1157
+ // 1. 仕訳ヘッダを登録
1158
+ journalMapper.insertJournal(journal);
1159
+
1160
+ // 2. 仕訳明細を登録
1161
+ journal.getDetails().forEach(detail -> {
1162
+ journalMapper.insertJournalDetail(detail);
1163
+
1164
+ // 3. 仕訳貸借明細を登録
1165
+ detail.getDebitCreditDetails().forEach(dcDetail -> {
1166
+ journalMapper.insertJournalDebitCreditDetail(dcDetail);
1167
+ });
1168
+ });
1169
+ }
1170
+
1171
+ @Override
1172
+ public Optional<Journal> findByVoucherNumber(String voucherNumber) {
1173
+ return journalMapper.findByVoucherNumber(voucherNumber)
1174
+ .map(journal -> {
1175
+ var details = journalMapper.findDetailsByVoucherNumber(voucherNumber);
1176
+ details.forEach(detail -> {
1177
+ var dcDetails = journalMapper.findDCDetailsByVoucherAndLine(
1178
+ voucherNumber, detail.getLineNumber());
1179
+ detail.setDebitCreditDetails(dcDetails);
1180
+ });
1181
+ journal.setDetails(details);
1182
+ return journal;
1183
+ });
1184
+ }
1185
+
1186
+ @Override
1187
+ public List<Journal> findByPostingDateBetween(LocalDate fromDate, LocalDate toDate) {
1188
+ return journalMapper.findVoucherNumbersByDateRange(fromDate, toDate).stream()
1189
+ .map(this::findByVoucherNumber)
1190
+ .filter(Optional::isPresent)
1191
+ .map(Optional::get)
1192
+ .collect(Collectors.toList());
1193
+ }
1194
+
1195
+ @Override
1196
+ public List<Journal> findByAccountCode(String accountCode) {
1197
+ return journalMapper.findVoucherNumbersByAccountCode(accountCode).stream()
1198
+ .map(this::findByVoucherNumber)
1199
+ .filter(Optional::isPresent)
1200
+ .map(Optional::get)
1201
+ .collect(Collectors.toList());
1202
+ }
1203
+
1204
+ @Override
1205
+ public List<Journal> findByDepartmentCode(String departmentCode) {
1206
+ return journalMapper.findVoucherNumbersByDepartmentCode(departmentCode).stream()
1207
+ .map(this::findByVoucherNumber)
1208
+ .filter(Optional::isPresent)
1209
+ .map(Optional::get)
1210
+ .collect(Collectors.toList());
1211
+ }
1212
+
1213
+ @Override
1214
+ @Transactional
1215
+ public void delete(String voucherNumber) {
1216
+ journalMapper.deleteJournal(voucherNumber);
1217
+ }
1218
+
1219
+ @Override
1220
+ @Transactional
1221
+ public void deleteAll() {
1222
+ journalMapper.deleteAll();
1223
+ }
1224
+ }
1225
+ ```
1226
+
1227
+ </details>
1228
+
1229
+ ---
1230
+
1231
+ ## 16.4 リレーションと楽観ロックの設計
1232
+
1233
+ ### MyBatis ネストした ResultMap によるリレーション設定
1234
+
1235
+ 仕訳データは、仕訳(ヘッダ)→ 仕訳明細 → 仕訳貸借明細の3層構造を持ちます。MyBatis でこの親子関係を効率的に取得するためのリレーション設定を実装します。
1236
+
1237
+ #### ネストした ResultMap の定義
1238
+
1239
+ <details>
1240
+ <summary>JournalMapper.xml(リレーション設定)</summary>
1241
+
1242
+ ```xml
1243
+ <?xml version="1.0" encoding="UTF-8" ?>
1244
+ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
1245
+ "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
1246
+
1247
+ <!-- src/main/resources/mapper/JournalMapper.xml -->
1248
+ <mapper namespace="com.example.fas.infrastructure.persistence.mapper.JournalMapper">
1249
+
1250
+ <!-- 仕訳(ヘッダ)の ResultMap(リレーション込み) -->
1251
+ <resultMap id="journalWithDetailsResultMap" type="com.example.fas.domain.model.journal.Journal">
1252
+ <id property="journalVoucherNumber" column="j_仕訳伝票番号"/>
1253
+ <result property="postingDate" column="j_起票日"/>
1254
+ <result property="entryDate" column="j_入力日"/>
1255
+ <result property="closingJournalFlag" column="j_決算仕訳フラグ"/>
1256
+ <result property="singleEntryFlag" column="j_単振フラグ"/>
1257
+ <result property="voucherType" column="j_仕訳伝票区分"
1258
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler"/>
1259
+ <result property="periodicPostingFlag" column="j_定期計上フラグ"/>
1260
+ <result property="employeeCode" column="j_社員コード"/>
1261
+ <result property="departmentCode" column="j_部門コード"/>
1262
+ <result property="redSlipFlag" column="j_赤伝フラグ"/>
1263
+ <result property="redBlackVoucherNumber" column="j_赤黒伝票番号"/>
1264
+ <result property="version" column="j_バージョン"/>
1265
+ <result property="createdAt" column="j_作成日時"/>
1266
+ <result property="updatedAt" column="j_更新日時"/>
1267
+ <!-- 仕訳明細との1:N関連 -->
1268
+ <collection property="details" ofType="com.example.fas.domain.model.journal.JournalDetail"
1269
+ resultMap="journalDetailNestedResultMap"/>
1270
+ </resultMap>
1271
+
1272
+ <!-- 仕訳明細のネスト ResultMap -->
1273
+ <resultMap id="journalDetailNestedResultMap" type="com.example.fas.domain.model.journal.JournalDetail">
1274
+ <id property="journalVoucherNumber" column="d_仕訳伝票番号"/>
1275
+ <id property="lineNumber" column="d_仕訳行番号"/>
1276
+ <result property="lineSummary" column="d_行摘要"/>
1277
+ <result property="version" column="d_バージョン"/>
1278
+ <result property="createdAt" column="d_作成日時"/>
1279
+ <result property="updatedAt" column="d_更新日時"/>
1280
+ <!-- 仕訳貸借明細との1:N関連 -->
1281
+ <collection property="debitCreditDetails" ofType="com.example.fas.domain.model.journal.JournalDebitCreditDetail"
1282
+ resultMap="journalDebitCreditDetailNestedResultMap"/>
1283
+ </resultMap>
1284
+
1285
+ <!-- 仕訳貸借明細のネスト ResultMap -->
1286
+ <resultMap id="journalDebitCreditDetailNestedResultMap" type="com.example.fas.domain.model.journal.JournalDebitCreditDetail">
1287
+ <id property="journalVoucherNumber" column="dc_仕訳伝票番号"/>
1288
+ <id property="lineNumber" column="dc_仕訳行番号"/>
1289
+ <id property="debitCreditType" column="dc_仕訳行貸借区分"
1290
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.DebitCreditTypeHandler"/>
1291
+ <result property="accountCode" column="dc_勘定科目コード"/>
1292
+ <result property="subAccountCode" column="dc_補助科目コード"/>
1293
+ <result property="departmentCode" column="dc_部門コード"/>
1294
+ <result property="projectCode" column="dc_プロジェクトコード"/>
1295
+ <result property="amount" column="dc_仕訳金額"/>
1296
+ <result property="currencyCode" column="dc_通貨コード"/>
1297
+ <result property="exchangeRate" column="dc_為替レート"/>
1298
+ <result property="baseCurrencyAmount" column="dc_基軸換算仕訳金額"/>
1299
+ <result property="taxType" column="dc_消費税区分"
1300
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxTypeHandler"/>
1301
+ <result property="taxRate" column="dc_消費税率"/>
1302
+ <result property="taxCalcType" column="dc_消費税計算区分"
1303
+ typeHandler="com.example.fas.infrastructure.persistence.typehandler.TaxCalculationTypeHandler"/>
1304
+ <result property="dueDate" column="dc_期日"/>
1305
+ <result property="cashFlowFlag" column="dc_資金繰フラグ"/>
1306
+ <result property="segmentCode" column="dc_セグメントコード"/>
1307
+ <result property="counterAccountCode" column="dc_相手勘定科目コード"/>
1308
+ <result property="counterSubAccountCode" column="dc_相手補助科目コード"/>
1309
+ <result property="tagCode" column="dc_付箋コード"/>
1310
+ <result property="tagContent" column="dc_付箋内容"/>
1311
+ <result property="version" column="dc_バージョン"/>
1312
+ <result property="createdAt" column="dc_作成日時"/>
1313
+ <result property="updatedAt" column="dc_更新日時"/>
1314
+ </resultMap>
1315
+
1316
+ <!-- JOIN による一括取得クエリ(3層構造) -->
1317
+ <select id="findWithDetailsByVoucherNumber" resultMap="journalWithDetailsResultMap">
1318
+ SELECT
1319
+ j."仕訳伝票番号" AS j_仕訳伝票番号,
1320
+ j."起票日" AS j_起票日,
1321
+ j."入力日" AS j_入力日,
1322
+ j."決算仕訳フラグ" AS j_決算仕訳フラグ,
1323
+ j."単振フラグ" AS j_単振フラグ,
1324
+ j."仕訳伝票区分" AS j_仕訳伝票区分,
1325
+ j."定期計上フラグ" AS j_定期計上フラグ,
1326
+ j."社員コード" AS j_社員コード,
1327
+ j."部門コード" AS j_部門コード,
1328
+ j."赤伝フラグ" AS j_赤伝フラグ,
1329
+ j."赤黒伝票番号" AS j_赤黒伝票番号,
1330
+ j."バージョン" AS j_バージョン,
1331
+ j."作成日時" AS j_作成日時,
1332
+ j."更新日時" AS j_更新日時,
1333
+ d."仕訳伝票番号" AS d_仕訳伝票番号,
1334
+ d."仕訳行番号" AS d_仕訳行番号,
1335
+ d."行摘要" AS d_行摘要,
1336
+ d."バージョン" AS d_バージョン,
1337
+ d."作成日時" AS d_作成日時,
1338
+ d."更新日時" AS d_更新日時,
1339
+ dc."仕訳伝票番号" AS dc_仕訳伝票番号,
1340
+ dc."仕訳行番号" AS dc_仕訳行番号,
1341
+ dc."仕訳行貸借区分" AS dc_仕訳行貸借区分,
1342
+ dc."勘定科目コード" AS dc_勘定科目コード,
1343
+ dc."補助科目コード" AS dc_補助科目コード,
1344
+ dc."部門コード" AS dc_部門コード,
1345
+ dc."プロジェクトコード" AS dc_プロジェクトコード,
1346
+ dc."仕訳金額" AS dc_仕訳金額,
1347
+ dc."通貨コード" AS dc_通貨コード,
1348
+ dc."為替レート" AS dc_為替レート,
1349
+ dc."基軸換算仕訳金額" AS dc_基軸換算仕訳金額,
1350
+ dc."消費税区分" AS dc_消費税区分,
1351
+ dc."消費税率" AS dc_消費税率,
1352
+ dc."消費税計算区分" AS dc_消費税計算区分,
1353
+ dc."期日" AS dc_期日,
1354
+ dc."資金繰フラグ" AS dc_資金繰フラグ,
1355
+ dc."セグメントコード" AS dc_セグメントコード,
1356
+ dc."相手勘定科目コード" AS dc_相手勘定科目コード,
1357
+ dc."相手補助科目コード" AS dc_相手補助科目コード,
1358
+ dc."付箋コード" AS dc_付箋コード,
1359
+ dc."付箋内容" AS dc_付箋内容,
1360
+ dc."バージョン" AS dc_バージョン,
1361
+ dc."作成日時" AS dc_作成日時,
1362
+ dc."更新日時" AS dc_更新日時
1363
+ FROM "仕訳" j
1364
+ LEFT JOIN "仕訳明細" d ON j."仕訳伝票番号" = d."仕訳伝票番号"
1365
+ LEFT JOIN "仕訳貸借明細" dc ON d."仕訳伝票番号" = dc."仕訳伝票番号"
1366
+ AND d."仕訳行番号" = dc."仕訳行番号"
1367
+ WHERE j."仕訳伝票番号" = #{voucherNumber}
1368
+ ORDER BY d."仕訳行番号", dc."仕訳行貸借区分"
1369
+ </select>
1370
+
1371
+ </mapper>
1372
+ ```
1373
+
1374
+ </details>
1375
+
1376
+ #### リレーション設定のポイント
1377
+
1378
+ | 設定項目 | 説明 |
1379
+ |---------|------|
1380
+ | `<collection>` | 1:N 関連のマッピング |
1381
+ | `<id>` | 主キーの識別(MyBatis が重複排除に使用) |
1382
+ | 複合主キー | 仕訳明細・仕訳貸借明細は複数の `<id>` で複合主キーを表現 |
1383
+ | `resultMap` | ネストした ResultMap の参照 |
1384
+ | エイリアス(AS) | カラム名の重複を避けるためのプレフィックス(j_, d_, dc_) |
1385
+ | `ORDER BY` | コレクションの順序を保証 |
1386
+
1387
+ ### 楽観ロックの実装
1388
+
1389
+ 複数ユーザーが同時に仕訳データを編集する場合、データの整合性を保つために楽観ロック(Optimistic Locking)を実装します。
1390
+
1391
+ #### Flyway マイグレーション: バージョンカラム追加
1392
+
1393
+ <details>
1394
+ <summary>V006__add_journal_version_columns.sql</summary>
1395
+
1396
+ ```sql
1397
+ -- src/main/resources/db/migration/V006__add_journal_version_columns.sql
1398
+
1399
+ -- 仕訳テーブルにバージョンカラムを追加
1400
+ ALTER TABLE "仕訳" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
1401
+
1402
+ -- 仕訳明細テーブルにバージョンカラムを追加
1403
+ ALTER TABLE "仕訳明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
1404
+
1405
+ -- 仕訳貸借明細テーブルにバージョンカラムを追加
1406
+ ALTER TABLE "仕訳貸借明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
1407
+
1408
+ -- コメント追加
1409
+ COMMENT ON COLUMN "仕訳"."バージョン" IS '楽観ロック用バージョン番号';
1410
+ COMMENT ON COLUMN "仕訳明細"."バージョン" IS '楽観ロック用バージョン番号';
1411
+ COMMENT ON COLUMN "仕訳貸借明細"."バージョン" IS '楽観ロック用バージョン番号';
1412
+ ```
1413
+
1414
+ </details>
1415
+
1416
+ #### エンティティへのバージョンフィールド追加
1417
+
1418
+ <details>
1419
+ <summary>Journal.java(バージョンフィールド追加)</summary>
1420
+
1421
+ ```java
1422
+ // src/main/java/com/example/fas/domain/model/journal/Journal.java
1423
+ package com.example.fas.domain.model.journal;
1424
+
1425
+ import lombok.AllArgsConstructor;
1426
+ import lombok.Builder;
1427
+ import lombok.Data;
1428
+ import lombok.NoArgsConstructor;
1429
+
1430
+ import java.math.BigDecimal;
1431
+ import java.time.LocalDate;
1432
+ import java.time.LocalDateTime;
1433
+ import java.util.ArrayList;
1434
+ import java.util.List;
1435
+
1436
+ @Data
1437
+ @Builder
1438
+ @NoArgsConstructor
1439
+ @AllArgsConstructor
1440
+ public class Journal {
1441
+ private String journalVoucherNumber; // 仕訳伝票番号
1442
+ private LocalDate postingDate; // 起票日
1443
+ private LocalDate entryDate; // 入力日
1444
+ private Boolean closingJournalFlag; // 決算仕訳フラグ
1445
+ private Boolean singleEntryFlag; // 単振フラグ
1446
+ private JournalVoucherType voucherType; // 仕訳伝票区分
1447
+ private Boolean periodicPostingFlag; // 定期計上フラグ
1448
+ private String employeeCode; // 社員コード
1449
+ private String departmentCode; // 部門コード
1450
+ private Boolean redSlipFlag; // 赤伝フラグ
1451
+ private Integer redBlackVoucherNumber; // 赤黒伝票番号
1452
+ private LocalDateTime createdAt; // 作成日時
1453
+ private LocalDateTime updatedAt; // 更新日時
1454
+
1455
+ // 楽観ロック用バージョン
1456
+ @Builder.Default
1457
+ private Integer version = 1;
1458
+
1459
+ // リレーション
1460
+ @Builder.Default
1461
+ private List<JournalDetail> details = new ArrayList<>();
1462
+
1463
+ // 以下、既存のビジネスロジック...
1464
+ }
1465
+ ```
1466
+
1467
+ </details>
1468
+
1469
+ #### MyBatis Mapper: 楽観ロック対応の更新
1470
+
1471
+ <details>
1472
+ <summary>JournalMapper.xml(楽観ロック対応 UPDATE)</summary>
1473
+
1474
+ ```xml
1475
+ <!-- 楽観ロック対応の更新(バージョンチェック付き) -->
1476
+ <update id="updateWithOptimisticLock" parameterType="com.example.fas.domain.model.journal.Journal">
1477
+ UPDATE "仕訳"
1478
+ SET
1479
+ "起票日" = #{postingDate},
1480
+ "入力日" = #{entryDate},
1481
+ "決算仕訳フラグ" = #{closingJournalFlag},
1482
+ "単振フラグ" = #{singleEntryFlag},
1483
+ "仕訳伝票区分" = #{voucherType, typeHandler=com.example.fas.infrastructure.persistence.typehandler.JournalVoucherTypeHandler}::仕訳伝票区分,
1484
+ "定期計上フラグ" = #{periodicPostingFlag},
1485
+ "社員コード" = #{employeeCode},
1486
+ "部門コード" = #{departmentCode},
1487
+ "赤伝フラグ" = #{redSlipFlag},
1488
+ "赤黒伝票番号" = #{redBlackVoucherNumber},
1489
+ "更新日時" = CURRENT_TIMESTAMP,
1490
+ "バージョン" = "バージョン" + 1
1491
+ WHERE "仕訳伝票番号" = #{journalVoucherNumber}
1492
+ AND "バージョン" = #{version}
1493
+ </update>
1494
+
1495
+ <!-- バージョンのみ取得 -->
1496
+ <select id="findVersionByVoucherNumber" resultType="Integer">
1497
+ SELECT "バージョン" FROM "仕訳"
1498
+ WHERE "仕訳伝票番号" = #{voucherNumber}
1499
+ </select>
1500
+ ```
1501
+
1502
+ </details>
1503
+
1504
+ #### 楽観ロック例外クラス
1505
+
1506
+ <details>
1507
+ <summary>OptimisticLockException.java</summary>
1508
+
1509
+ ```java
1510
+ // src/main/java/com/example/fas/domain/exception/OptimisticLockException.java
1511
+ package com.example.fas.domain.exception;
1512
+
1513
+ public class OptimisticLockException extends RuntimeException {
1514
+
1515
+ private final String entityName;
1516
+ private final String entityId;
1517
+ private final Integer expectedVersion;
1518
+ private final Integer actualVersion;
1519
+
1520
+ public OptimisticLockException(String entityName, String entityId) {
1521
+ super(String.format("%s (ID: %s) は既に削除されています", entityName, entityId));
1522
+ this.entityName = entityName;
1523
+ this.entityId = entityId;
1524
+ this.expectedVersion = null;
1525
+ this.actualVersion = null;
1526
+ }
1527
+
1528
+ public OptimisticLockException(String entityName, String entityId,
1529
+ Integer expectedVersion, Integer actualVersion) {
1530
+ super(String.format("%s (ID: %s) は他のユーザーによって更新されています。" +
1531
+ "期待バージョン: %d, 実際のバージョン: %d",
1532
+ entityName, entityId, expectedVersion, actualVersion));
1533
+ this.entityName = entityName;
1534
+ this.entityId = entityId;
1535
+ this.expectedVersion = expectedVersion;
1536
+ this.actualVersion = actualVersion;
1537
+ }
1538
+
1539
+ public String getEntityName() {
1540
+ return entityName;
1541
+ }
1542
+
1543
+ public String getEntityId() {
1544
+ return entityId;
1545
+ }
1546
+
1547
+ public Integer getExpectedVersion() {
1548
+ return expectedVersion;
1549
+ }
1550
+
1551
+ public Integer getActualVersion() {
1552
+ return actualVersion;
1553
+ }
1554
+ }
1555
+ ```
1556
+
1557
+ </details>
1558
+
1559
+ #### Repository 実装: 楽観ロック対応
1560
+
1561
+ <details>
1562
+ <summary>JournalRepositoryImpl.java(楽観ロック対応)</summary>
1563
+
1564
+ ```java
1565
+ // src/main/java/com/example/fas/infrastructure/persistence/repository/JournalRepositoryImpl.java
1566
+ package com.example.fas.infrastructure.persistence.repository;
1567
+
1568
+ import com.example.fas.application.port.out.JournalRepository;
1569
+ import com.example.fas.domain.exception.OptimisticLockException;
1570
+ import com.example.fas.domain.model.journal.Journal;
1571
+ import com.example.fas.infrastructure.persistence.mapper.JournalMapper;
1572
+ import lombok.RequiredArgsConstructor;
1573
+ import org.springframework.stereotype.Repository;
1574
+ import org.springframework.transaction.annotation.Transactional;
1575
+
1576
+ import java.time.LocalDate;
1577
+ import java.util.List;
1578
+ import java.util.Optional;
1579
+ import java.util.stream.Collectors;
1580
+
1581
+ @Repository
1582
+ @RequiredArgsConstructor
1583
+ public class JournalRepositoryImpl implements JournalRepository {
1584
+
1585
+ private final JournalMapper journalMapper;
1586
+
1587
+ @Override
1588
+ @Transactional
1589
+ public void update(Journal journal) {
1590
+ int updatedCount = journalMapper.updateWithOptimisticLock(journal);
1591
+
1592
+ if (updatedCount == 0) {
1593
+ // バージョン不一致または削除済み
1594
+ Integer currentVersion = journalMapper.findVersionByVoucherNumber(
1595
+ journal.getJournalVoucherNumber());
1596
+ if (currentVersion == null) {
1597
+ throw new OptimisticLockException("仕訳", journal.getJournalVoucherNumber());
1598
+ } else {
1599
+ throw new OptimisticLockException("仕訳", journal.getJournalVoucherNumber(),
1600
+ journal.getVersion(), currentVersion);
1601
+ }
1602
+ }
1603
+ }
1604
+
1605
+ @Override
1606
+ public Optional<Journal> findByVoucherNumberWithDetails(String voucherNumber) {
1607
+ return Optional.ofNullable(journalMapper.findWithDetailsByVoucherNumber(voucherNumber));
1608
+ }
1609
+
1610
+ // その他のメソッド...
1611
+ }
1612
+ ```
1613
+
1614
+ </details>
1615
+
1616
+ #### TDD: 楽観ロックのテスト
1617
+
1618
+ <details>
1619
+ <summary>JournalRepositoryOptimisticLockTest.java</summary>
1620
+
1621
+ ```java
1622
+ // src/test/java/com/example/fas/infrastructure/persistence/repository/JournalRepositoryOptimisticLockTest.java
1623
+ package com.example.fas.infrastructure.persistence.repository;
1624
+
1625
+ import com.example.fas.application.port.out.JournalRepository;
1626
+ import com.example.fas.domain.exception.OptimisticLockException;
1627
+ import com.example.fas.domain.model.account.DebitCreditType;
1628
+ import com.example.fas.domain.model.journal.*;
1629
+ import com.example.fas.testsetup.BaseIntegrationTest;
1630
+ import org.junit.jupiter.api.*;
1631
+ import org.springframework.beans.factory.annotation.Autowired;
1632
+
1633
+ import java.math.BigDecimal;
1634
+ import java.time.LocalDate;
1635
+ import java.util.List;
1636
+
1637
+ import static org.assertj.core.api.Assertions.*;
1638
+
1639
+ @DisplayName("仕訳リポジトリ - 楽観ロック")
1640
+ class JournalRepositoryOptimisticLockTest extends BaseIntegrationTest {
1641
+
1642
+ @Autowired
1643
+ private JournalRepository journalRepository;
1644
+
1645
+ @BeforeEach
1646
+ void setUp() {
1647
+ journalRepository.deleteAll();
1648
+ }
1649
+
1650
+ @Nested
1651
+ @DisplayName("楽観ロック")
1652
+ class OptimisticLocking {
1653
+
1654
+ @Test
1655
+ @DisplayName("同じバージョンで更新できる")
1656
+ void canUpdateWithSameVersion() {
1657
+ // Arrange
1658
+ var journal = createTestJournal("J0001");
1659
+ journalRepository.save(journal);
1660
+
1661
+ // Act
1662
+ var fetched = journalRepository.findByVoucherNumber("J0001").get();
1663
+ fetched.setDepartmentCode("DEPT-002");
1664
+ journalRepository.update(fetched);
1665
+
1666
+ // Assert
1667
+ var updated = journalRepository.findByVoucherNumber("J0001").get();
1668
+ assertThat(updated.getDepartmentCode()).isEqualTo("DEPT-002");
1669
+ assertThat(updated.getVersion()).isEqualTo(2); // バージョンがインクリメント
1670
+ }
1671
+
1672
+ @Test
1673
+ @DisplayName("異なるバージョンで更新すると楽観ロック例外が発生する")
1674
+ void throwsExceptionWhenVersionMismatch() {
1675
+ // Arrange
1676
+ var journal = createTestJournal("J0002");
1677
+ journalRepository.save(journal);
1678
+
1679
+ // ユーザーAが取得
1680
+ var journalA = journalRepository.findByVoucherNumber("J0002").get();
1681
+ // ユーザーBが取得
1682
+ var journalB = journalRepository.findByVoucherNumber("J0002").get();
1683
+
1684
+ // ユーザーAが更新(成功)
1685
+ journalA.setDepartmentCode("DEPT-A");
1686
+ journalRepository.update(journalA);
1687
+
1688
+ // Act & Assert: ユーザーBが古いバージョンで更新(失敗)
1689
+ journalB.setDepartmentCode("DEPT-B");
1690
+ assertThatThrownBy(() -> journalRepository.update(journalB))
1691
+ .isInstanceOf(OptimisticLockException.class)
1692
+ .hasMessageContaining("他のユーザーによって更新されています");
1693
+ }
1694
+
1695
+ @Test
1696
+ @DisplayName("削除済みデータを更新すると楽観ロック例外が発生する")
1697
+ void throwsExceptionWhenAlreadyDeleted() {
1698
+ // Arrange
1699
+ var journal = createTestJournal("J0003");
1700
+ journalRepository.save(journal);
1701
+
1702
+ var fetched = journalRepository.findByVoucherNumber("J0003").get();
1703
+ journalRepository.delete("J0003");
1704
+
1705
+ // Act & Assert
1706
+ assertThatThrownBy(() -> journalRepository.update(fetched))
1707
+ .isInstanceOf(OptimisticLockException.class)
1708
+ .hasMessageContaining("既に削除されています");
1709
+ }
1710
+ }
1711
+
1712
+ private Journal createTestJournal(String voucherNumber) {
1713
+ var debitDetail = JournalDebitCreditDetail.builder()
1714
+ .journalVoucherNumber(voucherNumber)
1715
+ .lineNumber(1)
1716
+ .debitCreditType(DebitCreditType.DEBIT)
1717
+ .accountCode("11110")
1718
+ .amount(new BigDecimal("100000"))
1719
+ .build();
1720
+
1721
+ var creditDetail = JournalDebitCreditDetail.builder()
1722
+ .journalVoucherNumber(voucherNumber)
1723
+ .lineNumber(1)
1724
+ .debitCreditType(DebitCreditType.CREDIT)
1725
+ .accountCode("41100")
1726
+ .amount(new BigDecimal("100000"))
1727
+ .build();
1728
+
1729
+ var journalDetail = JournalDetail.builder()
1730
+ .journalVoucherNumber(voucherNumber)
1731
+ .lineNumber(1)
1732
+ .lineSummary("テスト仕訳")
1733
+ .debitCreditDetails(List.of(debitDetail, creditDetail))
1734
+ .build();
1735
+
1736
+ return Journal.builder()
1737
+ .journalVoucherNumber(voucherNumber)
1738
+ .postingDate(LocalDate.of(2024, 4, 1))
1739
+ .entryDate(LocalDate.now())
1740
+ .closingJournalFlag(false)
1741
+ .singleEntryFlag(true)
1742
+ .voucherType(JournalVoucherType.NORMAL)
1743
+ .periodicPostingFlag(false)
1744
+ .departmentCode("DEPT-001")
1745
+ .redSlipFlag(false)
1746
+ .details(List.of(journalDetail))
1747
+ .build();
1748
+ }
1749
+ }
1750
+ ```
1751
+
1752
+ </details>
1753
+
1754
+ #### 楽観ロックのベストプラクティス
1755
+
1756
+ | ポイント | 説明 |
1757
+ |---------|------|
1758
+ | **バージョンカラム** | INTEGER 型で十分(オーバーフローは実用上問題なし) |
1759
+ | **WHERE 条件** | 必ず `AND "バージョン" = #{version}` を含める |
1760
+ | **インクリメント** | `"バージョン" = "バージョン" + 1` でアトミックに更新 |
1761
+ | **例外処理** | 更新件数が0の場合は楽観ロック例外をスロー |
1762
+ | **リトライ** | 必要に応じて再取得・再更新のリトライロジックを実装 |
1763
+ | **3層構造の考慮** | 仕訳は通常ヘッダ単位でロック、明細の個別ロックは必要に応じて追加 |
1764
+
1765
+ ### 3層構造におけるリレーションと整合性
1766
+
1767
+ 仕訳データは3層構造(仕訳 → 仕訳明細 → 仕訳貸借明細)を持つため、更新時の整合性確保が重要です。
1768
+
1769
+ #### 更新パターン
1770
+
1771
+ | パターン | 説明 | 推奨 |
1772
+ |---------|------|------|
1773
+ | **ヘッダ単位ロック** | 仕訳ヘッダのバージョンのみチェック | ○ シンプルで実用的 |
1774
+ | **全層ロック** | 仕訳・明細・貸借明細すべてのバージョンをチェック | △ 複雑だが厳密 |
1775
+ | **カスケード更新** | ヘッダ更新時に明細も一括削除・再挿入 | △ 実装が簡単だが履歴管理に注意 |
1776
+
1777
+ #### 推奨アプローチ
1778
+
1779
+ 仕訳データは通常、伝票単位で一括編集されるため、**ヘッダ単位ロック**が推奨されます。明細の部分更新が必要な場合は、明細レベルのバージョンチェックを追加します。
1780
+
1781
+ ```plantuml
1782
+ @startuml
1783
+
1784
+ title 仕訳更新の楽観ロックフロー
1785
+
1786
+ actor ユーザーA
1787
+ actor ユーザーB
1788
+ database DB
1789
+
1790
+ == 取得フェーズ ==
1791
+ ユーザーA -> DB : 仕訳J0001取得
1792
+ DB --> ユーザーA : Journal(version=1)
1793
+ ユーザーB -> DB : 仕訳J0001取得
1794
+ DB --> ユーザーB : Journal(version=1)
1795
+
1796
+ == 更新フェーズ ==
1797
+ ユーザーA -> DB : UPDATE WHERE version=1
1798
+ DB --> ユーザーA : 更新成功 (version=2)
1799
+
1800
+ ユーザーB -> DB : UPDATE WHERE version=1
1801
+ DB --> ユーザーB : 更新件数=0
1802
+ ユーザーB -> ユーザーB : OptimisticLockException
1803
+
1804
+ @enduml
1805
+ ```
1806
+
1807
+ ---
1808
+
1809
+ ## 第16章のまとめ
1810
+
1811
+ ### 作成したテーブル
1812
+
1813
+ | テーブル名 | 説明 |
1814
+ |----------|------|
1815
+ | `仕訳` | 仕訳伝票のヘッダ情報(起票日、入力日、各種フラグ) |
1816
+ | `仕訳明細` | 仕訳の行単位の情報(行摘要) |
1817
+ | `仕訳貸借明細` | 借方・貸方の詳細(勘定科目、金額、補助情報) |
1818
+
1819
+ ### ER図
1820
+
1821
+ ```plantuml
1822
+ @startuml
1823
+
1824
+ title 仕訳テーブル ER図
1825
+
1826
+ entity 仕訳 {
1827
+ 仕訳伝票番号 <<PK>>
1828
+ --
1829
+ 起票日
1830
+ 入力日
1831
+ 決算仕訳フラグ
1832
+ 単振フラグ
1833
+ 仕訳伝票区分
1834
+ 定期計上フラグ
1835
+ 社員コード
1836
+ 部門コード
1837
+ 赤伝フラグ
1838
+ 赤黒伝票番号
1839
+ 作成日時
1840
+ 更新日時
1841
+ }
1842
+
1843
+ entity 仕訳明細 {
1844
+ 仕訳伝票番号 <<PK,FK>>
1845
+ 仕訳行番号 <<PK>>
1846
+ --
1847
+ 行摘要
1848
+ 作成日時
1849
+ 更新日時
1850
+ }
1851
+
1852
+ entity 仕訳貸借明細 {
1853
+ 仕訳伝票番号 <<PK,FK>>
1854
+ 仕訳行番号 <<PK>>
1855
+ 仕訳行貸借区分 <<PK>>
1856
+ --
1857
+ 勘定科目コード <<FK>>
1858
+ 補助科目コード
1859
+ 部門コード <<FK>>
1860
+ プロジェクトコード
1861
+ 仕訳金額
1862
+ 通貨コード
1863
+ 為替レート
1864
+ 基軸換算仕訳金額
1865
+ 消費税区分
1866
+ 消費税率
1867
+ 消費税計算区分
1868
+ 期日
1869
+ ...
1870
+ }
1871
+
1872
+ entity 勘定科目マスタ {
1873
+ 勘定科目コード <<PK>>
1874
+ --
1875
+ ...
1876
+ }
1877
+
1878
+ entity 部門マスタ {
1879
+ 部門コード <<PK>>
1880
+ --
1881
+ ...
1882
+ }
1883
+
1884
+ 仕訳 ||--o{ 仕訳明細 : "含む"
1885
+ 仕訳明細 ||--o{ 仕訳貸借明細 : "含む"
1886
+ 仕訳貸借明細 }o..|| 勘定科目マスタ : "参照"
1887
+ 仕訳貸借明細 }o..o| 部門マスタ : "参照"
1888
+ 仕訳 }o..o| 部門マスタ : "参照"
1889
+
1890
+ @enduml
1891
+ ```
1892
+
1893
+ ### 設計のポイント
1894
+
1895
+ 1. **3階層構造**: 仕訳 → 仕訳明細 → 仕訳貸借明細の親子関係
1896
+ 2. **複式簿記の原則**: 借方合計 = 貸方合計の貸借一致チェック
1897
+ 3. **単一仕訳と複合仕訳**: 単振フラグで区別
1898
+ 4. **赤黒処理対応**: 赤伝フラグと赤黒伝票番号で訂正履歴を管理
1899
+ 5. **多通貨対応**: 通貨コード、為替レート、基軸換算金額
1900
+ 6. **消費税対応**: 消費税区分、消費税率、消費税計算区分
1901
+ 7. **管理会計対応**: 部門コード、プロジェクトコード、セグメントコード
1902
+
1903
+ ### ドメインロジック
1904
+
1905
+ | メソッド | 説明 |
1906
+ |---------|------|
1907
+ | `isBalanced()` | 借方合計と貸方合計が一致するかチェック |
1908
+ | `getDebitTotal()` | 借方合計を取得 |
1909
+ | `getCreditTotal()` | 貸方合計を取得 |
1910
+
1911
+ ---
1912
+
1913
+ ## 次章の予告
1914
+
1915
+ 第17章では、自動仕訳の設計に進みます。販売管理システムと会計システムの連携、自動仕訳パターンマスタの設計、売上伝票から仕訳伝票への転記ロジックを TDD で実装していきます。