@k2works/claude-code-booster 6.1.0 → 6.3.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 (622) hide show
  1. package/lib/assets/.claude/skills/practicing-getting-start-tdd/SKILL.md +3 -7
  2. package/lib/assets/docs/article/index.md +16 -41
  3. package/lib/assets/docs/assets/js/extra.js +5 -1
  4. package/lib/assets/docs/assets/js/mathjax.js +27 -0
  5. package/lib/assets/docs/index.md +1 -1
  6. package/lib/assets/mkdocs.yml +6 -7
  7. package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
  8. package/lib/assets/package.json +1 -1
  9. package/package.json +1 -1
  10. package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +0 -484
  11. package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +0 -528
  12. package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +0 -546
  13. package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +0 -309
  14. package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +0 -329
  15. package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +0 -507
  16. package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +0 -307
  17. package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +0 -300
  18. package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +0 -345
  19. package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +0 -312
  20. package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +0 -295
  21. package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +0 -331
  22. package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +0 -328
  23. package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +0 -374
  24. package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +0 -165
  25. package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +0 -187
  26. package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +0 -321
  27. package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +0 -296
  28. package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +0 -295
  29. package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +0 -283
  30. package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +0 -303
  31. package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +0 -346
  32. package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +0 -388
  33. package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +0 -282
  34. package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +0 -389
  35. package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +0 -393
  36. package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +0 -359
  37. package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +0 -361
  38. package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +0 -392
  39. package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +0 -538
  40. package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +0 -404
  41. package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +0 -408
  42. package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +0 -494
  43. package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +0 -575
  44. package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +0 -576
  45. package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +0 -484
  46. package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +0 -471
  47. package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +0 -334
  48. package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +0 -410
  49. package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +0 -459
  50. package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +0 -484
  51. package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +0 -748
  52. package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +0 -576
  53. package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +0 -527
  54. package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +0 -541
  55. package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +0 -241
  56. package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +0 -392
  57. package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +0 -383
  58. package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +0 -384
  59. package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +0 -204
  60. package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +0 -277
  61. package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +0 -303
  62. package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +0 -173
  63. package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +0 -177
  64. package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +0 -263
  65. package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +0 -278
  66. package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +0 -157
  67. package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +0 -185
  68. package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +0 -613
  69. package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +0 -738
  70. package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +0 -300
  71. package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +0 -429
  72. package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +0 -328
  73. package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +0 -475
  74. package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +0 -532
  75. package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +0 -296
  76. package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +0 -349
  77. package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +0 -404
  78. package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +0 -248
  79. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +0 -393
  80. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +0 -461
  81. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +0 -504
  82. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +0 -425
  83. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +0 -391
  84. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +0 -696
  85. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +0 -451
  86. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +0 -488
  87. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +0 -488
  88. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +0 -436
  89. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +0 -437
  90. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +0 -348
  91. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +0 -318
  92. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +0 -605
  93. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +0 -364
  94. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +0 -359
  95. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +0 -423
  96. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +0 -376
  97. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +0 -412
  98. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +0 -300
  99. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +0 -333
  100. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +0 -341
  101. package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +0 -274
  102. package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +0 -307
  103. package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +0 -313
  104. package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +0 -371
  105. package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +0 -266
  106. package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +0 -263
  107. package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +0 -292
  108. package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +0 -404
  109. package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +0 -328
  110. package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +0 -391
  111. package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +0 -296
  112. package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +0 -312
  113. package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +0 -335
  114. package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +0 -341
  115. package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +0 -388
  116. package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +0 -186
  117. package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +0 -228
  118. package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +0 -253
  119. package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +0 -372
  120. package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +0 -447
  121. package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +0 -334
  122. package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +0 -412
  123. package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +0 -478
  124. package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +0 -174
  125. package/lib/assets/docs/article/functional-desgin-ppp/index.md +0 -90
  126. package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +0 -459
  127. package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +0 -472
  128. package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +0 -434
  129. package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +0 -282
  130. package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +0 -256
  131. package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +0 -850
  132. package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +0 -393
  133. package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +0 -392
  134. package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +0 -348
  135. package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +0 -340
  136. package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +0 -365
  137. package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +0 -388
  138. package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +0 -370
  139. package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +0 -401
  140. package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +0 -309
  141. package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +0 -306
  142. package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +0 -313
  143. package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +0 -324
  144. package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +0 -320
  145. package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +0 -313
  146. package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +0 -345
  147. package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +0 -358
  148. package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +0 -243
  149. package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +0 -337
  150. package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +0 -357
  151. package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +0 -366
  152. package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +0 -373
  153. package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +0 -524
  154. package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +0 -566
  155. package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +0 -372
  156. package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +0 -336
  157. package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +0 -526
  158. package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +0 -450
  159. package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +0 -416
  160. package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +0 -388
  161. package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +0 -407
  162. package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +0 -485
  163. package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +0 -400
  164. package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +0 -351
  165. package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +0 -333
  166. package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +0 -739
  167. package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +0 -633
  168. package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +0 -521
  169. package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +0 -442
  170. package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +0 -697
  171. package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +0 -243
  172. package/lib/assets/docs/article/getting-start-docker-kubernetes/01-container-and-docker-basics.md +0 -341
  173. package/lib/assets/docs/article/getting-start-docker-kubernetes/02-container-deployment.md +0 -785
  174. package/lib/assets/docs/article/getting-start-docker-kubernetes/03-practical-container-build-deploy.md +0 -466
  175. package/lib/assets/docs/article/getting-start-docker-kubernetes/04-multi-container-application.md +0 -878
  176. package/lib/assets/docs/article/getting-start-docker-kubernetes/05-kubernetes-introduction.md +0 -585
  177. package/lib/assets/docs/article/getting-start-docker-kubernetes/06-kubernetes-deploy-cluster.md +0 -620
  178. package/lib/assets/docs/article/getting-start-docker-kubernetes/07-kubernetes-advanced.md +0 -621
  179. package/lib/assets/docs/article/getting-start-docker-kubernetes/08-kubernetes-packaging.md +0 -518
  180. package/lib/assets/docs/article/getting-start-docker-kubernetes/09-container-operations.md +0 -546
  181. package/lib/assets/docs/article/getting-start-docker-kubernetes/10-optimal-container-image.md +0 -640
  182. package/lib/assets/docs/article/getting-start-docker-kubernetes/11-continuous-delivery.md +0 -607
  183. package/lib/assets/docs/article/getting-start-docker-kubernetes/12-container-use-cases.md +0 -463
  184. package/lib/assets/docs/article/getting-start-docker-kubernetes/13-case-monolith-compose-vs-kustomize.md +0 -346
  185. package/lib/assets/docs/article/getting-start-docker-kubernetes/14-case-event-driven-kustomize-vs-helm.md +0 -324
  186. package/lib/assets/docs/article/getting-start-docker-kubernetes/15-case-escqrs-axon-kustomize-vs-helm.md +0 -297
  187. package/lib/assets/docs/article/getting-start-docker-kubernetes/16-case-escqrs-kafka-kustomize-vs-helm.md +0 -256
  188. package/lib/assets/docs/article/getting-start-docker-kubernetes/17-case-comparison-summary.md +0 -141
  189. package/lib/assets/docs/article/getting-start-docker-kubernetes/18-case-load-testing.md +0 -303
  190. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-a-dev-tools-setup.md +0 -274
  191. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-b-orchestration-environments.md +0 -530
  192. package/lib/assets/docs/article/getting-start-docker-kubernetes/appendix-c-tips.md +0 -519
  193. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-01-comic.png +0 -0
  194. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-02-comic.png +0 -0
  195. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-03-comic.png +0 -0
  196. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-04-comic.png +0 -0
  197. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-05-comic.png +0 -0
  198. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-06-comic.png +0 -0
  199. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-07-comic.png +0 -0
  200. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-08-comic.png +0 -0
  201. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-09-comic.png +0 -0
  202. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-10-comic.png +0 -0
  203. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-11-comic.png +0 -0
  204. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/chapter-12-comic.png +0 -0
  205. package/lib/assets/docs/article/getting-start-docker-kubernetes/images/overview-comic.png +0 -0
  206. package/lib/assets/docs/article/getting-start-docker-kubernetes/index.md +0 -82
  207. package/lib/assets/docs/article/getting-start-docker-kubernetes/outline.md +0 -220
  208. package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +0 -175
  209. package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +0 -171
  210. package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +0 -144
  211. package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +0 -97
  212. package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +0 -308
  213. package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +0 -250
  214. package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +0 -140
  215. package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +0 -139
  216. package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +0 -136
  217. package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +0 -123
  218. package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +0 -147
  219. package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +0 -170
  220. package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +0 -65
  221. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +0 -241
  222. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +0 -253
  223. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +0 -211
  224. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +0 -101
  225. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +0 -265
  226. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +0 -204
  227. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +0 -223
  228. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +0 -258
  229. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +0 -183
  230. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +0 -175
  231. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +0 -201
  232. package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +0 -220
  233. package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +0 -83
  234. package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +0 -96
  235. package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +0 -104
  236. package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +0 -118
  237. package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +0 -105
  238. package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +0 -97
  239. package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +0 -80
  240. package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +0 -119
  241. package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +0 -117
  242. package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +0 -113
  243. package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +0 -187
  244. package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +0 -151
  245. package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +0 -154
  246. package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +0 -35
  247. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +0 -211
  248. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +0 -255
  249. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +0 -227
  250. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +0 -188
  251. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +0 -276
  252. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +0 -199
  253. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +0 -170
  254. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +0 -184
  255. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +0 -231
  256. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +0 -198
  257. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +0 -221
  258. package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +0 -224
  259. package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +0 -71
  260. package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +0 -222
  261. package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +0 -311
  262. package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +0 -348
  263. package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +0 -121
  264. package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +0 -281
  265. package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +0 -242
  266. package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +0 -402
  267. package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +0 -430
  268. package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +0 -408
  269. package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +0 -234
  270. package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +0 -304
  271. package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +0 -419
  272. package/lib/assets/docs/article/getting-start-tdd/go/index.md +0 -83
  273. package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +0 -288
  274. package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +0 -346
  275. package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +0 -266
  276. package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +0 -191
  277. package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +0 -322
  278. package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +0 -318
  279. package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +0 -421
  280. package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +0 -399
  281. package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +0 -470
  282. package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +0 -443
  283. package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +0 -401
  284. package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +0 -640
  285. package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +0 -49
  286. package/lib/assets/docs/article/getting-start-tdd/index.md +0 -93
  287. package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +0 -384
  288. package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +0 -358
  289. package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +0 -454
  290. package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +0 -418
  291. package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +0 -339
  292. package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +0 -299
  293. package/lib/assets/docs/article/getting-start-tdd/integration/index.md +0 -69
  294. package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +0 -243
  295. package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +0 -270
  296. package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +0 -194
  297. package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +0 -124
  298. package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +0 -391
  299. package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +0 -281
  300. package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +0 -635
  301. package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +0 -402
  302. package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +0 -319
  303. package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +0 -197
  304. package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +0 -176
  305. package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +0 -214
  306. package/lib/assets/docs/article/getting-start-tdd/java/index.md +0 -61
  307. package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +0 -253
  308. package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +0 -271
  309. package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +0 -178
  310. package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +0 -121
  311. package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +0 -323
  312. package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +0 -244
  313. package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +0 -336
  314. package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +0 -331
  315. package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +0 -294
  316. package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +0 -208
  317. package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +0 -216
  318. package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +0 -304
  319. package/lib/assets/docs/article/getting-start-tdd/node/index.md +0 -56
  320. package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +0 -268
  321. package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +0 -209
  322. package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +0 -257
  323. package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +0 -150
  324. package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +0 -419
  325. package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +0 -330
  326. package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +0 -380
  327. package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +0 -461
  328. package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +0 -468
  329. package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +0 -190
  330. package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +0 -274
  331. package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +0 -316
  332. package/lib/assets/docs/article/getting-start-tdd/php/index.md +0 -84
  333. package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +0 -210
  334. package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +0 -256
  335. package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +0 -208
  336. package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +0 -96
  337. package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +0 -283
  338. package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +0 -199
  339. package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +0 -217
  340. package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +0 -181
  341. package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +0 -139
  342. package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +0 -131
  343. package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +0 -125
  344. package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +0 -135
  345. package/lib/assets/docs/article/getting-start-tdd/python/index.md +0 -55
  346. package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +0 -240
  347. package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +0 -247
  348. package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +0 -237
  349. package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +0 -121
  350. package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +0 -296
  351. package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +0 -257
  352. package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +0 -288
  353. package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +0 -338
  354. package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +0 -205
  355. package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +0 -184
  356. package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +0 -246
  357. package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +0 -407
  358. package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +0 -83
  359. package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +0 -220
  360. package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +0 -273
  361. package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +0 -242
  362. package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +0 -101
  363. package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +0 -221
  364. package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +0 -173
  365. package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +0 -151
  366. package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +0 -154
  367. package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +0 -119
  368. package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +0 -103
  369. package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +0 -114
  370. package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +0 -121
  371. package/lib/assets/docs/article/getting-start-tdd/rust/index.md +0 -83
  372. package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +0 -120
  373. package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +0 -116
  374. package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +0 -108
  375. package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +0 -132
  376. package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +0 -205
  377. package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +0 -195
  378. package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +0 -148
  379. package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +0 -115
  380. package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +0 -84
  381. package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +0 -113
  382. package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +0 -103
  383. package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +0 -101
  384. package/lib/assets/docs/article/getting-start-tdd/scala/index.md +0 -65
  385. package/lib/assets/docs/article/grokking-concurrency/all/index.md +0 -403
  386. package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +0 -563
  387. package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +0 -478
  388. package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +0 -529
  389. package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +0 -429
  390. package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +0 -519
  391. package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +0 -444
  392. package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +0 -474
  393. package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +0 -386
  394. package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +0 -106
  395. package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +0 -107
  396. package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +0 -100
  397. package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +0 -121
  398. package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +0 -122
  399. package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +0 -117
  400. package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +0 -88
  401. package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +0 -99
  402. package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +0 -119
  403. package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +0 -91
  404. package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +0 -96
  405. package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +0 -122
  406. package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +0 -100
  407. package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +0 -111
  408. package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +0 -98
  409. package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +0 -60
  410. package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +0 -83
  411. package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +0 -91
  412. package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +0 -55
  413. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +0 -79
  414. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +0 -102
  415. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +0 -93
  416. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +0 -109
  417. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +0 -103
  418. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +0 -92
  419. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +0 -120
  420. package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +0 -106
  421. package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +0 -238
  422. package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +0 -95
  423. package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +0 -95
  424. package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +0 -90
  425. package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +0 -105
  426. package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +0 -98
  427. package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +0 -94
  428. package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +0 -110
  429. package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +0 -117
  430. package/lib/assets/docs/article/grokking-concurrency/index.md +0 -66
  431. package/lib/assets/docs/article/grokking-concurrency/java/index.md +0 -92
  432. package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +0 -307
  433. package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +0 -333
  434. package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +0 -220
  435. package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +0 -212
  436. package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +0 -111
  437. package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +0 -68
  438. package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +0 -100
  439. package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +0 -100
  440. package/lib/assets/docs/article/grokking-concurrency/python/index.md +0 -303
  441. package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +0 -238
  442. package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +0 -417
  443. package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +0 -226
  444. package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +0 -298
  445. package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +0 -314
  446. package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +0 -296
  447. package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +0 -313
  448. package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +0 -359
  449. package/lib/assets/docs/article/grokking-concurrency/rust/index.md +0 -260
  450. package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +0 -107
  451. package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +0 -119
  452. package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +0 -125
  453. package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +0 -174
  454. package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +0 -157
  455. package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +0 -93
  456. package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +0 -132
  457. package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +0 -154
  458. package/lib/assets/docs/article/grokking-concurrency/scala/index.md +0 -59
  459. package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +0 -77
  460. package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +0 -111
  461. package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +0 -92
  462. package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +0 -109
  463. package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +0 -118
  464. package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +0 -82
  465. package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +0 -130
  466. package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +0 -128
  467. package/lib/assets/docs/article/grokkingfp/all/index.md +0 -368
  468. package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +0 -539
  469. package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +0 -932
  470. package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +0 -1137
  471. package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +0 -1113
  472. package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +0 -1035
  473. package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +0 -794
  474. package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +0 -880
  475. package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +0 -981
  476. package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +0 -935
  477. package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +0 -879
  478. package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +0 -724
  479. package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +0 -635
  480. package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +0 -721
  481. package/lib/assets/docs/article/grokkingfp/clojure/index.md +0 -276
  482. package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +0 -676
  483. package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +0 -652
  484. package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +0 -629
  485. package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +0 -706
  486. package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +0 -760
  487. package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +0 -730
  488. package/lib/assets/docs/article/grokkingfp/csharp/index.md +0 -246
  489. package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +0 -820
  490. package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +0 -980
  491. package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +0 -990
  492. package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +0 -958
  493. package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +0 -956
  494. package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +0 -748
  495. package/lib/assets/docs/article/grokkingfp/elixir/index.md +0 -203
  496. package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +0 -721
  497. package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +0 -847
  498. package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +0 -994
  499. package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +0 -983
  500. package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +0 -1295
  501. package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +0 -1058
  502. package/lib/assets/docs/article/grokkingfp/fsharp/index.md +0 -210
  503. package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +0 -723
  504. package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +0 -970
  505. package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +0 -981
  506. package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +0 -841
  507. package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +0 -920
  508. package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +0 -931
  509. package/lib/assets/docs/article/grokkingfp/haskell/index.md +0 -234
  510. package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +0 -600
  511. package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +0 -875
  512. package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +0 -924
  513. package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +0 -887
  514. package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +0 -854
  515. package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +0 -853
  516. package/lib/assets/docs/article/grokkingfp/index.md +0 -143
  517. package/lib/assets/docs/article/grokkingfp/java/index.md +0 -211
  518. package/lib/assets/docs/article/grokkingfp/java/part-1.md +0 -657
  519. package/lib/assets/docs/article/grokkingfp/java/part-2.md +0 -684
  520. package/lib/assets/docs/article/grokkingfp/java/part-3.md +0 -681
  521. package/lib/assets/docs/article/grokkingfp/java/part-4.md +0 -780
  522. package/lib/assets/docs/article/grokkingfp/java/part-5.md +0 -968
  523. package/lib/assets/docs/article/grokkingfp/java/part-6.md +0 -1337
  524. package/lib/assets/docs/article/grokkingfp/python/index.md +0 -258
  525. package/lib/assets/docs/article/grokkingfp/python/part-1.md +0 -452
  526. package/lib/assets/docs/article/grokkingfp/python/part-2.md +0 -967
  527. package/lib/assets/docs/article/grokkingfp/python/part-3.md +0 -1013
  528. package/lib/assets/docs/article/grokkingfp/python/part-4.md +0 -774
  529. package/lib/assets/docs/article/grokkingfp/python/part-5.md +0 -756
  530. package/lib/assets/docs/article/grokkingfp/python/part-6.md +0 -870
  531. package/lib/assets/docs/article/grokkingfp/ruby/index.md +0 -330
  532. package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +0 -764
  533. package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +0 -947
  534. package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +0 -955
  535. package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +0 -930
  536. package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +0 -917
  537. package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +0 -1421
  538. package/lib/assets/docs/article/grokkingfp/rust/index.md +0 -242
  539. package/lib/assets/docs/article/grokkingfp/rust/part-1.md +0 -643
  540. package/lib/assets/docs/article/grokkingfp/rust/part-2.md +0 -1069
  541. package/lib/assets/docs/article/grokkingfp/rust/part-3.md +0 -1003
  542. package/lib/assets/docs/article/grokkingfp/rust/part-4.md +0 -582
  543. package/lib/assets/docs/article/grokkingfp/rust/part-5.md +0 -714
  544. package/lib/assets/docs/article/grokkingfp/rust/part-6.md +0 -517
  545. package/lib/assets/docs/article/grokkingfp/scala/index.md +0 -171
  546. package/lib/assets/docs/article/grokkingfp/scala/part-1.md +0 -552
  547. package/lib/assets/docs/article/grokkingfp/scala/part-2.md +0 -955
  548. package/lib/assets/docs/article/grokkingfp/scala/part-3.md +0 -928
  549. package/lib/assets/docs/article/grokkingfp/scala/part-4.md +0 -751
  550. package/lib/assets/docs/article/grokkingfp/scala/part-5.md +0 -731
  551. package/lib/assets/docs/article/grokkingfp/scala/part-6.md +0 -876
  552. package/lib/assets/docs/article/grokkingfp/typescript/index.md +0 -273
  553. package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +0 -570
  554. package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +0 -1138
  555. package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +0 -851
  556. package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +0 -1096
  557. package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +0 -726
  558. package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +0 -991
  559. package/lib/assets/docs/article/practical-database-design/index.md +0 -121
  560. package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +0 -297
  561. package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +0 -527
  562. package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +0 -566
  563. package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +0 -933
  564. package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +0 -1636
  565. package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +0 -2725
  566. package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +0 -2091
  567. package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +0 -2114
  568. package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +0 -2040
  569. package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +0 -1396
  570. package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +0 -1686
  571. package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +0 -1426
  572. package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +0 -1443
  573. package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +0 -676
  574. package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +0 -1634
  575. package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +0 -1924
  576. package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +0 -1717
  577. package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +0 -2104
  578. package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +0 -1132
  579. package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +0 -1040
  580. package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +0 -1391
  581. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +0 -1000
  582. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +0 -1309
  583. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +0 -1175
  584. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +0 -1593
  585. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +0 -1192
  586. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +0 -1025
  587. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +0 -1762
  588. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +0 -1456
  589. package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +0 -1887
  590. package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +0 -974
  591. package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +0 -2078
  592. package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +0 -2448
  593. package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +0 -3670
  594. package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +0 -2925
  595. package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +0 -3114
  596. package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +0 -2706
  597. package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +0 -2552
  598. package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +0 -2188
  599. package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +0 -1200
  600. package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +0 -2109
  601. package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +0 -1041
  602. package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +0 -1618
  603. package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +0 -1462
  604. package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +0 -1301
  605. package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +0 -1479
  606. package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +0 -1707
  607. package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +0 -2343
  608. package/lib/assets/docs/article/practical-database-design/study/study2-1.md +0 -1702
  609. package/lib/assets/docs/article/practical-database-design/study/study2-2.md +0 -1356
  610. package/lib/assets/docs/article/practical-database-design/study/study2-3.md +0 -2053
  611. package/lib/assets/docs/article/practical-database-design/study/study2-4.md +0 -2238
  612. package/lib/assets/docs/article/practical-database-design/study/study2-5.md +0 -2427
  613. package/lib/assets/docs/article/practical-database-design/study/study3-1.md +0 -2214
  614. package/lib/assets/docs/article/practical-database-design/study/study3-2.md +0 -2230
  615. package/lib/assets/docs/article/practical-database-design/study/study3-3.md +0 -2262
  616. package/lib/assets/docs/article/practical-database-design/study/study3-4.md +0 -2115
  617. package/lib/assets/docs/article/practical-database-design/study/study3-5.md +0 -2516
  618. package/lib/assets/docs/article/practical-database-design/study/study4-1.md +0 -2596
  619. package/lib/assets/docs/article/practical-database-design/study/study4-2.md +0 -2084
  620. package/lib/assets/docs/article/practical-database-design/study/study4-3.md +0 -1814
  621. package/lib/assets/docs/article/practical-database-design/study/study4-4.md +0 -1904
  622. package/lib/assets/docs/article/practical-database-design/study/study4-5.md +0 -2887
@@ -1,2725 +0,0 @@
1
- ---
2
- type: Article
3
- title: "第6章:受注・出荷・売上の設計"
4
- description: "販売管理システムの中核となるトランザクションデータを設計していきます。"
5
- tags: [article, practical-database-design, part2]
6
- status: stable
7
- generated: { by: human:kakimomokuri, at: 2026-03-30T08:34:28Z }
8
- ---
9
-
10
- # 第6章:受注・出荷・売上の設計
11
-
12
- 販売管理システムの中核となるトランザクションデータを設計していきます。本章では、受注から出荷、売上計上までの一連の業務フローに対応したデータベース設計と実装を行います。
13
-
14
- ## 販売業務の全体フロー
15
-
16
- 販売業務は「見積 → 受注 → 出荷 → 売上」という一連のフローで構成されます。
17
-
18
- ```plantuml
19
- @startuml
20
-
21
- title 販売業務フロー
22
-
23
- |営業部門|
24
- start
25
- :見積作成;
26
- note right
27
- 顧客からの問い合わせに
28
- 対して見積書を作成
29
- end note
30
-
31
- :見積提出;
32
-
33
- if (受注確定?) then (yes)
34
- :受注登録;
35
- else (no)
36
- :失注処理;
37
- stop
38
- endif
39
-
40
- |倉庫部門|
41
- :出荷指示;
42
- note right
43
- 受注データを元に
44
- 出荷指示を作成
45
- end note
46
-
47
- :ピッキング;
48
- :検品・梱包;
49
- :出荷;
50
-
51
- |営業部門|
52
- :売上計上;
53
- note right
54
- 出荷完了時点で
55
- 売上を計上
56
- end note
57
-
58
- :納品書発行;
59
- stop
60
-
61
- @enduml
62
- ```
63
-
64
- ### トランザクションデータの特徴
65
-
66
- トランザクションデータには以下の共通した特徴があります。
67
-
68
- | 特徴 | 説明 |
69
- |-----|------|
70
- | **ヘッダ・明細構造** | 1つの取引に複数の商品が含まれるため、ヘッダ(親)と明細(子)の構造を持つ |
71
- | **ステータス管理** | 業務の進捗状態を管理するステータスを持つ |
72
- | **連番管理** | 伝票番号などの連番を自動採番する |
73
- | **日時管理** | 作成日時、更新日時など監査証跡を残す |
74
- | **マスタ参照** | 顧客・商品・社員などのマスタを参照する |
75
-
76
- ---
77
-
78
- ## 6.1 受注業務の DB 設計
79
-
80
- ### 受注業務フローの理解
81
-
82
- 受注業務は、顧客からの注文を受け付け、システムに登録する業務です。
83
-
84
- ```plantuml
85
- @startuml
86
-
87
- title 受注業務フロー
88
-
89
- |営業担当|
90
- start
91
- :見積書を確認;
92
-
93
- if (見積あり?) then (yes)
94
- :見積から受注へ変換;
95
- else (no)
96
- :新規受注入力;
97
- endif
98
-
99
- :受注データ登録;
100
- note right
101
- 顧客情報
102
- 商品・数量・単価
103
- 納期
104
- 配送先
105
- end note
106
-
107
- :受注明細登録;
108
-
109
- :受注確認書発行;
110
-
111
- |倉庫担当|
112
- :在庫引当;
113
- note right
114
- 受注数量分の
115
- 在庫を確保
116
- end note
117
-
118
- if (在庫あり?) then (yes)
119
- :引当完了;
120
- else (no)
121
- :入荷待ち;
122
- endif
123
-
124
- stop
125
-
126
- @enduml
127
- ```
128
-
129
- ### 受注データ・受注明細データの構造
130
-
131
- #### 受注データの ER 図
132
-
133
- ```plantuml
134
- @startuml
135
-
136
- title 受注関連テーブル
137
-
138
- entity 見積データ {
139
- ID <<PK>>
140
- --
141
- 見積番号 <<UK>>
142
- 見積日
143
- 見積有効期限
144
- 顧客コード <<FK>>
145
- 顧客枝番 <<FK>>
146
- 担当者コード
147
- 件名
148
- 見積金額
149
- 消費税額
150
- 見積合計
151
- ステータス
152
- 備考
153
- 作成日時
154
- 更新日時
155
- }
156
-
157
- entity 見積明細 {
158
- ID <<PK>>
159
- --
160
- 見積ID <<FK>>
161
- 行番号
162
- 商品コード <<FK>>
163
- 商品名
164
- 数量
165
- 単位
166
- 単価
167
- 金額
168
- 税区分
169
- 消費税率
170
- 消費税額
171
- 備考
172
- }
173
-
174
- entity 受注データ {
175
- ID <<PK>>
176
- --
177
- 受注番号 <<UK>>
178
- 受注日
179
- 顧客コード <<FK>>
180
- 顧客枝番 <<FK>>
181
- 出荷先番号 <<FK>>
182
- 担当者コード
183
- 希望納期
184
- 出荷予定日
185
- 受注金額
186
- 消費税額
187
- 受注合計
188
- ステータス
189
- 見積ID <<FK>>
190
- 顧客注文番号
191
- 備考
192
- 作成日時
193
- 更新日時
194
- }
195
-
196
- entity 受注明細 {
197
- ID <<PK>>
198
- --
199
- 受注ID <<FK>>
200
- 行番号
201
- 商品コード <<FK>>
202
- 商品名
203
- 受注数量
204
- 引当数量
205
- 出荷数量
206
- 残数量
207
- 単位
208
- 単価
209
- 金額
210
- 税区分
211
- 消費税率
212
- 消費税額
213
- 倉庫コード
214
- 希望納期
215
- 備考
216
- }
217
-
218
- 見積データ ||--o{ 見積明細
219
- 見積データ ||--o| 受注データ : "受注化"
220
- 受注データ ||--o{ 受注明細
221
-
222
- @enduml
223
- ```
224
-
225
- ### 受注ステータスの定義
226
-
227
- ```plantuml
228
- @startuml
229
-
230
- title 受注ステータス遷移図
231
-
232
- [*] --> 受付済
233
-
234
- 受付済 --> 引当済 : 在庫引当完了
235
- 受付済 --> 出荷指示済 : 出荷指示作成
236
-
237
- 引当済 --> 出荷指示済 : 出荷指示作成
238
-
239
- 出荷指示済 --> 出荷済 : 出荷完了
240
-
241
- 出荷済 --> [*]
242
-
243
- 受付済 --> キャンセル
244
- 引当済 --> キャンセル
245
- 出荷指示済 --> キャンセル
246
-
247
- キャンセル --> [*]
248
-
249
- @enduml
250
- ```
251
-
252
- | ステータス | 説明 |
253
- |-----------|------|
254
- | **受付済** | 受注登録が完了した状態 |
255
- | **引当済** | 在庫引当が完了した状態 |
256
- | **出荷指示済** | 出荷指示が作成された状態 |
257
- | **出荷済** | 出荷が完了した状態 |
258
- | **キャンセル** | 受注がキャンセルされた状態 |
259
-
260
- ### マイグレーション:受注関連テーブルの作成
261
-
262
- <details>
263
- <summary>V007__create_quotation_order_tables.sql</summary>
264
-
265
- ```sql
266
- -- src/main/resources/db/migration/V007__create_quotation_order_tables.sql
267
-
268
- -- 見積ステータス
269
- CREATE TYPE 見積ステータス AS ENUM ('商談中', '受注確定', '失注', '期限切れ');
270
-
271
- -- 受注ステータス
272
- CREATE TYPE 受注ステータス AS ENUM ('受付済', '引当済', '出荷指示済', '出荷済', 'キャンセル');
273
-
274
- -- 見積データ(ヘッダ)
275
- CREATE TABLE "見積データ" (
276
- "ID" SERIAL PRIMARY KEY,
277
- "見積番号" VARCHAR(20) UNIQUE NOT NULL,
278
- "見積日" DATE NOT NULL,
279
- "見積有効期限" DATE,
280
- "顧客コード" VARCHAR(20) NOT NULL,
281
- "顧客枝番" VARCHAR(10) DEFAULT '00',
282
- "担当者コード" VARCHAR(20),
283
- "件名" VARCHAR(200),
284
- "見積金額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
285
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
286
- "見積合計" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
287
- "ステータス" 見積ステータス DEFAULT '商談中' NOT NULL,
288
- "備考" TEXT,
289
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
290
- "作成者" VARCHAR(50),
291
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
292
- "更新者" VARCHAR(50),
293
- CONSTRAINT "fk_見積データ_顧客"
294
- FOREIGN KEY ("顧客コード", "顧客枝番") REFERENCES "顧客マスタ"("顧客コード", "顧客枝番")
295
- );
296
-
297
- -- 見積明細
298
- CREATE TABLE "見積明細" (
299
- "ID" SERIAL PRIMARY KEY,
300
- "見積ID" INTEGER NOT NULL,
301
- "行番号" INTEGER NOT NULL,
302
- "商品コード" VARCHAR(20) NOT NULL,
303
- "商品名" VARCHAR(100) NOT NULL,
304
- "数量" DECIMAL(15, 2) NOT NULL,
305
- "単位" VARCHAR(10),
306
- "単価" DECIMAL(15, 2) NOT NULL,
307
- "金額" DECIMAL(15, 2) NOT NULL,
308
- "税区分" 税区分 DEFAULT '外税' NOT NULL,
309
- "消費税率" DECIMAL(5, 2) DEFAULT 10.00 NOT NULL,
310
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
311
- "備考" TEXT,
312
- CONSTRAINT "fk_見積明細_見積"
313
- FOREIGN KEY ("見積ID") REFERENCES "見積データ"("ID") ON DELETE CASCADE,
314
- CONSTRAINT "fk_見積明細_商品"
315
- FOREIGN KEY ("商品コード") REFERENCES "商品マスタ"("商品コード"),
316
- CONSTRAINT "uq_見積明細_行番号" UNIQUE ("見積ID", "行番号")
317
- );
318
-
319
- -- 受注データ(ヘッダ)
320
- CREATE TABLE "受注データ" (
321
- "ID" SERIAL PRIMARY KEY,
322
- "受注番号" VARCHAR(20) UNIQUE NOT NULL,
323
- "受注日" DATE NOT NULL,
324
- "顧客コード" VARCHAR(20) NOT NULL,
325
- "顧客枝番" VARCHAR(10) DEFAULT '00',
326
- "出荷先番号" VARCHAR(10),
327
- "担当者コード" VARCHAR(20),
328
- "希望納期" DATE,
329
- "出荷予定日" DATE,
330
- "受注金額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
331
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
332
- "受注合計" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
333
- "ステータス" 受注ステータス DEFAULT '受付済' NOT NULL,
334
- "見積ID" INTEGER,
335
- "顧客注文番号" VARCHAR(50),
336
- "備考" TEXT,
337
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
338
- "作成者" VARCHAR(50),
339
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
340
- "更新者" VARCHAR(50),
341
- CONSTRAINT "fk_受注データ_顧客"
342
- FOREIGN KEY ("顧客コード", "顧客枝番") REFERENCES "顧客マスタ"("顧客コード", "顧客枝番"),
343
- CONSTRAINT "fk_受注データ_出荷先"
344
- FOREIGN KEY ("顧客コード", "顧客枝番", "出荷先番号") REFERENCES "出荷先マスタ"("取引先コード", "顧客枝番", "出荷先番号"),
345
- CONSTRAINT "fk_受注データ_見積"
346
- FOREIGN KEY ("見積ID") REFERENCES "見積データ"("ID")
347
- );
348
-
349
- -- 受注明細
350
- CREATE TABLE "受注明細" (
351
- "ID" SERIAL PRIMARY KEY,
352
- "受注ID" INTEGER NOT NULL,
353
- "行番号" INTEGER NOT NULL,
354
- "商品コード" VARCHAR(20) NOT NULL,
355
- "商品名" VARCHAR(100) NOT NULL,
356
- "受注数量" DECIMAL(15, 2) NOT NULL,
357
- "引当数量" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
358
- "出荷数量" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
359
- "残数量" DECIMAL(15, 2) NOT NULL,
360
- "単位" VARCHAR(10),
361
- "単価" DECIMAL(15, 2) NOT NULL,
362
- "金額" DECIMAL(15, 2) NOT NULL,
363
- "税区分" 税区分 DEFAULT '外税' NOT NULL,
364
- "消費税率" DECIMAL(5, 2) DEFAULT 10.00 NOT NULL,
365
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
366
- "倉庫コード" VARCHAR(20),
367
- "希望納期" DATE,
368
- "備考" TEXT,
369
- CONSTRAINT "fk_受注明細_受注"
370
- FOREIGN KEY ("受注ID") REFERENCES "受注データ"("ID") ON DELETE CASCADE,
371
- CONSTRAINT "fk_受注明細_商品"
372
- FOREIGN KEY ("商品コード") REFERENCES "商品マスタ"("商品コード"),
373
- CONSTRAINT "uq_受注明細_行番号" UNIQUE ("受注ID", "行番号")
374
- );
375
-
376
- -- インデックス
377
- CREATE INDEX "idx_見積データ_顧客コード" ON "見積データ"("顧客コード");
378
- CREATE INDEX "idx_見積データ_見積日" ON "見積データ"("見積日");
379
- CREATE INDEX "idx_見積データ_ステータス" ON "見積データ"("ステータス");
380
- CREATE INDEX "idx_受注データ_顧客コード" ON "受注データ"("顧客コード");
381
- CREATE INDEX "idx_受注データ_受注日" ON "受注データ"("受注日");
382
- CREATE INDEX "idx_受注データ_ステータス" ON "受注データ"("ステータス");
383
- CREATE INDEX "idx_受注データ_希望納期" ON "受注データ"("希望納期");
384
- ```
385
-
386
- </details>
387
-
388
- ### 受注ステータス Enum
389
-
390
- <details>
391
- <summary>OrderStatus.java</summary>
392
-
393
- ```java
394
- // src/main/java/com/example/sms/domain/model/sales/OrderStatus.java
395
- package com.example.sms.domain.model.sales;
396
-
397
- import lombok.Getter;
398
- import lombok.RequiredArgsConstructor;
399
-
400
- @Getter
401
- @RequiredArgsConstructor
402
- public enum OrderStatus {
403
- RECEIVED("受付済"),
404
- ALLOCATED("引当済"),
405
- SHIPMENT_INSTRUCTED("出荷指示済"),
406
- SHIPPED("出荷済"),
407
- CANCELLED("キャンセル");
408
-
409
- private final String displayName;
410
-
411
- public static OrderStatus fromDisplayName(String displayName) {
412
- for (OrderStatus status : values()) {
413
- if (status.displayName.equals(displayName)) {
414
- return status;
415
- }
416
- }
417
- throw new IllegalArgumentException("Unknown order status: " + displayName);
418
- }
419
- }
420
- ```
421
-
422
- </details>
423
-
424
- ### 受注エンティティ
425
-
426
- <details>
427
- <summary>SalesOrder.java</summary>
428
-
429
- ```java
430
- // src/main/java/com/example/sms/domain/model/sales/SalesOrder.java
431
- package com.example.sms.domain.model.sales;
432
-
433
- import lombok.AllArgsConstructor;
434
- import lombok.Builder;
435
- import lombok.Data;
436
- import lombok.NoArgsConstructor;
437
-
438
- import java.math.BigDecimal;
439
- import java.time.LocalDate;
440
- import java.time.LocalDateTime;
441
- import java.util.ArrayList;
442
- import java.util.List;
443
-
444
- @Data
445
- @Builder
446
- @NoArgsConstructor
447
- @AllArgsConstructor
448
- @SuppressWarnings("PMD.RedundantFieldInitializer")
449
- public class SalesOrder {
450
- private Integer id;
451
- private String orderNumber;
452
- private LocalDate orderDate;
453
- private String customerCode;
454
- private String customerBranchNumber;
455
- private String shippingDestinationNumber;
456
- private String representativeCode;
457
- private LocalDate requestedDeliveryDate;
458
- private LocalDate scheduledShippingDate;
459
- @Builder.Default
460
- private BigDecimal orderAmount = BigDecimal.ZERO;
461
- @Builder.Default
462
- private BigDecimal taxAmount = BigDecimal.ZERO;
463
- @Builder.Default
464
- private BigDecimal totalAmount = BigDecimal.ZERO;
465
- @Builder.Default
466
- private OrderStatus status = OrderStatus.RECEIVED;
467
- private Integer quotationId;
468
- private String customerOrderNumber;
469
- private String remarks;
470
- private LocalDateTime createdAt;
471
- private String createdBy;
472
- private LocalDateTime updatedAt;
473
- private String updatedBy;
474
-
475
- @Builder.Default
476
- private List<SalesOrderDetail> details = new ArrayList<>();
477
- }
478
- ```
479
-
480
- </details>
481
-
482
- <details>
483
- <summary>SalesOrderDetail.java</summary>
484
-
485
- ```java
486
- // src/main/java/com/example/sms/domain/model/sales/SalesOrderDetail.java
487
- package com.example.sms.domain.model.sales;
488
-
489
- import com.example.sms.domain.model.product.TaxCategory;
490
- import lombok.AllArgsConstructor;
491
- import lombok.Builder;
492
- import lombok.Data;
493
- import lombok.NoArgsConstructor;
494
-
495
- import java.math.BigDecimal;
496
- import java.time.LocalDate;
497
-
498
- @Data
499
- @Builder
500
- @NoArgsConstructor
501
- @AllArgsConstructor
502
- public class SalesOrderDetail {
503
- private Integer id;
504
- private Integer orderId;
505
- private Integer lineNumber;
506
- private String productCode;
507
- private String productName;
508
- private BigDecimal orderQuantity;
509
- private BigDecimal allocatedQuantity;
510
- private BigDecimal shippedQuantity;
511
- private BigDecimal remainingQuantity;
512
- private String unit;
513
- private BigDecimal unitPrice;
514
- private BigDecimal amount;
515
- private TaxCategory taxCategory;
516
- private BigDecimal taxRate;
517
- private BigDecimal taxAmount;
518
- private String warehouseCode;
519
- private LocalDate requestedDeliveryDate;
520
- private String remarks;
521
- }
522
- ```
523
-
524
- </details>
525
-
526
- ### TypeHandler の実装
527
-
528
- 日本語の ENUM 値を Java の Enum に変換するための TypeHandler を実装します。
529
-
530
- <details>
531
- <summary>OrderStatusTypeHandler.java</summary>
532
-
533
- ```java
534
- // src/main/java/com/example/sms/infrastructure/out/persistence/typehandler/OrderStatusTypeHandler.java
535
- package com.example.sms.infrastructure.out.persistence.typehandler;
536
-
537
- import com.example.sms.domain.model.sales.OrderStatus;
538
- import org.apache.ibatis.type.BaseTypeHandler;
539
- import org.apache.ibatis.type.JdbcType;
540
- import org.apache.ibatis.type.MappedTypes;
541
-
542
- import java.sql.*;
543
-
544
- @MappedTypes(OrderStatus.class)
545
- public class OrderStatusTypeHandler extends BaseTypeHandler<OrderStatus> {
546
-
547
- @Override
548
- public void setNonNullParameter(PreparedStatement ps, int i,
549
- OrderStatus parameter, JdbcType jdbcType) throws SQLException {
550
- ps.setObject(i, parameter.getDisplayName(), Types.OTHER);
551
- }
552
-
553
- @Override
554
- public OrderStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
555
- String value = rs.getString(columnName);
556
- return value == null ? null : OrderStatus.fromDisplayName(value);
557
- }
558
-
559
- @Override
560
- public OrderStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
561
- String value = rs.getString(columnIndex);
562
- return value == null ? null : OrderStatus.fromDisplayName(value);
563
- }
564
-
565
- @Override
566
- public OrderStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
567
- String value = cs.getString(columnIndex);
568
- return value == null ? null : OrderStatus.fromDisplayName(value);
569
- }
570
- }
571
- ```
572
-
573
- </details>
574
-
575
- ### TDD:受注の登録と取得
576
-
577
- #### Red: 失敗するテストを書く
578
-
579
- <details>
580
- <summary>SalesOrderRepositoryTest.java</summary>
581
-
582
- ```java
583
- // src/test/java/com/example/sms/infrastructure/persistence/repository/SalesOrderRepositoryTest.java
584
- package com.example.sms.infrastructure.persistence.repository;
585
-
586
- import com.example.sms.application.port.out.SalesOrderRepository;
587
- import com.example.sms.domain.model.product.TaxCategory;
588
- import com.example.sms.domain.model.sales.*;
589
- import com.example.sms.testsetup.BaseIntegrationTest;
590
- import org.junit.jupiter.api.*;
591
- import org.springframework.beans.factory.annotation.Autowired;
592
-
593
- import java.math.BigDecimal;
594
- import java.time.LocalDate;
595
-
596
- import static org.assertj.core.api.Assertions.*;
597
-
598
- @DisplayName("受注リポジトリ")
599
- class SalesOrderRepositoryTest extends BaseIntegrationTest {
600
-
601
- @Autowired
602
- private SalesOrderRepository salesOrderRepository;
603
-
604
- @BeforeEach
605
- void setUp() {
606
- salesOrderRepository.deleteAll();
607
- }
608
-
609
- @Nested
610
- @DisplayName("受注の登録")
611
- class OrderRegistration {
612
-
613
- @Test
614
- @DisplayName("受注を登録できる")
615
- void canRegisterOrder() {
616
- // Arrange
617
- var order = SalesOrder.builder()
618
- .orderNumber("SO-2025-0001")
619
- .orderDate(LocalDate.of(2025, 1, 20))
620
- .customerCode("CUST-001")
621
- .requestedDeliveryDate(LocalDate.of(2025, 1, 30))
622
- .subtotal(new BigDecimal("50000"))
623
- .taxAmount(new BigDecimal("5000"))
624
- .totalAmount(new BigDecimal("55000"))
625
- .status(OrderStatus.RECEIVED)
626
- .build();
627
-
628
- // Act
629
- salesOrderRepository.save(order);
630
-
631
- // Assert
632
- var result = salesOrderRepository.findByOrderNumber("SO-2025-0001");
633
- assertThat(result).isPresent();
634
- assertThat(result.get().getOrderNumber()).isEqualTo("SO-2025-0001");
635
- assertThat(result.get().getCustomerCode()).isEqualTo("CUST-001");
636
- assertThat(result.get().getTotalAmount()).isEqualByComparingTo(new BigDecimal("55000"));
637
- assertThat(result.get().getStatus()).isEqualTo(OrderStatus.RECEIVED);
638
- }
639
-
640
- @Test
641
- @DisplayName("受注明細を登録できる")
642
- void canRegisterOrderWithDetails() {
643
- // Arrange
644
- var order = SalesOrder.builder()
645
- .orderNumber("SO-2025-0002")
646
- .orderDate(LocalDate.of(2025, 1, 20))
647
- .customerCode("CUST-001")
648
- .subtotal(new BigDecimal("10000"))
649
- .taxAmount(new BigDecimal("1000"))
650
- .totalAmount(new BigDecimal("11000"))
651
- .status(OrderStatus.RECEIVED)
652
- .build();
653
- salesOrderRepository.save(order);
654
-
655
- var detail = SalesOrderDetail.builder()
656
- .orderId(order.getId())
657
- .lineNumber(1)
658
- .productCode("PROD-001")
659
- .productName("テスト商品A")
660
- .orderQuantity(new BigDecimal("10"))
661
- .allocatedQuantity(BigDecimal.ZERO)
662
- .shippedQuantity(BigDecimal.ZERO)
663
- .remainingQuantity(new BigDecimal("10"))
664
- .unit("個")
665
- .unitPrice(new BigDecimal("1000"))
666
- .amount(new BigDecimal("10000"))
667
- .taxCategory(TaxCategory.EXCLUSIVE)
668
- .taxRate(new BigDecimal("10.00"))
669
- .taxAmount(new BigDecimal("1000"))
670
- .warehouseCode("WH-001")
671
- .build();
672
-
673
- // Act
674
- salesOrderRepository.saveDetail(detail);
675
-
676
- // Assert
677
- var result = salesOrderRepository.findByIdWithDetails(order.getId());
678
- assertThat(result).isPresent();
679
- assertThat(result.get().getDetails()).hasSize(1);
680
- assertThat(result.get().getDetails().get(0).getProductCode()).isEqualTo("PROD-001");
681
- assertThat(result.get().getDetails().get(0).getOrderQuantity())
682
- .isEqualByComparingTo(new BigDecimal("10"));
683
- }
684
-
685
- @Test
686
- @DisplayName("受注明細の数量を更新できる")
687
- void canUpdateDetailQuantities() {
688
- // Arrange
689
- var order = SalesOrder.builder()
690
- .orderNumber("SO-2025-0003")
691
- .orderDate(LocalDate.of(2025, 1, 20))
692
- .customerCode("CUST-001")
693
- .subtotal(new BigDecimal("10000"))
694
- .taxAmount(new BigDecimal("1000"))
695
- .totalAmount(new BigDecimal("11000"))
696
- .status(OrderStatus.RECEIVED)
697
- .build();
698
- salesOrderRepository.save(order);
699
-
700
- var detail = SalesOrderDetail.builder()
701
- .orderId(order.getId())
702
- .lineNumber(1)
703
- .productCode("PROD-001")
704
- .productName("テスト商品A")
705
- .orderQuantity(new BigDecimal("10"))
706
- .allocatedQuantity(BigDecimal.ZERO)
707
- .shippedQuantity(BigDecimal.ZERO)
708
- .remainingQuantity(new BigDecimal("10"))
709
- .unit("個")
710
- .unitPrice(new BigDecimal("1000"))
711
- .amount(new BigDecimal("10000"))
712
- .taxCategory(TaxCategory.EXCLUSIVE)
713
- .taxRate(new BigDecimal("10.00"))
714
- .taxAmount(new BigDecimal("1000"))
715
- .build();
716
- salesOrderRepository.saveDetail(detail);
717
-
718
- // Act: 5個引当
719
- salesOrderRepository.updateDetailQuantities(
720
- detail.getId(),
721
- new BigDecimal("5"), // 引当数量
722
- BigDecimal.ZERO, // 出荷数量
723
- new BigDecimal("5") // 残数量
724
- );
725
-
726
- // Assert
727
- var result = salesOrderRepository.findByIdWithDetails(order.getId());
728
- assertThat(result).isPresent();
729
- var updatedDetail = result.get().getDetails().get(0);
730
- assertThat(updatedDetail.getAllocatedQuantity()).isEqualByComparingTo(new BigDecimal("5"));
731
- assertThat(updatedDetail.getRemainingQuantity()).isEqualByComparingTo(new BigDecimal("5"));
732
- }
733
- }
734
-
735
- @Nested
736
- @DisplayName("受注の検索")
737
- class OrderSearch {
738
-
739
- @Test
740
- @DisplayName("納期範囲で受注を検索できる")
741
- void canSearchByDeliveryDateRange() {
742
- // Arrange
743
- var order1 = SalesOrder.builder()
744
- .orderNumber("SO-2025-0004")
745
- .orderDate(LocalDate.of(2025, 1, 15))
746
- .customerCode("CUST-001")
747
- .requestedDeliveryDate(LocalDate.of(2025, 1, 25))
748
- .subtotal(BigDecimal.ZERO)
749
- .taxAmount(BigDecimal.ZERO)
750
- .totalAmount(BigDecimal.ZERO)
751
- .status(OrderStatus.RECEIVED)
752
- .build();
753
- salesOrderRepository.save(order1);
754
-
755
- var order2 = SalesOrder.builder()
756
- .orderNumber("SO-2025-0005")
757
- .orderDate(LocalDate.of(2025, 1, 20))
758
- .customerCode("CUST-001")
759
- .requestedDeliveryDate(LocalDate.of(2025, 2, 15))
760
- .subtotal(BigDecimal.ZERO)
761
- .taxAmount(BigDecimal.ZERO)
762
- .totalAmount(BigDecimal.ZERO)
763
- .status(OrderStatus.RECEIVED)
764
- .build();
765
- salesOrderRepository.save(order2);
766
-
767
- // Act: 1月中の納期で検索
768
- var januaryOrders = salesOrderRepository.findByDeliveryDateRange(
769
- LocalDate.of(2025, 1, 1),
770
- LocalDate.of(2025, 1, 31)
771
- );
772
-
773
- // Assert
774
- assertThat(januaryOrders).hasSize(1);
775
- assertThat(januaryOrders.get(0).getOrderNumber()).isEqualTo("SO-2025-0004");
776
- }
777
- }
778
- }
779
- ```
780
-
781
- </details>
782
-
783
- #### Green: テストを通す実装
784
-
785
- <details>
786
- <summary>SalesOrderRepository.java</summary>
787
-
788
- ```java
789
- // src/main/java/com/example/sms/application/port/out/SalesOrderRepository.java
790
- package com.example.sms.application.port.out;
791
-
792
- import com.example.sms.domain.model.sales.OrderStatus;
793
- import com.example.sms.domain.model.sales.SalesOrder;
794
- import com.example.sms.domain.model.sales.SalesOrderDetail;
795
-
796
- import java.math.BigDecimal;
797
- import java.time.LocalDate;
798
- import java.util.List;
799
- import java.util.Optional;
800
-
801
- public interface SalesOrderRepository {
802
-
803
- void save(SalesOrder order);
804
-
805
- void saveDetail(SalesOrderDetail detail);
806
-
807
- Optional<SalesOrder> findById(Integer id);
808
-
809
- Optional<SalesOrder> findByOrderNumber(String orderNumber);
810
-
811
- Optional<SalesOrder> findByIdWithDetails(Integer id);
812
-
813
- List<SalesOrder> findByDeliveryDateRange(LocalDate from, LocalDate to);
814
-
815
- void updateStatus(Integer id, OrderStatus status);
816
-
817
- void updateDetailQuantities(Integer detailId,
818
- BigDecimal allocatedQuantity,
819
- BigDecimal shippedQuantity,
820
- BigDecimal remainingQuantity);
821
-
822
- void deleteAll();
823
- }
824
- ```
825
-
826
- </details>
827
-
828
- <details>
829
- <summary>SalesOrderMapper.xml</summary>
830
-
831
- ```xml
832
- <?xml version="1.0" encoding="UTF-8" ?>
833
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
834
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
835
-
836
- <!-- src/main/resources/mapper/SalesOrderMapper.xml -->
837
- <mapper namespace="com.example.sms.infrastructure.persistence.mapper.SalesOrderMapper">
838
-
839
- <resultMap id="SalesOrderResultMap" type="com.example.sms.domain.model.sales.SalesOrder">
840
- <id property="id" column="ID"/>
841
- <result property="orderNumber" column="受注番号"/>
842
- <result property="orderDate" column="受注日"/>
843
- <result property="customerCode" column="顧客コード"/>
844
- <result property="customerBranchNumber" column="顧客枝番"/>
845
- <result property="shippingDestinationNumber" column="出荷先番号"/>
846
- <result property="representativeCode" column="担当者コード"/>
847
- <result property="requestedDeliveryDate" column="希望納期"/>
848
- <result property="scheduledShippingDate" column="出荷予定日"/>
849
- <result property="orderAmount" column="受注金額"/>
850
- <result property="taxAmount" column="消費税額"/>
851
- <result property="totalAmount" column="受注合計"/>
852
- <result property="status" column="ステータス"
853
- typeHandler="com.example.sms.infrastructure.out.persistence.typehandler.OrderStatusTypeHandler"/>
854
- <result property="quotationId" column="見積ID"/>
855
- <result property="customerOrderNumber" column="顧客注文番号"/>
856
- <result property="remarks" column="備考"/>
857
- <result property="createdAt" column="作成日時"/>
858
- <result property="createdBy" column="作成者"/>
859
- <result property="updatedAt" column="更新日時"/>
860
- <result property="updatedBy" column="更新者"/>
861
- <collection property="details" ofType="com.example.sms.domain.model.sales.SalesOrderDetail">
862
- <id property="id" column="明細ID"/>
863
- <result property="orderId" column="受注ID"/>
864
- <result property="lineNumber" column="行番号"/>
865
- <result property="productCode" column="商品コード"/>
866
- <result property="productName" column="商品名"/>
867
- <result property="orderQuantity" column="受注数量"/>
868
- <result property="allocatedQuantity" column="引当数量"/>
869
- <result property="shippedQuantity" column="出荷数量"/>
870
- <result property="remainingQuantity" column="残数量"/>
871
- <result property="unit" column="単位"/>
872
- <result property="unitPrice" column="単価"/>
873
- <result property="amount" column="金額"/>
874
- <result property="taxCategory" column="税区分"
875
- typeHandler="com.example.sms.infrastructure.out.persistence.typehandler.TaxCategoryTypeHandler"/>
876
- <result property="taxRate" column="消費税率"/>
877
- <result property="taxAmount" column="明細消費税額"/>
878
- <result property="warehouseCode" column="倉庫コード"/>
879
- <result property="requestedDeliveryDate" column="明細希望納期"/>
880
- </collection>
881
- </resultMap>
882
-
883
- <insert id="insertHeader" parameterType="com.example.sms.domain.model.sales.SalesOrder"
884
- useGeneratedKeys="true" keyProperty="id" keyColumn="ID">
885
- INSERT INTO "受注データ" (
886
- "受注番号", "受注日", "顧客コード", "顧客枝番", "出荷先番号",
887
- "担当者コード", "希望納期", "出荷予定日", "受注金額", "消費税額",
888
- "受注合計", "ステータス", "見積ID", "顧客注文番号", "備考",
889
- "作成日時", "作成者", "更新日時", "更新者"
890
- ) VALUES (
891
- #{orderNumber}, #{orderDate}, #{customerCode}, #{customerBranchNumber}, #{shippingDestinationNumber},
892
- #{representativeCode}, #{requestedDeliveryDate}, #{scheduledShippingDate}, #{orderAmount}, #{taxAmount},
893
- #{totalAmount},
894
- #{status, typeHandler=com.example.sms.infrastructure.out.persistence.typehandler.OrderStatusTypeHandler}::受注ステータス,
895
- #{quotationId}, #{customerOrderNumber}, #{remarks},
896
- CURRENT_TIMESTAMP, #{createdBy}, CURRENT_TIMESTAMP, #{updatedBy}
897
- )
898
- </insert>
899
-
900
- <insert id="insertDetail" useGeneratedKeys="true" keyProperty="id" keyColumn="ID">
901
- INSERT INTO "受注明細" (
902
- "受注ID", "行番号", "商品コード", "商品名", "受注数量",
903
- "引当数量", "出荷数量", "残数量", "単位", "単価", "金額",
904
- "税区分", "消費税率", "消費税額", "倉庫コード", "希望納期"
905
- ) VALUES (
906
- #{orderId}, #{lineNumber}, #{productCode}, #{productName}, #{orderQuantity},
907
- #{allocatedQuantity}, #{shippedQuantity}, #{remainingQuantity}, #{unit},
908
- #{unitPrice}, #{amount},
909
- #{taxCategory, typeHandler=com.example.sms.infrastructure.out.persistence.typehandler.TaxCategoryTypeHandler},
910
- #{taxRate}, #{taxAmount}, #{warehouseCode}, #{requestedDeliveryDate}
911
- )
912
- </insert>
913
-
914
- <select id="findByOrderNumber" resultMap="SalesOrderResultMap">
915
- SELECT * FROM "受注データ" WHERE "受注番号" = #{orderNumber}
916
- </select>
917
-
918
- <select id="findDetailsByOrderId" resultMap="SalesOrderDetailResultMap">
919
- SELECT * FROM "受注明細"
920
- WHERE "受注ID" = #{orderId}
921
- ORDER BY "行番号"
922
- </select>
923
-
924
- <select id="findByDeliveryDateRange" resultMap="SalesOrderResultMap">
925
- SELECT * FROM "受注データ"
926
- WHERE "希望納期" BETWEEN #{from} AND #{to}
927
- ORDER BY "希望納期"
928
- </select>
929
-
930
- <update id="updateStatus">
931
- UPDATE "受注データ"
932
- SET "ステータス" = #{status, typeHandler=com.example.sms.infrastructure.out.persistence.typehandler.OrderStatusTypeHandler},
933
- "更新日時" = CURRENT_TIMESTAMP
934
- WHERE "ID" = #{id}
935
- </update>
936
-
937
- <update id="updateDetailQuantities">
938
- UPDATE "受注明細"
939
- SET "引当数量" = #{allocatedQuantity},
940
- "出荷数量" = #{shippedQuantity},
941
- "残数量" = #{remainingQuantity}
942
- WHERE "ID" = #{detailId}
943
- </update>
944
-
945
- <delete id="deleteAll">
946
- DELETE FROM "受注明細";
947
- DELETE FROM "受注データ";
948
- </delete>
949
-
950
- </mapper>
951
- ```
952
-
953
- </details>
954
-
955
- ---
956
-
957
- ## 6.2 出荷業務の DB 設計
958
-
959
- ### 出荷業務の流れ
960
-
961
- 出荷業務は、受注データを起点として商品を顧客に届けるまでのプロセスです。
962
-
963
- ```plantuml
964
- @startuml
965
-
966
- title 出荷業務フロー
967
-
968
- |倉庫部門|
969
- start
970
- :受注データ確認;
971
- :出荷指示作成;
972
-
973
- :ピッキングリスト発行;
974
-
975
- :ピッキング作業;
976
- note right
977
- 倉庫から商品を
978
- ピックアップ
979
- end note
980
-
981
- :検品・梱包;
982
-
983
- :出荷;
984
- note right
985
- 配送業者に引渡し
986
- 追跡番号取得
987
- end note
988
-
989
- |営業部門|
990
- :売上計上;
991
- :納品書発行;
992
-
993
- stop
994
-
995
- @enduml
996
- ```
997
-
998
- ### 出荷指示データ・出荷明細データ
999
-
1000
- #### 出荷関連テーブルの ER 図
1001
-
1002
- ```plantuml
1003
- @startuml
1004
-
1005
- title 出荷関連テーブル
1006
-
1007
- entity 受注データ {
1008
- ID <<PK>>
1009
- --
1010
- 受注番号
1011
- 受注日
1012
- 顧客コード
1013
- ステータス
1014
- ...
1015
- }
1016
-
1017
- entity 受注明細 {
1018
- ID <<PK>>
1019
- --
1020
- 受注ID <<FK>>
1021
- 商品コード
1022
- 受注数量
1023
- 出荷数量
1024
- 残数量
1025
- ...
1026
- }
1027
-
1028
- entity 出荷データ {
1029
- ID <<PK>>
1030
- --
1031
- 出荷番号 <<UK>>
1032
- 出荷日
1033
- 受注ID <<FK>>
1034
- 顧客コード <<FK>>
1035
- 顧客枝番 <<FK>>
1036
- 出荷先番号 <<FK>>
1037
- 出荷先名
1038
- 出荷先郵便番号
1039
- 出荷先住所1
1040
- 出荷先住所2
1041
- 担当者コード
1042
- 倉庫コード
1043
- ステータス
1044
- 備考
1045
- 作成日時
1046
- 更新日時
1047
- }
1048
-
1049
- entity 出荷明細 {
1050
- ID <<PK>>
1051
- --
1052
- 出荷ID <<FK>>
1053
- 行番号
1054
- 受注明細ID <<FK>>
1055
- 商品コード <<FK>>
1056
- 商品名
1057
- 出荷数量
1058
- 単位
1059
- 単価
1060
- 金額
1061
- 税区分
1062
- 消費税率
1063
- 消費税額
1064
- 倉庫コード
1065
- 備考
1066
- }
1067
-
1068
- 受注データ ||--o{ 受注明細
1069
- 受注データ ||--o{ 出荷データ
1070
- 出荷データ ||--o{ 出荷明細
1071
- 受注明細 ||--o{ 出荷明細
1072
-
1073
- @enduml
1074
- ```
1075
-
1076
- ### 出荷ステータスの定義
1077
-
1078
- ```plantuml
1079
- @startuml
1080
-
1081
- title 出荷ステータス遷移図
1082
-
1083
- [*] --> 出荷指示済
1084
-
1085
- 出荷指示済 --> 出荷準備中 : 出荷準備開始
1086
- 出荷準備中 --> 出荷済 : 出荷完了
1087
-
1088
- 出荷済 --> [*]
1089
-
1090
- 出荷指示済 --> キャンセル
1091
- 出荷準備中 --> キャンセル
1092
-
1093
- キャンセル --> [*]
1094
-
1095
- @enduml
1096
- ```
1097
-
1098
- | ステータス | 説明 |
1099
- |-----------|------|
1100
- | **出荷指示済** | 出荷指示が作成された状態 |
1101
- | **出荷準備中** | 出荷準備作業中の状態 |
1102
- | **出荷済** | 配送業者に引き渡した状態 |
1103
- | **キャンセル** | 出荷指示がキャンセルされた状態 |
1104
-
1105
- ### マイグレーション:出荷関連テーブルの作成
1106
-
1107
- <details>
1108
- <summary>V008__create_shipment_tables.sql</summary>
1109
-
1110
- ```sql
1111
- -- src/main/resources/db/migration/V008__create_shipment_tables.sql
1112
-
1113
- -- 出荷ステータス
1114
- CREATE TYPE 出荷ステータス AS ENUM ('出荷指示済', '出荷準備中', '出荷済', 'キャンセル');
1115
-
1116
- -- 出荷データ(ヘッダ)
1117
- CREATE TABLE "出荷データ" (
1118
- "ID" SERIAL PRIMARY KEY,
1119
- "出荷番号" VARCHAR(20) UNIQUE NOT NULL,
1120
- "出荷日" DATE NOT NULL,
1121
- "受注ID" INTEGER NOT NULL,
1122
- "顧客コード" VARCHAR(20) NOT NULL,
1123
- "顧客枝番" VARCHAR(10) DEFAULT '00',
1124
- "出荷先番号" VARCHAR(10),
1125
- "出荷先名" VARCHAR(100),
1126
- "出荷先郵便番号" VARCHAR(10),
1127
- "出荷先住所1" VARCHAR(100),
1128
- "出荷先住所2" VARCHAR(100),
1129
- "担当者コード" VARCHAR(20),
1130
- "倉庫コード" VARCHAR(20),
1131
- "ステータス" 出荷ステータス DEFAULT '出荷指示済' NOT NULL,
1132
- "備考" TEXT,
1133
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1134
- "作成者" VARCHAR(50),
1135
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1136
- "更新者" VARCHAR(50),
1137
- CONSTRAINT "fk_出荷データ_受注"
1138
- FOREIGN KEY ("受注ID") REFERENCES "受注データ"("ID"),
1139
- CONSTRAINT "fk_出荷データ_顧客"
1140
- FOREIGN KEY ("顧客コード", "顧客枝番") REFERENCES "顧客マスタ"("顧客コード", "顧客枝番"),
1141
- CONSTRAINT "fk_出荷データ_出荷先"
1142
- FOREIGN KEY ("顧客コード", "顧客枝番", "出荷先番号") REFERENCES "出荷先マスタ"("取引先コード", "顧客枝番", "出荷先番号")
1143
- );
1144
-
1145
- -- 出荷明細
1146
- CREATE TABLE "出荷明細" (
1147
- "ID" SERIAL PRIMARY KEY,
1148
- "出荷ID" INTEGER NOT NULL,
1149
- "行番号" INTEGER NOT NULL,
1150
- "受注明細ID" INTEGER NOT NULL,
1151
- "商品コード" VARCHAR(20) NOT NULL,
1152
- "商品名" VARCHAR(100) NOT NULL,
1153
- "出荷数量" DECIMAL(15, 2) NOT NULL,
1154
- "単位" VARCHAR(10),
1155
- "単価" DECIMAL(15, 2) NOT NULL,
1156
- "金額" DECIMAL(15, 2) NOT NULL,
1157
- "税区分" 税区分 DEFAULT '外税' NOT NULL,
1158
- "消費税率" DECIMAL(5, 2) DEFAULT 10.00 NOT NULL,
1159
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1160
- "倉庫コード" VARCHAR(20),
1161
- "備考" TEXT,
1162
- CONSTRAINT "fk_出荷明細_出荷"
1163
- FOREIGN KEY ("出荷ID") REFERENCES "出荷データ"("ID") ON DELETE CASCADE,
1164
- CONSTRAINT "fk_出荷明細_受注明細"
1165
- FOREIGN KEY ("受注明細ID") REFERENCES "受注明細"("ID"),
1166
- CONSTRAINT "fk_出荷明細_商品"
1167
- FOREIGN KEY ("商品コード") REFERENCES "商品マスタ"("商品コード"),
1168
- CONSTRAINT "uq_出荷明細_行番号" UNIQUE ("出荷ID", "行番号")
1169
- );
1170
-
1171
- -- インデックス
1172
- CREATE INDEX "idx_出荷データ_受注ID" ON "出荷データ"("受注ID");
1173
- CREATE INDEX "idx_出荷データ_顧客コード" ON "出荷データ"("顧客コード");
1174
- CREATE INDEX "idx_出荷データ_出荷日" ON "出荷データ"("出荷日");
1175
- CREATE INDEX "idx_出荷データ_ステータス" ON "出荷データ"("ステータス");
1176
- ```
1177
-
1178
- </details>
1179
-
1180
- ### 出荷ステータス Enum
1181
-
1182
- <details>
1183
- <summary>ShipmentStatus.java</summary>
1184
-
1185
- ```java
1186
- // src/main/java/com/example/sms/domain/model/shipping/ShipmentStatus.java
1187
- package com.example.sms.domain.model.shipping;
1188
-
1189
- import lombok.Getter;
1190
- import lombok.RequiredArgsConstructor;
1191
-
1192
- @Getter
1193
- @RequiredArgsConstructor
1194
- public enum ShipmentStatus {
1195
- INSTRUCTED("出荷指示済"),
1196
- PREPARING("出荷準備中"),
1197
- SHIPPED("出荷済"),
1198
- CANCELLED("キャンセル");
1199
-
1200
- private final String displayName;
1201
-
1202
- public static ShipmentStatus fromDisplayName(String displayName) {
1203
- for (ShipmentStatus status : values()) {
1204
- if (status.displayName.equals(displayName)) {
1205
- return status;
1206
- }
1207
- }
1208
- throw new IllegalArgumentException("Unknown shipment status: " + displayName);
1209
- }
1210
- }
1211
- ```
1212
-
1213
- </details>
1214
-
1215
- ### 出荷エンティティ
1216
-
1217
- <details>
1218
- <summary>Shipment.java</summary>
1219
-
1220
- ```java
1221
- // src/main/java/com/example/sms/domain/model/shipping/Shipment.java
1222
- package com.example.sms.domain.model.shipping;
1223
-
1224
- import lombok.AllArgsConstructor;
1225
- import lombok.Builder;
1226
- import lombok.Data;
1227
- import lombok.NoArgsConstructor;
1228
-
1229
- import java.time.LocalDate;
1230
- import java.time.LocalDateTime;
1231
- import java.util.List;
1232
-
1233
- @Data
1234
- @Builder
1235
- @NoArgsConstructor
1236
- @AllArgsConstructor
1237
- @SuppressWarnings("PMD.RedundantFieldInitializer")
1238
- public class Shipment {
1239
- private Integer id;
1240
- private String shipmentNumber;
1241
- private LocalDate shipmentDate;
1242
- private Integer orderId;
1243
- private String customerCode;
1244
- private String customerBranchNumber;
1245
- private String shippingDestinationNumber;
1246
- private String shippingDestinationName;
1247
- private String shippingDestinationPostalCode;
1248
- private String shippingDestinationAddress1;
1249
- private String shippingDestinationAddress2;
1250
- private String representativeCode;
1251
- private String warehouseCode;
1252
- @Builder.Default
1253
- private ShipmentStatus status = ShipmentStatus.INSTRUCTED;
1254
- private String remarks;
1255
- private LocalDateTime createdAt;
1256
- private String createdBy;
1257
- private LocalDateTime updatedAt;
1258
- private String updatedBy;
1259
- private List<ShipmentDetail> details;
1260
- }
1261
- ```
1262
-
1263
- </details>
1264
-
1265
- <details>
1266
- <summary>ShipmentDetail.java</summary>
1267
-
1268
- ```java
1269
- // src/main/java/com/example/sms/domain/model/shipping/ShipmentDetail.java
1270
- package com.example.sms.domain.model.shipping;
1271
-
1272
- import com.example.sms.domain.model.product.TaxCategory;
1273
- import lombok.AllArgsConstructor;
1274
- import lombok.Builder;
1275
- import lombok.Data;
1276
- import lombok.NoArgsConstructor;
1277
-
1278
- import java.math.BigDecimal;
1279
-
1280
- @Data
1281
- @Builder
1282
- @NoArgsConstructor
1283
- @AllArgsConstructor
1284
- public class ShipmentDetail {
1285
- private Integer id;
1286
- private Integer shipmentId;
1287
- private Integer lineNumber;
1288
- private Integer orderDetailId;
1289
- private String productCode;
1290
- private String productName;
1291
- private BigDecimal shippedQuantity;
1292
- private String unit;
1293
- private BigDecimal unitPrice;
1294
- private BigDecimal amount;
1295
- private TaxCategory taxCategory;
1296
- private BigDecimal taxRate;
1297
- private BigDecimal taxAmount;
1298
- private String warehouseCode;
1299
- private String remarks;
1300
- }
1301
- ```
1302
-
1303
- </details>
1304
-
1305
- ### TDD:出荷指示の登録と取得
1306
-
1307
- <details>
1308
- <summary>ShipmentInstructionRepositoryTest.java</summary>
1309
-
1310
- ```java
1311
- // src/test/java/com/example/sms/infrastructure/persistence/repository/ShipmentInstructionRepositoryTest.java
1312
- package com.example.sms.infrastructure.persistence.repository;
1313
-
1314
- import com.example.sms.application.port.out.SalesOrderRepository;
1315
- import com.example.sms.application.port.out.ShipmentInstructionRepository;
1316
- import com.example.sms.domain.model.sales.*;
1317
- import com.example.sms.testsetup.BaseIntegrationTest;
1318
- import org.junit.jupiter.api.*;
1319
- import org.springframework.beans.factory.annotation.Autowired;
1320
-
1321
- import java.math.BigDecimal;
1322
- import java.time.LocalDate;
1323
-
1324
- import static org.assertj.core.api.Assertions.*;
1325
-
1326
- @DisplayName("出荷指示リポジトリ")
1327
- class ShipmentInstructionRepositoryTest extends BaseIntegrationTest {
1328
-
1329
- @Autowired
1330
- private ShipmentInstructionRepository shipmentInstructionRepository;
1331
-
1332
- @Autowired
1333
- private SalesOrderRepository salesOrderRepository;
1334
-
1335
- @BeforeEach
1336
- void setUp() {
1337
- shipmentInstructionRepository.deleteAll();
1338
- salesOrderRepository.deleteAll();
1339
- setupOrderData();
1340
- }
1341
-
1342
- private void setupOrderData() {
1343
- // 受注データ準備
1344
- var order = SalesOrder.builder()
1345
- .orderNumber("SO-2025-0001")
1346
- .orderDate(LocalDate.of(2025, 1, 20))
1347
- .customerCode("CUST-001")
1348
- .requestedDeliveryDate(LocalDate.of(2025, 1, 30))
1349
- .subtotal(new BigDecimal("50000"))
1350
- .taxAmount(new BigDecimal("5000"))
1351
- .totalAmount(new BigDecimal("55000"))
1352
- .status(OrderStatus.RECEIVED)
1353
- .build();
1354
- salesOrderRepository.save(order);
1355
- }
1356
-
1357
- @Nested
1358
- @DisplayName("出荷指示の登録")
1359
- class ShipmentInstructionRegistration {
1360
-
1361
- @Test
1362
- @DisplayName("出荷指示を登録できる")
1363
- void canRegisterShipmentInstruction() {
1364
- // Arrange
1365
- var order = salesOrderRepository.findByOrderNumber("SO-2025-0001").get();
1366
- var instruction = ShipmentInstruction.builder()
1367
- .instructionNumber("SI-2025-0001")
1368
- .instructionDate(LocalDate.of(2025, 1, 25))
1369
- .orderId(order.getId())
1370
- .customerCode("CUST-001")
1371
- .scheduledShipDate(LocalDate.of(2025, 1, 28))
1372
- .warehouseCode("WH-001")
1373
- .status(ShipmentStatus.INSTRUCTED)
1374
- .build();
1375
-
1376
- // Act
1377
- shipmentInstructionRepository.save(instruction);
1378
-
1379
- // Assert
1380
- var result = shipmentInstructionRepository.findByInstructionNumber("SI-2025-0001");
1381
- assertThat(result).isPresent();
1382
- assertThat(result.get().getOrderId()).isEqualTo(order.getId());
1383
- assertThat(result.get().getStatus()).isEqualTo(ShipmentStatus.INSTRUCTED);
1384
- }
1385
-
1386
- @Test
1387
- @DisplayName("出荷指示ステータスを更新できる")
1388
- void canUpdateShipmentStatus() {
1389
- // Arrange
1390
- var order = salesOrderRepository.findByOrderNumber("SO-2025-0001").get();
1391
- var instruction = ShipmentInstruction.builder()
1392
- .instructionNumber("SI-2025-0002")
1393
- .instructionDate(LocalDate.of(2025, 1, 25))
1394
- .orderId(order.getId())
1395
- .customerCode("CUST-001")
1396
- .scheduledShipDate(LocalDate.of(2025, 1, 28))
1397
- .warehouseCode("WH-001")
1398
- .status(ShipmentStatus.INSTRUCTED)
1399
- .build();
1400
- shipmentInstructionRepository.save(instruction);
1401
-
1402
- // Act: ピッキング開始
1403
- shipmentInstructionRepository.updateStatus(instruction.getId(), ShipmentStatus.PICKING);
1404
-
1405
- // Assert
1406
- var result = shipmentInstructionRepository.findById(instruction.getId());
1407
- assertThat(result).isPresent();
1408
- assertThat(result.get().getStatus()).isEqualTo(ShipmentStatus.PICKING);
1409
- }
1410
-
1411
- @Test
1412
- @DisplayName("追跡番号を登録できる")
1413
- void canRegisterTrackingNumber() {
1414
- // Arrange
1415
- var order = salesOrderRepository.findByOrderNumber("SO-2025-0001").get();
1416
- var instruction = ShipmentInstruction.builder()
1417
- .instructionNumber("SI-2025-0003")
1418
- .instructionDate(LocalDate.of(2025, 1, 25))
1419
- .orderId(order.getId())
1420
- .customerCode("CUST-001")
1421
- .scheduledShipDate(LocalDate.of(2025, 1, 28))
1422
- .warehouseCode("WH-001")
1423
- .status(ShipmentStatus.SHIPPED)
1424
- .carrierCode("YAMATO")
1425
- .trackingNumber("1234-5678-9012")
1426
- .build();
1427
-
1428
- // Act
1429
- shipmentInstructionRepository.save(instruction);
1430
-
1431
- // Assert
1432
- var result = shipmentInstructionRepository.findByInstructionNumber("SI-2025-0003");
1433
- assertThat(result).isPresent();
1434
- assertThat(result.get().getCarrierCode()).isEqualTo("YAMATO");
1435
- assertThat(result.get().getTrackingNumber()).isEqualTo("1234-5678-9012");
1436
- }
1437
- }
1438
- }
1439
- ```
1440
-
1441
- </details>
1442
-
1443
- ### 受注から出荷指示を作成するサービス
1444
-
1445
- <details>
1446
- <summary>ShipmentService.java</summary>
1447
-
1448
- ```java
1449
- // src/main/java/com/example/sms/application/service/ShipmentService.java
1450
- package com.example.sms.application.service;
1451
-
1452
- import com.example.sms.application.port.out.*;
1453
- import com.example.sms.domain.model.sales.*;
1454
- import lombok.RequiredArgsConstructor;
1455
- import org.springframework.stereotype.Service;
1456
- import org.springframework.transaction.annotation.Transactional;
1457
-
1458
- import java.math.BigDecimal;
1459
- import java.time.LocalDate;
1460
-
1461
- @Service
1462
- @RequiredArgsConstructor
1463
- public class ShipmentService {
1464
-
1465
- private final SalesOrderRepository salesOrderRepository;
1466
- private final ShipmentInstructionRepository shipmentInstructionRepository;
1467
-
1468
- /**
1469
- * 受注から出荷指示を作成する
1470
- */
1471
- @Transactional
1472
- public ShipmentInstruction createShipmentInstruction(Integer orderId,
1473
- LocalDate scheduledShipDate,
1474
- String warehouseCode) {
1475
- var order = salesOrderRepository.findByIdWithDetails(orderId)
1476
- .orElseThrow(() -> new IllegalArgumentException("Order not found: " + orderId));
1477
-
1478
- if (order.getStatus() != OrderStatus.RECEIVED && order.getStatus() != OrderStatus.ALLOCATED) {
1479
- throw new IllegalStateException("Cannot create shipment instruction for order with status: "
1480
- + order.getStatus());
1481
- }
1482
-
1483
- // 出荷指示ヘッダ作成
1484
- var instruction = ShipmentInstruction.builder()
1485
- .instructionNumber(generateInstructionNumber(LocalDate.now()))
1486
- .instructionDate(LocalDate.now())
1487
- .orderId(orderId)
1488
- .customerCode(order.getCustomerCode())
1489
- .shipToCode(order.getShipToCode())
1490
- .scheduledShipDate(scheduledShipDate)
1491
- .warehouseCode(warehouseCode)
1492
- .status(ShipmentStatus.INSTRUCTED)
1493
- .build();
1494
- shipmentInstructionRepository.save(instruction);
1495
-
1496
- // 出荷指示明細作成
1497
- int lineNumber = 1;
1498
- for (var orderDetail : order.getDetails()) {
1499
- if (orderDetail.getRemainingQuantity().compareTo(BigDecimal.ZERO) > 0) {
1500
- var instructionDetail = ShipmentInstructionDetail.builder()
1501
- .shipmentInstructionId(instruction.getId())
1502
- .lineNumber(lineNumber++)
1503
- .orderDetailId(orderDetail.getId())
1504
- .productCode(orderDetail.getProductCode())
1505
- .productName(orderDetail.getProductName())
1506
- .instructedQuantity(orderDetail.getRemainingQuantity())
1507
- .shippedQuantity(BigDecimal.ZERO)
1508
- .unit(orderDetail.getUnit())
1509
- .build();
1510
- shipmentInstructionRepository.saveDetail(instructionDetail);
1511
- }
1512
- }
1513
-
1514
- // 受注ステータスを更新
1515
- salesOrderRepository.updateStatus(orderId, OrderStatus.SHIPMENT_INSTRUCTED);
1516
-
1517
- return instruction;
1518
- }
1519
-
1520
- private String generateInstructionNumber(LocalDate date) {
1521
- return String.format("SI-%d-%04d", date.getYear(), System.currentTimeMillis() % 10000);
1522
- }
1523
- }
1524
- ```
1525
-
1526
- </details>
1527
-
1528
- ---
1529
-
1530
- ## 6.3 売上業務の DB 設計
1531
-
1532
- ### 売上データ・売上明細データの構造
1533
-
1534
- #### 売上関連テーブルの ER 図
1535
-
1536
- ```plantuml
1537
- @startuml
1538
-
1539
- title 売上関連テーブル
1540
-
1541
- entity 出荷データ {
1542
- ID <<PK>>
1543
- --
1544
- 出荷番号
1545
- 受注ID <<FK>>
1546
- ステータス
1547
- ...
1548
- }
1549
-
1550
- entity 売上データ {
1551
- ID <<PK>>
1552
- --
1553
- 売上番号 <<UK>>
1554
- 売上日
1555
- 受注ID <<FK>>
1556
- 出荷ID <<FK>>
1557
- 顧客コード <<FK>>
1558
- 顧客枝番 <<FK>>
1559
- 担当者コード
1560
- 売上金額
1561
- 消費税額
1562
- 売上合計
1563
- ステータス
1564
- 請求ID
1565
- 備考
1566
- 作成日時
1567
- 更新日時
1568
- }
1569
-
1570
- entity 売上明細 {
1571
- ID <<PK>>
1572
- --
1573
- 売上ID <<FK>>
1574
- 行番号
1575
- 受注明細ID <<FK>>
1576
- 出荷明細ID <<FK>>
1577
- 商品コード <<FK>>
1578
- 商品名
1579
- 売上数量
1580
- 単位
1581
- 単価
1582
- 金額
1583
- 税区分
1584
- 消費税率
1585
- 消費税額
1586
- 備考
1587
- }
1588
-
1589
- entity 返品データ {
1590
- ID <<PK>>
1591
- --
1592
- 返品番号 <<UK>>
1593
- 返品日
1594
- 売上ID <<FK>>
1595
- 顧客コード <<FK>>
1596
- 返品理由
1597
- 返品金額
1598
- 消費税額
1599
- 返品合計
1600
- 倉庫コード
1601
- 備考
1602
- 作成日時
1603
- 更新日時
1604
- }
1605
-
1606
- entity 返品明細 {
1607
- ID <<PK>>
1608
- --
1609
- 返品ID <<FK>>
1610
- 行番号
1611
- 売上明細ID <<FK>>
1612
- 商品コード <<FK>>
1613
- 商品名
1614
- 返品数量
1615
- 単位
1616
- 単価
1617
- 金額
1618
- 税区分
1619
- 消費税率
1620
- 消費税額
1621
- 備考
1622
- }
1623
-
1624
- 出荷データ ||--o| 売上データ
1625
- 売上データ ||--o{ 売上明細
1626
- 売上データ ||--o{ 返品データ
1627
- 返品データ ||--o{ 返品明細
1628
-
1629
- @enduml
1630
- ```
1631
-
1632
- ### 出荷完了から売上計上への流れ
1633
-
1634
- ```plantuml
1635
- @startuml
1636
-
1637
- title 出荷完了から売上計上への流れ
1638
-
1639
- |倉庫部門|
1640
- start
1641
- :出荷完了報告;
1642
- note right
1643
- 配送業者に引渡し
1644
- 追跡番号登録
1645
- end note
1646
-
1647
- |システム|
1648
- :出荷ステータス更新;
1649
- note right
1650
- 出荷済に変更
1651
- end note
1652
-
1653
- :売上データ自動生成;
1654
- note right
1655
- 出荷指示データを元に
1656
- 売上ヘッダ・明細を作成
1657
- end note
1658
-
1659
- :受注明細の出荷数量更新;
1660
-
1661
- if (全明細出荷完了?) then (yes)
1662
- :受注ステータスを出荷済に更新;
1663
- else (no)
1664
- :一部出荷として継続;
1665
- endif
1666
-
1667
- |営業部門|
1668
- :納品書発行;
1669
-
1670
- stop
1671
-
1672
- @enduml
1673
- ```
1674
-
1675
- ### 売上ステータスの定義
1676
-
1677
- | ステータス | 説明 |
1678
- |-----------|------|
1679
- | **計上済** | 売上が計上された状態 |
1680
- | **請求済** | 請求書が発行された状態 |
1681
- | **入金済** | 入金が確認された状態 |
1682
- | **取消** | 売上が取り消された状態 |
1683
-
1684
- ### マイグレーション:売上関連テーブルの作成
1685
-
1686
- <details>
1687
- <summary>V009__create_sales_tables.sql</summary>
1688
-
1689
- ```sql
1690
- -- src/main/resources/db/migration/V009__create_sales_tables.sql
1691
-
1692
- -- 売上ステータス
1693
- CREATE TYPE 売上ステータス AS ENUM ('計上済', '請求済', '入金済', 'キャンセル');
1694
-
1695
- -- 売上データ(ヘッダ)
1696
- CREATE TABLE "売上データ" (
1697
- "ID" SERIAL PRIMARY KEY,
1698
- "売上番号" VARCHAR(20) UNIQUE NOT NULL,
1699
- "売上日" DATE NOT NULL,
1700
- "受注ID" INTEGER NOT NULL,
1701
- "出荷ID" INTEGER,
1702
- "顧客コード" VARCHAR(20) NOT NULL,
1703
- "顧客枝番" VARCHAR(10) DEFAULT '00',
1704
- "担当者コード" VARCHAR(20),
1705
- "売上金額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1706
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1707
- "売上合計" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1708
- "ステータス" 売上ステータス DEFAULT '計上済' NOT NULL,
1709
- "請求ID" INTEGER,
1710
- "備考" TEXT,
1711
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1712
- "作成者" VARCHAR(50),
1713
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1714
- "更新者" VARCHAR(50),
1715
- CONSTRAINT "fk_売上データ_受注"
1716
- FOREIGN KEY ("受注ID") REFERENCES "受注データ"("ID"),
1717
- CONSTRAINT "fk_売上データ_出荷"
1718
- FOREIGN KEY ("出荷ID") REFERENCES "出荷データ"("ID"),
1719
- CONSTRAINT "fk_売上データ_顧客"
1720
- FOREIGN KEY ("顧客コード", "顧客枝番") REFERENCES "顧客マスタ"("顧客コード", "顧客枝番")
1721
- );
1722
-
1723
- -- 売上明細
1724
- CREATE TABLE "売上明細" (
1725
- "ID" SERIAL PRIMARY KEY,
1726
- "売上ID" INTEGER NOT NULL,
1727
- "行番号" INTEGER NOT NULL,
1728
- "受注明細ID" INTEGER NOT NULL,
1729
- "出荷明細ID" INTEGER,
1730
- "商品コード" VARCHAR(20) NOT NULL,
1731
- "商品名" VARCHAR(100) NOT NULL,
1732
- "売上数量" DECIMAL(15, 2) NOT NULL,
1733
- "単位" VARCHAR(10),
1734
- "単価" DECIMAL(15, 2) NOT NULL,
1735
- "金額" DECIMAL(15, 2) NOT NULL,
1736
- "税区分" 税区分 DEFAULT '外税' NOT NULL,
1737
- "消費税率" DECIMAL(5, 2) DEFAULT 10.00 NOT NULL,
1738
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1739
- "備考" TEXT,
1740
- CONSTRAINT "fk_売上明細_売上"
1741
- FOREIGN KEY ("売上ID") REFERENCES "売上データ"("ID") ON DELETE CASCADE,
1742
- CONSTRAINT "fk_売上明細_受注明細"
1743
- FOREIGN KEY ("受注明細ID") REFERENCES "受注明細"("ID"),
1744
- CONSTRAINT "fk_売上明細_出荷明細"
1745
- FOREIGN KEY ("出荷明細ID") REFERENCES "出荷明細"("ID"),
1746
- CONSTRAINT "fk_売上明細_商品"
1747
- FOREIGN KEY ("商品コード") REFERENCES "商品マスタ"("商品コード"),
1748
- CONSTRAINT "uq_売上明細_行番号" UNIQUE ("売上ID", "行番号")
1749
- );
1750
-
1751
- -- インデックス
1752
- CREATE INDEX "idx_売上データ_受注ID" ON "売上データ"("受注ID");
1753
- CREATE INDEX "idx_売上データ_出荷ID" ON "売上データ"("出荷ID");
1754
- CREATE INDEX "idx_売上データ_顧客コード" ON "売上データ"("顧客コード");
1755
- CREATE INDEX "idx_売上データ_売上日" ON "売上データ"("売上日");
1756
- CREATE INDEX "idx_売上データ_ステータス" ON "売上データ"("ステータス");
1757
- ```
1758
-
1759
- </details>
1760
-
1761
- ### 売上ステータス Enum
1762
-
1763
- <details>
1764
- <summary>SalesStatus.java</summary>
1765
-
1766
- ```java
1767
- // src/main/java/com/example/sms/domain/model/sales/SalesStatus.java
1768
- package com.example.sms.domain.model.sales;
1769
-
1770
- import lombok.Getter;
1771
- import lombok.RequiredArgsConstructor;
1772
-
1773
- @Getter
1774
- @RequiredArgsConstructor
1775
- public enum SalesStatus {
1776
- RECORDED("計上済"),
1777
- INVOICED("請求済"),
1778
- COLLECTED("入金済"),
1779
- CANCELLED("キャンセル");
1780
-
1781
- private final String displayName;
1782
-
1783
- public static SalesStatus fromDisplayName(String displayName) {
1784
- for (SalesStatus status : values()) {
1785
- if (status.displayName.equals(displayName)) {
1786
- return status;
1787
- }
1788
- }
1789
- throw new IllegalArgumentException("Unknown sales status: " + displayName);
1790
- }
1791
- }
1792
- ```
1793
-
1794
- </details>
1795
-
1796
- ### 売上エンティティ
1797
-
1798
- <details>
1799
- <summary>Sales.java</summary>
1800
-
1801
- ```java
1802
- // src/main/java/com/example/sms/domain/model/sales/Sales.java
1803
- package com.example.sms.domain.model.sales;
1804
-
1805
- import lombok.AllArgsConstructor;
1806
- import lombok.Builder;
1807
- import lombok.Data;
1808
- import lombok.NoArgsConstructor;
1809
-
1810
- import java.math.BigDecimal;
1811
- import java.time.LocalDate;
1812
- import java.time.LocalDateTime;
1813
- import java.util.ArrayList;
1814
- import java.util.List;
1815
-
1816
- @Data
1817
- @Builder
1818
- @NoArgsConstructor
1819
- @AllArgsConstructor
1820
- @SuppressWarnings("PMD.RedundantFieldInitializer")
1821
- public class Sales {
1822
- private Integer id;
1823
- private String salesNumber;
1824
- private LocalDate salesDate;
1825
- private Integer orderId;
1826
- private Integer shipmentId;
1827
- private String customerCode;
1828
- private String customerBranchNumber;
1829
- private String representativeCode;
1830
- @Builder.Default
1831
- private BigDecimal salesAmount = BigDecimal.ZERO;
1832
- @Builder.Default
1833
- private BigDecimal taxAmount = BigDecimal.ZERO;
1834
- @Builder.Default
1835
- private BigDecimal totalAmount = BigDecimal.ZERO;
1836
- @Builder.Default
1837
- private SalesStatus status = SalesStatus.RECORDED;
1838
- private Integer invoiceId;
1839
- private String remarks;
1840
- private LocalDateTime createdAt;
1841
- private String createdBy;
1842
- private LocalDateTime updatedAt;
1843
- private String updatedBy;
1844
-
1845
- @Builder.Default
1846
- private List<SalesDetail> details = new ArrayList<>();
1847
- }
1848
- ```
1849
-
1850
- </details>
1851
-
1852
- <details>
1853
- <summary>SalesDetail.java</summary>
1854
-
1855
- ```java
1856
- // src/main/java/com/example/sms/domain/model/sales/SalesDetail.java
1857
- package com.example.sms.domain.model.sales;
1858
-
1859
- import com.example.sms.domain.model.product.TaxCategory;
1860
- import lombok.AllArgsConstructor;
1861
- import lombok.Builder;
1862
- import lombok.Data;
1863
- import lombok.NoArgsConstructor;
1864
-
1865
- import java.math.BigDecimal;
1866
-
1867
- @Data
1868
- @Builder
1869
- @NoArgsConstructor
1870
- @AllArgsConstructor
1871
- public class SalesDetail {
1872
- private Integer id;
1873
- private Integer salesId;
1874
- private Integer lineNumber;
1875
- private Integer orderDetailId;
1876
- private Integer shipmentDetailId;
1877
- private String productCode;
1878
- private String productName;
1879
- private BigDecimal salesQuantity;
1880
- private String unit;
1881
- private BigDecimal unitPrice;
1882
- private BigDecimal amount;
1883
- private TaxCategory taxCategory;
1884
- private BigDecimal taxRate;
1885
- private BigDecimal taxAmount;
1886
- private String remarks;
1887
- }
1888
- ```
1889
-
1890
- </details>
1891
-
1892
- ### 返品処理の設計
1893
-
1894
- 返品処理は、売上データに対してマイナスの取引を記録する処理です。
1895
-
1896
- #### 返品業務の流れ
1897
-
1898
- ```plantuml
1899
- @startuml
1900
-
1901
- title 返品業務フロー
1902
-
1903
- |営業担当|
1904
- start
1905
- :返品依頼受付;
1906
- note right
1907
- 顧客からの返品依頼
1908
- 理由の確認
1909
- end note
1910
-
1911
- :返品データ登録;
1912
-
1913
- |倉庫担当|
1914
- :返品商品受入;
1915
- :検品;
1916
-
1917
- if (商品状態OK?) then (yes)
1918
- :在庫戻入;
1919
- else (no)
1920
- :廃棄処理;
1921
- endif
1922
-
1923
- |経理担当|
1924
- :売上取消処理;
1925
- note right
1926
- 返品金額分の
1927
- 売上をマイナス計上
1928
- end note
1929
-
1930
- stop
1931
-
1932
- @enduml
1933
- ```
1934
-
1935
- ### マイグレーション:返品関連テーブルの作成
1936
-
1937
- <details>
1938
- <summary>V009__create_return_tables.sql</summary>
1939
-
1940
- ```sql
1941
- -- src/main/resources/db/migration/V009__create_return_tables.sql
1942
-
1943
- -- 返品データ
1944
- CREATE TABLE "返品データ" (
1945
- "ID" SERIAL PRIMARY KEY,
1946
- "返品番号" VARCHAR(20) UNIQUE NOT NULL,
1947
- "返品日" DATE NOT NULL,
1948
- "売上ID" INTEGER NOT NULL,
1949
- "顧客コード" VARCHAR(20) NOT NULL,
1950
- "返品理由" VARCHAR(200),
1951
- "返品金額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1952
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1953
- "返品合計" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1954
- "倉庫コード" VARCHAR(20),
1955
- "備考" TEXT,
1956
- "作成日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1957
- "作成者" VARCHAR(50),
1958
- "更新日時" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1959
- "更新者" VARCHAR(50),
1960
- CONSTRAINT "fk_返品データ_売上"
1961
- FOREIGN KEY ("売上ID") REFERENCES "売上データ"("ID"),
1962
- CONSTRAINT "fk_返品データ_顧客"
1963
- FOREIGN KEY ("顧客コード") REFERENCES "顧客マスタ"("顧客コード")
1964
- );
1965
-
1966
- -- 返品明細
1967
- CREATE TABLE "返品明細" (
1968
- "ID" SERIAL PRIMARY KEY,
1969
- "返品ID" INTEGER NOT NULL,
1970
- "行番号" INTEGER NOT NULL,
1971
- "売上明細ID" INTEGER NOT NULL,
1972
- "商品コード" VARCHAR(20) NOT NULL,
1973
- "商品名" VARCHAR(100) NOT NULL,
1974
- "返品数量" DECIMAL(15, 2) NOT NULL,
1975
- "単位" VARCHAR(10),
1976
- "単価" DECIMAL(15, 2) NOT NULL,
1977
- "金額" DECIMAL(15, 2) NOT NULL,
1978
- "税区分" 税区分 DEFAULT '外税' NOT NULL,
1979
- "消費税率" DECIMAL(5, 2) DEFAULT 10.00 NOT NULL,
1980
- "消費税額" DECIMAL(15, 2) DEFAULT 0 NOT NULL,
1981
- "備考" TEXT,
1982
- CONSTRAINT "fk_返品明細_返品"
1983
- FOREIGN KEY ("返品ID") REFERENCES "返品データ"("ID") ON DELETE CASCADE,
1984
- CONSTRAINT "fk_返品明細_売上明細"
1985
- FOREIGN KEY ("売上明細ID") REFERENCES "売上明細"("ID"),
1986
- CONSTRAINT "fk_返品明細_商品"
1987
- FOREIGN KEY ("商品コード") REFERENCES "商品マスタ"("商品コード"),
1988
- CONSTRAINT "uq_返品明細_行番号" UNIQUE ("返品ID", "行番号")
1989
- );
1990
-
1991
- -- インデックス
1992
- CREATE INDEX "idx_返品データ_売上ID" ON "返品データ"("売上ID");
1993
- CREATE INDEX "idx_返品データ_返品日" ON "返品データ"("返品日");
1994
- ```
1995
-
1996
- </details>
1997
-
1998
- ### 赤黒処理の考え方
1999
-
2000
- 赤黒処理とは、誤った伝票を訂正するための会計処理手法です。
2001
-
2002
- ```plantuml
2003
- @startuml
2004
-
2005
- title 赤黒処理の流れ
2006
-
2007
- participant "元伝票" as original
2008
- participant "赤伝票" as red
2009
- participant "黒伝票" as black
2010
-
2011
- note over original
2012
- 誤った内容の伝票
2013
- 金額: +10,000円
2014
- end note
2015
-
2016
- original -> red : 取消処理
2017
- note over red
2018
- 赤伝票(マイナス)
2019
- 金額: -10,000円
2020
- end note
2021
-
2022
- red -> black : 訂正処理
2023
- note over black
2024
- 黒伝票(正しい内容)
2025
- 金額: +12,000円
2026
- end note
2027
-
2028
- note over original, black
2029
- 最終結果:
2030
- 10,000 - 10,000 + 12,000 = 12,000円
2031
- end note
2032
-
2033
- @enduml
2034
- ```
2035
-
2036
- #### 赤黒処理の特徴
2037
-
2038
- | 項目 | 説明 |
2039
- |-----|------|
2040
- | **元伝票** | 訂正対象となる誤った伝票 |
2041
- | **赤伝票** | 元伝票をマイナスで打ち消す伝票 |
2042
- | **黒伝票** | 正しい内容を記載した新しい伝票 |
2043
-
2044
- #### 赤黒処理のメリット
2045
-
2046
- - **監査証跡の保持**: 元の伝票を削除せず、訂正の履歴が残る
2047
- - **会計基準への準拠**: 伝票の修正ではなく、新規伝票で訂正
2048
- - **トレーサビリティ**: いつ、誰が、なぜ訂正したかを追跡可能
2049
-
2050
- ---
2051
-
2052
- ## 6.4 全体の ER 図
2053
-
2054
- 受注から売上までの全体的な関連を示します。
2055
-
2056
- ```plantuml
2057
- @startuml
2058
-
2059
- title 受注・出荷・売上の全体ER図
2060
-
2061
- entity 顧客マスタ {
2062
- 顧客コード <<PK>>
2063
- --
2064
- 顧客名
2065
- ...
2066
- }
2067
-
2068
- entity 商品マスタ {
2069
- 商品コード <<PK>>
2070
- --
2071
- 商品名
2072
- 単価
2073
- ...
2074
- }
2075
-
2076
- entity 見積データ {
2077
- ID <<PK>>
2078
- 見積番号 <<UK>>
2079
- --
2080
- 見積日
2081
- 顧客コード <<FK>>
2082
- ステータス
2083
- ...
2084
- }
2085
-
2086
- entity 見積明細 {
2087
- ID <<PK>>
2088
- --
2089
- 見積ID <<FK>>
2090
- 商品コード <<FK>>
2091
- 数量
2092
- 単価
2093
- ...
2094
- }
2095
-
2096
- entity 受注データ {
2097
- ID <<PK>>
2098
- 受注番号 <<UK>>
2099
- --
2100
- 受注日
2101
- 顧客コード <<FK>>
2102
- 見積ID <<FK>>
2103
- ステータス
2104
- ...
2105
- }
2106
-
2107
- entity 受注明細 {
2108
- ID <<PK>>
2109
- --
2110
- 受注ID <<FK>>
2111
- 商品コード <<FK>>
2112
- 受注数量
2113
- 引当数量
2114
- 出荷数量
2115
- 残数量
2116
- ...
2117
- }
2118
-
2119
- entity 出荷指示データ {
2120
- ID <<PK>>
2121
- 出荷指示番号 <<UK>>
2122
- --
2123
- 受注ID <<FK>>
2124
- 顧客コード <<FK>>
2125
- ステータス
2126
- ...
2127
- }
2128
-
2129
- entity 出荷指示明細 {
2130
- ID <<PK>>
2131
- --
2132
- 出荷指示ID <<FK>>
2133
- 受注明細ID <<FK>>
2134
- 商品コード <<FK>>
2135
- 指示数量
2136
- 出荷数量
2137
- ...
2138
- }
2139
-
2140
- entity 売上データ {
2141
- ID <<PK>>
2142
- 売上番号 <<UK>>
2143
- --
2144
- 受注ID <<FK>>
2145
- 出荷指示ID <<FK>>
2146
- 顧客コード <<FK>>
2147
- ステータス
2148
- ...
2149
- }
2150
-
2151
- entity 売上明細 {
2152
- ID <<PK>>
2153
- --
2154
- 売上ID <<FK>>
2155
- 受注明細ID <<FK>>
2156
- 商品コード <<FK>>
2157
- 売上数量
2158
- 原価
2159
- 粗利
2160
- ...
2161
- }
2162
-
2163
- entity 返品データ {
2164
- ID <<PK>>
2165
- 返品番号 <<UK>>
2166
- --
2167
- 売上ID <<FK>>
2168
- 顧客コード <<FK>>
2169
- 返品理由
2170
- ...
2171
- }
2172
-
2173
- entity 返品明細 {
2174
- ID <<PK>>
2175
- --
2176
- 返品ID <<FK>>
2177
- 売上明細ID <<FK>>
2178
- 商品コード <<FK>>
2179
- 返品数量
2180
- ...
2181
- }
2182
-
2183
- 顧客マスタ ||--o{ 見積データ
2184
- 顧客マスタ ||--o{ 受注データ
2185
- 顧客マスタ ||--o{ 出荷指示データ
2186
- 顧客マスタ ||--o{ 売上データ
2187
- 顧客マスタ ||--o{ 返品データ
2188
-
2189
- 商品マスタ ||--o{ 見積明細
2190
- 商品マスタ ||--o{ 受注明細
2191
- 商品マスタ ||--o{ 出荷指示明細
2192
- 商品マスタ ||--o{ 売上明細
2193
- 商品マスタ ||--o{ 返品明細
2194
-
2195
- 見積データ ||--o{ 見積明細
2196
- 見積データ ||--o| 受注データ : "受注化"
2197
-
2198
- 受注データ ||--o{ 受注明細
2199
- 受注データ ||--o{ 出荷指示データ
2200
- 受注データ ||--o{ 売上データ
2201
-
2202
- 受注明細 ||--o{ 出荷指示明細
2203
- 受注明細 ||--o{ 売上明細
2204
-
2205
- 出荷指示データ ||--o{ 出荷指示明細
2206
- 出荷指示データ ||--o| 売上データ
2207
-
2208
- 売上データ ||--o{ 売上明細
2209
- 売上データ ||--o{ 返品データ
2210
-
2211
- 売上明細 ||--o{ 返品明細
2212
-
2213
- 返品データ ||--o{ 返品明細
2214
-
2215
- @enduml
2216
- ```
2217
-
2218
- ---
2219
-
2220
- ## 6.5 リレーションと楽観ロックの設計
2221
-
2222
- ### MyBatis ネストした ResultMap によるリレーション設定
2223
-
2224
- 受注データは、受注(ヘッダ)→ 受注明細の2層構造を持ちます。MyBatis でこの親子関係を効率的に取得するためのリレーション設定を実装します。
2225
-
2226
- #### ネストした ResultMap の定義
2227
-
2228
- <details>
2229
- <summary>SalesOrderMapper.xml(リレーション設定)</summary>
2230
-
2231
- ```xml
2232
- <?xml version="1.0" encoding="UTF-8" ?>
2233
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
2234
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
2235
-
2236
- <!-- src/main/resources/mapper/SalesOrderMapper.xml -->
2237
- <mapper namespace="com.example.sms.infrastructure.persistence.mapper.SalesOrderMapper">
2238
-
2239
- <!-- 受注(ヘッダ)の ResultMap -->
2240
- <resultMap id="salesOrderWithDetailsResultMap" type="com.example.sms.domain.model.sales.SalesOrder">
2241
- <id property="id" column="o_id"/>
2242
- <result property="orderNumber" column="o_受注番号"/>
2243
- <result property="orderDate" column="o_受注日"/>
2244
- <result property="customerCode" column="o_顧客コード"/>
2245
- <result property="shipToCode" column="o_出荷先コード"/>
2246
- <result property="salesRepCode" column="o_担当者コード"/>
2247
- <result property="requestedDeliveryDate" column="o_希望納期"/>
2248
- <result property="scheduledShipDate" column="o_出荷予定日"/>
2249
- <result property="subtotal" column="o_受注金額"/>
2250
- <result property="taxAmount" column="o_消費税額"/>
2251
- <result property="totalAmount" column="o_受注合計"/>
2252
- <result property="status" column="o_ステータス"
2253
- typeHandler="com.example.sms.infrastructure.out.persistence.typehandler.OrderStatusTypeHandler"/>
2254
- <result property="version" column="o_バージョン"/>
2255
- <result property="createdAt" column="o_作成日時"/>
2256
- <result property="updatedAt" column="o_更新日時"/>
2257
- <!-- 受注明細との1:N関連 -->
2258
- <collection property="details" ofType="com.example.sms.domain.model.sales.SalesOrderDetail"
2259
- resultMap="salesOrderDetailNestedResultMap"/>
2260
- </resultMap>
2261
-
2262
- <!-- 受注明細のネスト ResultMap -->
2263
- <resultMap id="salesOrderDetailNestedResultMap" type="com.example.sms.domain.model.sales.SalesOrderDetail">
2264
- <id property="id" column="d_id"/>
2265
- <result property="orderId" column="d_受注ID"/>
2266
- <result property="lineNumber" column="d_行番号"/>
2267
- <result property="productCode" column="d_商品コード"/>
2268
- <result property="productName" column="d_商品名"/>
2269
- <result property="orderQuantity" column="d_受注数量"/>
2270
- <result property="allocatedQuantity" column="d_引当数量"/>
2271
- <result property="shippedQuantity" column="d_出荷数量"/>
2272
- <result property="remainingQuantity" column="d_残数量"/>
2273
- <result property="unit" column="d_単位"/>
2274
- <result property="unitPrice" column="d_単価"/>
2275
- <result property="amount" column="d_金額"/>
2276
- <result property="taxCategory" column="d_税区分"
2277
- typeHandler="com.example.sms.infrastructure.out.persistence.typehandler.TaxCategoryTypeHandler"/>
2278
- <result property="taxRate" column="d_消費税率"/>
2279
- <result property="taxAmount" column="d_消費税額"/>
2280
- <result property="version" column="d_バージョン"/>
2281
- </resultMap>
2282
-
2283
- <!-- JOIN による一括取得クエリ -->
2284
- <select id="findWithDetailsByOrderNumber" resultMap="salesOrderWithDetailsResultMap">
2285
- SELECT
2286
- o."ID" AS o_id,
2287
- o."受注番号" AS o_受注番号,
2288
- o."受注日" AS o_受注日,
2289
- o."顧客コード" AS o_顧客コード,
2290
- o."出荷先コード" AS o_出荷先コード,
2291
- o."担当者コード" AS o_担当者コード,
2292
- o."希望納期" AS o_希望納期,
2293
- o."出荷予定日" AS o_出荷予定日,
2294
- o."受注金額" AS o_受注金額,
2295
- o."消費税額" AS o_消費税額,
2296
- o."受注合計" AS o_受注合計,
2297
- o."ステータス" AS o_ステータス,
2298
- o."バージョン" AS o_バージョン,
2299
- o."作成日時" AS o_作成日時,
2300
- o."更新日時" AS o_更新日時,
2301
- d."ID" AS d_id,
2302
- d."受注ID" AS d_受注ID,
2303
- d."行番号" AS d_行番号,
2304
- d."商品コード" AS d_商品コード,
2305
- d."商品名" AS d_商品名,
2306
- d."受注数量" AS d_受注数量,
2307
- d."引当数量" AS d_引当数量,
2308
- d."出荷数量" AS d_出荷数量,
2309
- d."残数量" AS d_残数量,
2310
- d."単位" AS d_単位,
2311
- d."単価" AS d_単価,
2312
- d."金額" AS d_金額,
2313
- d."税区分" AS d_税区分,
2314
- d."消費税率" AS d_消費税率,
2315
- d."消費税額" AS d_消費税額,
2316
- d."バージョン" AS d_バージョン
2317
- FROM "受注データ" o
2318
- LEFT JOIN "受注明細" d
2319
- ON o."ID" = d."受注ID"
2320
- WHERE o."受注番号" = #{orderNumber}
2321
- ORDER BY d."行番号"
2322
- </select>
2323
-
2324
- </mapper>
2325
- ```
2326
-
2327
- </details>
2328
-
2329
- #### リレーション設定のポイント
2330
-
2331
- | 設定項目 | 説明 |
2332
- |---------|------|
2333
- | `<collection>` | 1:N 関連のマッピング |
2334
- | `<id>` | 主キーの識別(MyBatis が重複排除に使用) |
2335
- | `resultMap` | ネストした ResultMap の参照 |
2336
- | エイリアス(AS) | カラム名の重複を避けるためのプレフィックス |
2337
- | `ORDER BY` | コレクションの順序を保証 |
2338
-
2339
- ### 楽観ロックの実装
2340
-
2341
- 複数ユーザーが同時に受注データを編集する場合、データの整合性を保つために楽観ロック(Optimistic Locking)を実装します。
2342
-
2343
- #### Flyway マイグレーション: バージョンカラム追加
2344
-
2345
- <details>
2346
- <summary>V010__add_version_columns.sql</summary>
2347
-
2348
- ```sql
2349
- -- src/main/resources/db/migration/V010__add_version_columns.sql
2350
-
2351
- -- 受注データテーブルにバージョンカラムを追加
2352
- ALTER TABLE "受注データ" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2353
-
2354
- -- 受注明細テーブルにバージョンカラムを追加
2355
- ALTER TABLE "受注明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2356
-
2357
- -- 出荷指示データテーブルにバージョンカラムを追加
2358
- ALTER TABLE "出荷指示データ" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2359
-
2360
- -- 出荷指示明細テーブルにバージョンカラムを追加
2361
- ALTER TABLE "出荷指示明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2362
-
2363
- -- 売上データテーブルにバージョンカラムを追加
2364
- ALTER TABLE "売上データ" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2365
-
2366
- -- 売上明細テーブルにバージョンカラムを追加
2367
- ALTER TABLE "売上明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2368
-
2369
- -- 返品データテーブルにバージョンカラムを追加
2370
- ALTER TABLE "返品データ" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2371
-
2372
- -- 返品明細テーブルにバージョンカラムを追加
2373
- ALTER TABLE "返品明細" ADD COLUMN "バージョン" INTEGER DEFAULT 1 NOT NULL;
2374
-
2375
- -- コメント追加
2376
- COMMENT ON COLUMN "受注データ"."バージョン" IS '楽観ロック用バージョン番号';
2377
- COMMENT ON COLUMN "受注明細"."バージョン" IS '楽観ロック用バージョン番号';
2378
- COMMENT ON COLUMN "出荷指示データ"."バージョン" IS '楽観ロック用バージョン番号';
2379
- COMMENT ON COLUMN "出荷指示明細"."バージョン" IS '楽観ロック用バージョン番号';
2380
- COMMENT ON COLUMN "売上データ"."バージョン" IS '楽観ロック用バージョン番号';
2381
- COMMENT ON COLUMN "売上明細"."バージョン" IS '楽観ロック用バージョン番号';
2382
- COMMENT ON COLUMN "返品データ"."バージョン" IS '楽観ロック用バージョン番号';
2383
- COMMENT ON COLUMN "返品明細"."バージョン" IS '楽観ロック用バージョン番号';
2384
- ```
2385
-
2386
- </details>
2387
-
2388
- #### エンティティへのバージョンフィールド追加
2389
-
2390
- <details>
2391
- <summary>SalesOrder.java(バージョンフィールド追加)</summary>
2392
-
2393
- ```java
2394
- // src/main/java/com/example/sms/domain/model/sales/SalesOrder.java
2395
- package com.example.sms.domain.model.sales;
2396
-
2397
- import lombok.AllArgsConstructor;
2398
- import lombok.Builder;
2399
- import lombok.Data;
2400
- import lombok.NoArgsConstructor;
2401
-
2402
- import java.math.BigDecimal;
2403
- import java.time.LocalDate;
2404
- import java.time.LocalDateTime;
2405
- import java.util.ArrayList;
2406
- import java.util.List;
2407
-
2408
- @Data
2409
- @Builder
2410
- @NoArgsConstructor
2411
- @AllArgsConstructor
2412
- public class SalesOrder {
2413
- private Integer id;
2414
- private String orderNumber;
2415
- private LocalDate orderDate;
2416
- private String customerCode;
2417
- private String shipToCode;
2418
- private String salesRepCode;
2419
- private LocalDate requestedDeliveryDate;
2420
- private LocalDate scheduledShipDate;
2421
- private BigDecimal subtotal;
2422
- private BigDecimal taxAmount;
2423
- private BigDecimal totalAmount;
2424
- private OrderStatus status;
2425
- private Integer quotationId;
2426
- private String customerOrderNumber;
2427
- private String remarks;
2428
- private LocalDateTime createdAt;
2429
- private String createdBy;
2430
- private LocalDateTime updatedAt;
2431
- private String updatedBy;
2432
-
2433
- // 楽観ロック用バージョン
2434
- @Builder.Default
2435
- private Integer version = 1;
2436
-
2437
- // リレーション
2438
- @Builder.Default
2439
- private List<SalesOrderDetail> details = new ArrayList<>();
2440
- }
2441
- ```
2442
-
2443
- </details>
2444
-
2445
- #### MyBatis Mapper: 楽観ロック対応の更新
2446
-
2447
- <details>
2448
- <summary>SalesOrderMapper.xml(楽観ロック対応 UPDATE)</summary>
2449
-
2450
- ```xml
2451
- <!-- 楽観ロック対応の更新(バージョンチェック付き) -->
2452
- <update id="updateWithOptimisticLock" parameterType="com.example.sms.domain.model.sales.SalesOrder">
2453
- UPDATE "受注データ"
2454
- SET
2455
- "受注日" = #{orderDate},
2456
- "顧客コード" = #{customerCode},
2457
- "出荷先コード" = #{shipToCode},
2458
- "担当者コード" = #{salesRepCode},
2459
- "希望納期" = #{requestedDeliveryDate},
2460
- "出荷予定日" = #{scheduledShipDate},
2461
- "受注金額" = #{subtotal},
2462
- "消費税額" = #{taxAmount},
2463
- "受注合計" = #{totalAmount},
2464
- "ステータス" = #{status, typeHandler=com.example.sms.infrastructure.out.persistence.typehandler.OrderStatusTypeHandler},
2465
- "更新日時" = CURRENT_TIMESTAMP,
2466
- "バージョン" = "バージョン" + 1
2467
- WHERE "ID" = #{id}
2468
- AND "バージョン" = #{version}
2469
- </update>
2470
- ```
2471
-
2472
- </details>
2473
-
2474
- #### 楽観ロック例外クラス
2475
-
2476
- <details>
2477
- <summary>OptimisticLockException.java</summary>
2478
-
2479
- ```java
2480
- // src/main/java/com/example/sms/domain/exception/OptimisticLockException.java
2481
- package com.example.sms.domain.exception;
2482
-
2483
- public class OptimisticLockException extends RuntimeException {
2484
-
2485
- private final String entityName;
2486
- private final Integer entityId;
2487
- private final Integer expectedVersion;
2488
- private final Integer actualVersion;
2489
-
2490
- public OptimisticLockException(String entityName, Integer entityId) {
2491
- super(String.format("%s (ID: %d) は既に削除されています", entityName, entityId));
2492
- this.entityName = entityName;
2493
- this.entityId = entityId;
2494
- this.expectedVersion = null;
2495
- this.actualVersion = null;
2496
- }
2497
-
2498
- public OptimisticLockException(String entityName, Integer entityId,
2499
- Integer expectedVersion, Integer actualVersion) {
2500
- super(String.format("%s (ID: %d) は他のユーザーによって更新されています。" +
2501
- "期待バージョン: %d, 実際のバージョン: %d",
2502
- entityName, entityId, expectedVersion, actualVersion));
2503
- this.entityName = entityName;
2504
- this.entityId = entityId;
2505
- this.expectedVersion = expectedVersion;
2506
- this.actualVersion = actualVersion;
2507
- }
2508
-
2509
- public String getEntityName() {
2510
- return entityName;
2511
- }
2512
-
2513
- public Integer getEntityId() {
2514
- return entityId;
2515
- }
2516
-
2517
- public Integer getExpectedVersion() {
2518
- return expectedVersion;
2519
- }
2520
-
2521
- public Integer getActualVersion() {
2522
- return actualVersion;
2523
- }
2524
- }
2525
- ```
2526
-
2527
- </details>
2528
-
2529
- #### Repository 実装: 楽観ロック対応
2530
-
2531
- <details>
2532
- <summary>SalesOrderRepositoryImpl.java(楽観ロック対応)</summary>
2533
-
2534
- ```java
2535
- // src/main/java/com/example/sms/infrastructure/persistence/repository/SalesOrderRepositoryImpl.java
2536
- package com.example.sms.infrastructure.persistence.repository;
2537
-
2538
- import com.example.sms.application.port.out.SalesOrderRepository;
2539
- import com.example.sms.domain.exception.OptimisticLockException;
2540
- import com.example.sms.domain.model.sales.OrderStatus;
2541
- import com.example.sms.domain.model.sales.SalesOrder;
2542
- import com.example.sms.domain.model.sales.SalesOrderDetail;
2543
- import com.example.sms.infrastructure.persistence.mapper.SalesOrderMapper;
2544
- import lombok.RequiredArgsConstructor;
2545
- import org.springframework.stereotype.Repository;
2546
- import org.springframework.transaction.annotation.Transactional;
2547
-
2548
- import java.math.BigDecimal;
2549
- import java.time.LocalDate;
2550
- import java.util.List;
2551
- import java.util.Optional;
2552
-
2553
- @Repository
2554
- @RequiredArgsConstructor
2555
- public class SalesOrderRepositoryImpl implements SalesOrderRepository {
2556
-
2557
- private final SalesOrderMapper mapper;
2558
-
2559
- @Override
2560
- @Transactional
2561
- public void update(SalesOrder order) {
2562
- int updatedCount = mapper.updateWithOptimisticLock(order);
2563
-
2564
- if (updatedCount == 0) {
2565
- // バージョン不一致または削除済み
2566
- Integer currentVersion = mapper.findVersionById(order.getId());
2567
- if (currentVersion == null) {
2568
- throw new OptimisticLockException("受注", order.getId());
2569
- } else {
2570
- throw new OptimisticLockException("受注", order.getId(),
2571
- order.getVersion(), currentVersion);
2572
- }
2573
- }
2574
- }
2575
-
2576
- @Override
2577
- public Optional<SalesOrder> findByIdWithDetails(Integer id) {
2578
- return Optional.ofNullable(mapper.findByIdWithDetails(id));
2579
- }
2580
-
2581
- @Override
2582
- public Optional<SalesOrder> findByOrderNumber(String orderNumber) {
2583
- return Optional.ofNullable(mapper.findByOrderNumber(orderNumber));
2584
- }
2585
-
2586
- // その他のメソッド...
2587
- }
2588
- ```
2589
-
2590
- </details>
2591
-
2592
- #### TDD: 楽観ロックのテスト
2593
-
2594
- <details>
2595
- <summary>SalesOrderRepositoryOptimisticLockTest.java</summary>
2596
-
2597
- ```java
2598
- // src/test/java/com/example/sms/infrastructure/persistence/repository/SalesOrderRepositoryOptimisticLockTest.java
2599
- package com.example.sms.infrastructure.persistence.repository;
2600
-
2601
- import com.example.sms.application.port.out.SalesOrderRepository;
2602
- import com.example.sms.domain.exception.OptimisticLockException;
2603
- import com.example.sms.domain.model.sales.OrderStatus;
2604
- import com.example.sms.domain.model.sales.SalesOrder;
2605
- import com.example.sms.testsetup.BaseIntegrationTest;
2606
- import org.junit.jupiter.api.*;
2607
- import org.springframework.beans.factory.annotation.Autowired;
2608
-
2609
- import java.math.BigDecimal;
2610
- import java.time.LocalDate;
2611
-
2612
- import static org.assertj.core.api.Assertions.*;
2613
-
2614
- @DisplayName("受注リポジトリ - 楽観ロック")
2615
- class SalesOrderRepositoryOptimisticLockTest extends BaseIntegrationTest {
2616
-
2617
- @Autowired
2618
- private SalesOrderRepository salesOrderRepository;
2619
-
2620
- @BeforeEach
2621
- void setUp() {
2622
- salesOrderRepository.deleteAll();
2623
- }
2624
-
2625
- @Nested
2626
- @DisplayName("楽観ロック")
2627
- class OptimisticLocking {
2628
-
2629
- @Test
2630
- @DisplayName("同じバージョンで更新できる")
2631
- void canUpdateWithSameVersion() {
2632
- // Arrange
2633
- var order = SalesOrder.builder()
2634
- .orderNumber("SO-2025-0001")
2635
- .orderDate(LocalDate.of(2025, 1, 20))
2636
- .customerCode("CUST-001")
2637
- .subtotal(new BigDecimal("50000"))
2638
- .taxAmount(new BigDecimal("5000"))
2639
- .totalAmount(new BigDecimal("55000"))
2640
- .status(OrderStatus.RECEIVED)
2641
- .build();
2642
- salesOrderRepository.save(order);
2643
-
2644
- // Act
2645
- var fetched = salesOrderRepository.findByOrderNumber("SO-2025-0001").get();
2646
- fetched.setSubtotal(new BigDecimal("60000"));
2647
- salesOrderRepository.update(fetched);
2648
-
2649
- // Assert
2650
- var updated = salesOrderRepository.findByOrderNumber("SO-2025-0001").get();
2651
- assertThat(updated.getSubtotal()).isEqualByComparingTo(new BigDecimal("60000"));
2652
- assertThat(updated.getVersion()).isEqualTo(2); // バージョンがインクリメント
2653
- }
2654
-
2655
- @Test
2656
- @DisplayName("異なるバージョンで更新すると楽観ロック例外が発生する")
2657
- void throwsExceptionWhenVersionMismatch() {
2658
- // Arrange
2659
- var order = SalesOrder.builder()
2660
- .orderNumber("SO-2025-0002")
2661
- .orderDate(LocalDate.of(2025, 1, 20))
2662
- .customerCode("CUST-001")
2663
- .subtotal(new BigDecimal("50000"))
2664
- .taxAmount(new BigDecimal("5000"))
2665
- .totalAmount(new BigDecimal("55000"))
2666
- .status(OrderStatus.RECEIVED)
2667
- .build();
2668
- salesOrderRepository.save(order);
2669
-
2670
- // ユーザーAが取得
2671
- var orderA = salesOrderRepository.findByOrderNumber("SO-2025-0002").get();
2672
- // ユーザーBが取得
2673
- var orderB = salesOrderRepository.findByOrderNumber("SO-2025-0002").get();
2674
-
2675
- // ユーザーAが更新(成功)
2676
- orderA.setSubtotal(new BigDecimal("60000"));
2677
- salesOrderRepository.update(orderA);
2678
-
2679
- // Act & Assert: ユーザーBが古いバージョンで更新(失敗)
2680
- orderB.setSubtotal(new BigDecimal("70000"));
2681
- assertThatThrownBy(() -> salesOrderRepository.update(orderB))
2682
- .isInstanceOf(OptimisticLockException.class)
2683
- .hasMessageContaining("他のユーザーによって更新されています");
2684
- }
2685
- }
2686
- }
2687
- ```
2688
-
2689
- </details>
2690
-
2691
- #### 楽観ロックのベストプラクティス
2692
-
2693
- | ポイント | 説明 |
2694
- |---------|------|
2695
- | **バージョンカラム** | INTEGER 型で十分(オーバーフローは実用上問題なし) |
2696
- | **WHERE 条件** | 必ず `AND "バージョン" = #{version}` を含める |
2697
- | **インクリメント** | `"バージョン" = "バージョン" + 1` でアトミックに更新 |
2698
- | **例外処理** | 更新件数が0の場合は楽観ロック例外をスロー |
2699
- | **リトライ** | 必要に応じて再取得・再更新のリトライロジックを実装 |
2700
-
2701
- ---
2702
-
2703
- ## まとめ
2704
-
2705
- 本章では、販売管理システムの中核となるトランザクションデータの設計を行いました。
2706
-
2707
- ### 設計のポイント
2708
-
2709
- 1. **ヘッダ・明細構造**: すべてのトランザクションデータは、ヘッダ(親)と明細(子)の1対多構造を持つ
2710
-
2711
- 2. **ステータス管理**: 各トランザクションは状態遷移を持ち、PostgreSQL の ENUM 型で管理
2712
-
2713
- 3. **トレーサビリティ**: 見積→受注→出荷→売上の流れを追跡可能な外部キー設計
2714
-
2715
- 4. **数量管理**: 受注明細では受注数量・引当数量・出荷数量・残数量を分離管理
2716
-
2717
- 5. **返品・赤黒処理**: 売上の訂正は削除ではなく、返品データによる追記で対応
2718
-
2719
- 6. **リレーション設定**: MyBatis のネスト ResultMap で親子関係を効率的に取得
2720
-
2721
- 7. **楽観ロック**: バージョンカラムによる同時更新制御で整合性を保証
2722
-
2723
- ### 次章の予告
2724
-
2725
- 第7章では、売上データを起点とした債権管理(請求・入金)の設計を行います。