@things-factory/ops-master 10.1.34 → 10.1.36

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 (151) hide show
  1. package/dist-client/master-change-approval.d.ts +7 -0
  2. package/dist-client/master-change-approval.js +16 -0
  3. package/dist-client/master-change-approval.js.map +1 -1
  4. package/dist-client/pages/master/applied-when-dialog.d.ts +45 -0
  5. package/dist-client/pages/master/applied-when-dialog.js +139 -0
  6. package/dist-client/pages/master/applied-when-dialog.js.map +1 -0
  7. package/dist-client/pages/master/assignment-fields.d.ts +28 -0
  8. package/dist-client/pages/master/assignment-fields.js +55 -0
  9. package/dist-client/pages/master/assignment-fields.js.map +1 -0
  10. package/dist-client/pages/master/equipment.js +1 -0
  11. package/dist-client/pages/master/equipment.js.map +1 -1
  12. package/dist-client/pages/master/master-page-base.d.ts +14 -0
  13. package/dist-client/pages/master/master-page-base.js +40 -4
  14. package/dist-client/pages/master/master-page-base.js.map +1 -1
  15. package/dist-client/pages/master/material-definition.d.ts +4 -11
  16. package/dist-client/pages/master/material-definition.js +5 -53
  17. package/dist-client/pages/master/material-definition.js.map +1 -1
  18. package/dist-client/pages/master/material-revision-fields.d.ts +2 -0
  19. package/dist-client/pages/master/material-revision-fields.js +43 -0
  20. package/dist-client/pages/master/material-revision-fields.js.map +1 -0
  21. package/dist-client/pages/master/operational-location.js +1 -0
  22. package/dist-client/pages/master/operational-location.js.map +1 -1
  23. package/dist-client/pages/master/recipe-definition.d.ts +4 -135
  24. package/dist-client/pages/master/recipe-definition.js +7 -397
  25. package/dist-client/pages/master/recipe-definition.js.map +1 -1
  26. package/dist-client/pages/master/revision-comparison.d.ts +48 -0
  27. package/dist-client/pages/master/revision-comparison.js +553 -0
  28. package/dist-client/pages/master/revision-comparison.js.map +1 -0
  29. package/dist-client/pages/master/revision-view.d.ts +19 -0
  30. package/dist-client/pages/master/revision-view.js +133 -0
  31. package/dist-client/pages/master/revision-view.js.map +1 -0
  32. package/dist-client/pages/master/revision-workspace.d.ts +50 -0
  33. package/dist-client/pages/master/revision-workspace.js +752 -0
  34. package/dist-client/pages/master/revision-workspace.js.map +1 -0
  35. package/dist-client/pages/master/test-specification.d.ts +4 -29
  36. package/dist-client/pages/master/test-specification.js +5 -225
  37. package/dist-client/pages/master/test-specification.js.map +1 -1
  38. package/dist-client/tsconfig.tsbuildinfo +1 -1
  39. package/dist-server/service/assignment/applied-when.d.ts +24 -0
  40. package/dist-server/service/assignment/applied-when.js +16 -0
  41. package/dist-server/service/assignment/applied-when.js.map +1 -0
  42. package/dist-server/service/assignment/assignment-claim.d.ts +111 -0
  43. package/dist-server/service/assignment/assignment-claim.js +160 -0
  44. package/dist-server/service/assignment/assignment-claim.js.map +1 -0
  45. package/dist-server/service/assignment/assignment-history.d.ts +87 -0
  46. package/dist-server/service/assignment/assignment-history.js +250 -0
  47. package/dist-server/service/assignment/assignment-history.js.map +1 -0
  48. package/dist-server/service/assignment/assignment-write.d.ts +25 -0
  49. package/dist-server/service/assignment/assignment-write.js +69 -0
  50. package/dist-server/service/assignment/assignment-write.js.map +1 -0
  51. package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -1
  52. package/dist-server/service/equipment/equipment-master-resolver.js +6 -3
  53. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  54. package/dist-server/service/index.d.ts +15 -2
  55. package/dist-server/service/index.js +13 -0
  56. package/dist-server/service/index.js.map +1 -1
  57. package/dist-server/service/location/location-master-resolver.d.ts +4 -1
  58. package/dist-server/service/location/location-master-resolver.js +6 -3
  59. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  60. package/dist-server/service/master-axis.js +1 -1
  61. package/dist-server/service/master-axis.js.map +1 -1
  62. package/dist-server/service/master-change-policy.js +7 -2
  63. package/dist-server/service/master-change-policy.js.map +1 -1
  64. package/dist-server/service/master-conformance-resolver.js +11 -1
  65. package/dist-server/service/master-conformance-resolver.js.map +1 -1
  66. package/dist-server/service/master-crud.d.ts +11 -0
  67. package/dist-server/service/master-crud.js +15 -1
  68. package/dist-server/service/master-crud.js.map +1 -1
  69. package/dist-server/service/master-list.js +6 -0
  70. package/dist-server/service/master-list.js.map +1 -1
  71. package/dist-server/service/material/index.d.ts +3 -2
  72. package/dist-server/service/material/index.js +4 -2
  73. package/dist-server/service/material/index.js.map +1 -1
  74. package/dist-server/service/material/material-axis-status.d.ts +8 -0
  75. package/dist-server/service/material/material-axis-status.js +19 -0
  76. package/dist-server/service/material/material-axis-status.js.map +1 -0
  77. package/dist-server/service/material/material-definition.d.ts +3 -0
  78. package/dist-server/service/material/material-definition.js +13 -0
  79. package/dist-server/service/material/material-definition.js.map +1 -1
  80. package/dist-server/service/material/material-effective-list.d.ts +39 -0
  81. package/dist-server/service/material/material-effective-list.js +54 -0
  82. package/dist-server/service/material/material-effective-list.js.map +1 -0
  83. package/dist-server/service/material/material-master-resolver.js +4 -10
  84. package/dist-server/service/material/material-master-resolver.js.map +1 -1
  85. package/dist-server/service/material/material-query.js +9 -2
  86. package/dist-server/service/material/material-query.js.map +1 -1
  87. package/dist-server/service/material/material-revision-content.d.ts +10 -0
  88. package/dist-server/service/material/material-revision-content.js +92 -0
  89. package/dist-server/service/material/material-revision-content.js.map +1 -0
  90. package/dist-server/service/material/material-revision-resolver.d.ts +54 -0
  91. package/dist-server/service/material/material-revision-resolver.js +76 -0
  92. package/dist-server/service/material/material-revision-resolver.js.map +1 -0
  93. package/dist-server/service/material/material-revision-service.d.ts +144 -0
  94. package/dist-server/service/material/material-revision-service.js +547 -0
  95. package/dist-server/service/material/material-revision-service.js.map +1 -0
  96. package/dist-server/service/material/material-revision.d.ts +84 -0
  97. package/dist-server/service/material/material-revision.js +339 -0
  98. package/dist-server/service/material/material-revision.js.map +1 -0
  99. package/dist-server/service/operation/operation-definition.js +1 -0
  100. package/dist-server/service/operation/operation-definition.js.map +1 -1
  101. package/dist-server/service/operation/operation-master-resolver.js +2 -5
  102. package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
  103. package/dist-server/service/recipe/index.d.ts +3 -2
  104. package/dist-server/service/recipe/index.js +4 -2
  105. package/dist-server/service/recipe/index.js.map +1 -1
  106. package/dist-server/service/recipe/recipe-definition.d.ts +4 -0
  107. package/dist-server/service/recipe/recipe-definition.js +14 -1
  108. package/dist-server/service/recipe/recipe-definition.js.map +1 -1
  109. package/dist-server/service/recipe/recipe-effective-list.d.ts +49 -0
  110. package/dist-server/service/recipe/recipe-effective-list.js +96 -0
  111. package/dist-server/service/recipe/recipe-effective-list.js.map +1 -0
  112. package/dist-server/service/recipe/recipe-query.js +9 -2
  113. package/dist-server/service/recipe/recipe-query.js.map +1 -1
  114. package/dist-server/service/recipe/recipe-revision-content.d.ts +32 -0
  115. package/dist-server/service/recipe/recipe-revision-content.js +65 -0
  116. package/dist-server/service/recipe/recipe-revision-content.js.map +1 -0
  117. package/dist-server/service/recipe/recipe-revision-line.d.ts +30 -0
  118. package/dist-server/service/recipe/recipe-revision-line.js +95 -0
  119. package/dist-server/service/recipe/recipe-revision-line.js.map +1 -0
  120. package/dist-server/service/recipe/recipe-revision-resolver.d.ts +59 -0
  121. package/dist-server/service/recipe/recipe-revision-resolver.js +76 -0
  122. package/dist-server/service/recipe/recipe-revision-resolver.js.map +1 -0
  123. package/dist-server/service/recipe/recipe-revision-service.d.ts +147 -0
  124. package/dist-server/service/recipe/recipe-revision-service.js +604 -0
  125. package/dist-server/service/recipe/recipe-revision-service.js.map +1 -0
  126. package/dist-server/service/recipe/recipe-revision.d.ts +100 -0
  127. package/dist-server/service/recipe/recipe-revision.js +373 -0
  128. package/dist-server/service/recipe/recipe-revision.js.map +1 -0
  129. package/dist-server/service/recipe/route-definition.js +1 -0
  130. package/dist-server/service/recipe/route-definition.js.map +1 -1
  131. package/dist-server/service/resource/class-members.js +9 -0
  132. package/dist-server/service/resource/class-members.js.map +1 -1
  133. package/dist-server/service/status/master-status.js +2 -1
  134. package/dist-server/service/status/master-status.js.map +1 -1
  135. package/dist-server/service/test-spec/test-spec-effective-list.d.ts +2 -0
  136. package/dist-server/service/test-spec/test-spec-effective-list.js +3 -1
  137. package/dist-server/service/test-spec/test-spec-effective-list.js.map +1 -1
  138. package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +1 -0
  139. package/dist-server/service/test-spec/test-spec-revision-service.d.ts +21 -0
  140. package/dist-server/service/test-spec/test-spec-revision-service.js +33 -0
  141. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -1
  142. package/dist-server/service/test-spec/test-spec-revision.d.ts +2 -0
  143. package/dist-server/service/test-spec/test-spec-revision.js +6 -1
  144. package/dist-server/service/test-spec/test-spec-revision.js.map +1 -1
  145. package/dist-server/service/test-spec/test-specification.d.ts +2 -0
  146. package/dist-server/service/test-spec/test-specification.js +4 -0
  147. package/dist-server/service/test-spec/test-specification.js.map +1 -1
  148. package/dist-server/tsconfig.tsbuildinfo +1 -1
  149. package/package.json +4 -3
  150. package/translations/en.json +148 -0
  151. package/translations/ko.json +148 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-revision.js","sourceRoot":"","sources":["../../../server/service/recipe/recipe-revision.ts"],"names":[],"mappings":";;;;AAAA,qCAA0G;AAC1G,iDAA8C;AAC9C,yDAAgD;AAEhD,kEAA4D;AAC5D,iEAAkF;AAClF,qCAAmC;AAInC,0EAA0E;AAC1E,iFAAiF;AACjF,qEAAqE;AAErE,2FAA2F;AAYpF,IAAM,cAAc,GAApB,MAAM,cAAc;CA4C1B,CAAA;AA5CY,wCAAc;AAEV;IAAd,IAAA,gBAAM,EAAC,KAAK,CAAC;;sDAAuB;AAErC;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,yCAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCACrF,yCAAkB;8CAAA;AAE1B;IADC,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;gDAChC;AACgB;IAA/B,IAAA,gCAAsB,EAAC,MAAM,CAAC;;0CAAW;AAE1C;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCACrC,cAAM;8CAAA;AAEd;IADC,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;gDAChC;AAEhB;IADC,IAAA,mBAAS,EAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;;8CAC/D;AAExB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;gDAChC;AAEhB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCAC1E,cAAc;oDAAO;AAEnC;IADC,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC;;sDACzB;AACd;IAAd,IAAA,gBAAM,EAAC,KAAK,CAAC;;sDAAuB;AACN;IAA9B,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;sDAAuB;AACjB;IAAnC,IAAA,gBAAM,EAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDAA2B;AAC3C;IAAlB,IAAA,gBAAM,EAAC,SAAS,CAAC;;8DAA+B;AACyC;IAAzF,IAAA,mBAAS,EAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;;uDAAoC;AACnE;IAAzD,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC;;yDAA0B;AACG;IAArF,IAAA,mBAAS,EAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;;uDAAwC;AACnE;IAAzD,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC;;yDAAiC;AACnD;IAAtC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CAAqB;AAE3D;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,0CAAmB,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;;8CAC/B;AAE7B;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,2CAAoB,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;;+CAC9B;AACP;IAAvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;sCAAgB,IAAI;qDAAA;AAC3B;IAAf,IAAA,gBAAM,EAAC,MAAM,CAAC;;8CAAe;AAE9B;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCAClC,gBAAI;+CAAA;AAEb;IADC,IAAA,oBAAU,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC;;iDAChC;AACO;IAAvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;sCAAY,IAAI;iDAAA;AACC;IAAvC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAAW,IAAI;gDAAO;AACtB;IAAtC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDAA2B;AAE1B;IAAtC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0DAAkC;yBA3C7D,cAAc;IAX1B,IAAA,gBAAM,EAAC,qBAAqB,CAAC;IAC7B,IAAA,eAAK,EAAC,4BAA4B,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjF,IAAA,eAAK,EAAC,yBAAyB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;IACnE,IAAA,oBAAU,EAAC,oBAAoB,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAC3G,IAAA,oBAAU,EAAC,iBAAiB,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACxG,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,yCAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACtG,IAAA,oBAAU,EAAC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAChG,IAAA,oBAAU,EAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE;QAC9F,QAAQ,EAAE,UAAU;KACrB,CAAC;IACD,IAAA,eAAK,EAAC,2BAA2B,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;GAChF,cAAc,CA4C1B;AAED,yEAAyE;AASlE,IAAM,UAAU,GAAhB,MAAM,UAAU;CA6BtB,CAAA;AA7BY,gCAAU;AAErB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,yCAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCACrF,yCAAkB;0CAAA;AAE1B;IADC,IAAA,oBAAU,EAAC,CAAC,GAAe,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;4CAC5B;AAEhB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCACtE,cAAc;qDAAA;AAEjC;IADC,IAAA,oBAAU,EAAC,CAAC,GAAe,EAAE,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC;;uDAC5B;AACR;IAAlB,IAAA,gBAAM,EAAC,SAAS,CAAC;;+CAAmC;AACT;IAA3C,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCAAY,gBAAI;6CAAA;AACX;IAA/C,IAAA,oBAAU,EAAC,CAAC,GAAe,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;;+CAAoB;AACnC;IAA/B,IAAA,gCAAsB,EAAC,MAAM,CAAC;;sCAAW;AAE1C;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCACrC,cAAM;0CAAA;AAEd;IADC,IAAA,oBAAU,EAAC,CAAC,GAAe,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;4CAC5B;AAEhB;IADC,IAAA,mBAAS,EAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;;0CAC/D;AAExB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAe,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;4CAC5B;AACD;IAAd,IAAA,gBAAM,EAAC,KAAK,CAAC;;8CAAmB;AACT;IAAvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;sCAAa,IAAI;8CAAA;AACD;IAAtC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sDAAkC;AAExE;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAChC,gBAAI;4CAAA;AAEd;IADC,IAAA,oBAAU,EAAC,CAAC,GAAe,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;;8CAC5B;AACC;IAAlB,IAAA,gBAAM,EAAC,SAAS,CAAC;;kDAAuB;qBA5B9B,UAAU;IARtB,IAAA,gBAAM,EAAC,iBAAiB,CAAC;IACzB,IAAA,eAAK,EAAC,2BAA2B,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACxF,IAAA,eAAK,EAAC,yBAAyB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC9E,IAAA,eAAK,EAAC,yBAAyB,EAAE,CAAC,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1E,IAAA,eAAK,EAAC,4BAA4B,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5E,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,yCAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACtG,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACjI,IAAA,oBAAU,EAAC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;GACpF,UAAU,CA6BtB;AASM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAoBzB,CAAA;AApBY,sCAAa;AACQ;IAA/B,IAAA,gCAAsB,EAAC,MAAM,CAAC;;yCAAW;AAE1C;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCACrC,cAAM;6CAAA;AAEd;IADC,IAAA,oBAAU,EAAC,CAAC,GAAkB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;+CAC/B;AAEhB;IADC,IAAA,mBAAS,EAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;;6CAC/D;AAExB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAkB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;+CAC/B;AAEhB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCAC/E,UAAU;2CAAA;AAEhB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAkB,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;;6CAC/B;AAEd;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCAC/E,cAAc;+CAAA;AAExB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAkB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;;iDAC/B;AACM;IAAvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;sCAAY,IAAI;gDAAA;AACC;IAAvC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAAU,IAAI;8CAAO;wBAnBjD,aAAa;IAPzB,IAAA,gBAAM,EAAC,oBAAoB,CAAC;IAC5B,IAAA,eAAK,EAAC,0BAA0B,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5E,IAAA,oBAAU,EAAC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAChG,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAChH,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE;QAC9F,QAAQ,EAAE,UAAU;KACrB,CAAC;GACW,aAAa,CAoBzB;AAED,qFAAqF;AAW9E,IAAM,WAAW,GAAjB,MAAM,WAAW;CA2BvB,CAAA;AA3BY,kCAAW;AAEqB;IAA1C,IAAA,gBAAM,EAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDAElC;AAC+B;IAAtC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CAAyB;AAC/B;IAA/B,IAAA,gCAAsB,EAAC,MAAM,CAAC;;uCAAW;AAE1C;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCACrC,cAAM;2CAAA;AAEd;IADC,IAAA,oBAAU,EAAC,CAAC,GAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;6CAC7B;AAEhB;IADC,IAAA,mBAAS,EAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;;2CAC/D;AAExB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;6CAC7B;AACG;IAAlB,IAAA,gBAAM,EAAC,SAAS,CAAC;;8CAAkB;AAEpC;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCAC/E,cAAc;6CAAA;AAExB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;;+CAC7B;AAElB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,2BAA2B,EAAE,KAAK,EAAE,CAAC;sCAC/E,UAAU;yCAAA;AAEhB;IADC,IAAA,oBAAU,EAAC,CAAC,GAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;;2CAC7B;AACK;IAAlB,IAAA,gBAAM,EAAC,SAAS,CAAC;;gDAAoB;AACd;IAAvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;sCAAe,IAAI;iDAAA;AAClB;IAAvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;sCAAa,IAAI;+CAAA;sBA1B7B,WAAW;IAVvB,IAAA,gBAAM,EAAC,kBAAkB,CAAC;IAC1B,IAAA,eAAK,EAAC,2BAA2B,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACtE,IAAA,eAAK,EAAC,0BAA0B,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjF,IAAA,eAAK,EAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1E,IAAA,eAAK,EAAC,sBAAsB,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;IACnE,IAAA,oBAAU,EAAC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAChG,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAChH,IAAA,oBAAU,EAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE;QAC9F,QAAQ,EAAE,UAAU;KACrB,CAAC;GACW,WAAW,CA2BvB;AAEY,QAAA,wBAAwB,GAAG;IACtC,cAAc;IACd,UAAU;IACV,aAAa;IACb,WAAW;IACX,0CAAmB;IACnB,2CAAoB;CACrB,CAAA","sourcesContent":["import { Column, Entity, ForeignKey, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport type { RecipeDefinition } from './recipe-definition'\nimport { MasterChangePolicy } from '../master-change-policy'\nimport { RecipeRevisionInput, RecipeRevisionOutput } from './recipe-revision-line'\nimport { OneToMany } from 'typeorm'\nimport type { MaterialDefinition } from '../material/material-definition'\nimport type { RouteDefinition } from './route-definition'\n\n// Relations own the columns and RelationId exposes read-only identifiers.\n// Where a composite FK is declared, disable only the redundant single-column FK;\n// the database still enforces existence, domain and recipe together.\n\n/** Content and the proposed effective date freeze together. Worklist owns the decision. */\n@Entity('ops_recipe_revision')\n@Index('ix_recipe_revision_subject', ['domain', 'recipe', 'id'], { unique: true })\n@Index('ix_recipe_revision_list', ['domain', 'recipe', 'createdAt'])\n@ForeignKey('MaterialDefinition', ['domain', 'primaryMaterial'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RouteDefinition', ['domain', 'routeDefinition'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => MasterChangePolicy, ['domain', 'policy'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RecipeRevision', ['domain', 'recipe', 'baseRevision'], ['domain', 'recipe', 'id'], {\n onDelete: 'RESTRICT'\n})\n@Index('ix_recipe_revision_number', ['domain', 'recipe', 'revisionNumber'], { unique: true })\nexport class RecipeRevision {\n /** Human revision number within one identity; assigned once under the subject write lock. */\n @Column('int') revisionNumber: number\n @ManyToOne(() => MasterChangePolicy, { nullable: false, createForeignKeyConstraints: false })\n policy: MasterChangePolicy\n @RelationId((row: RecipeRevision) => row.policy)\n policyId: string\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipeRevision) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipeRevision) => row.recipe)\n recipeId: string\n @ManyToOne(() => RecipeRevision, { nullable: true, createForeignKeyConstraints: false })\n baseRevision: RecipeRevision | null\n @RelationId((row: RecipeRevision) => row.baseRevision)\n baseRevisionId: string | null\n @Column('int') baseGeneration: number\n @Column('int', { default: 0 }) editGeneration: number\n @Column('text', { nullable: true }) description: string | null\n @Column('varchar') materialDefinitionName: string\n @ManyToOne('MaterialDefinition', { nullable: false, createForeignKeyConstraints: false }) primaryMaterial: MaterialDefinition\n @RelationId((row: RecipeRevision) => row.primaryMaterial) primaryMaterialId: string\n @ManyToOne('RouteDefinition', { nullable: true, createForeignKeyConstraints: false }) routeDefinition: RouteDefinition | null\n @RelationId((row: RecipeRevision) => row.routeDefinition) routeDefinitionId: string | null\n @Column('varchar', { nullable: true }) route: string | null\n @OneToMany(() => RecipeRevisionInput, line => line.revision)\n inputs: RecipeRevisionInput[]\n @OneToMany(() => RecipeRevisionOutput, line => line.revision)\n outputs: RecipeRevisionOutput[]\n @Column({ type: Date }) effectiveFrom: Date\n @Column('text') reason: string\n @ManyToOne(() => User, { nullable: false })\n creator: User\n @RelationId((row: RecipeRevision) => row.creator)\n creatorId: string\n @Column({ type: Date }) createdAt: Date\n @Column({ type: Date, nullable: true }) frozenAt: Date | null\n @Column('varchar', { nullable: true }) contentHash: string | null\n /** Opaque product decision ID: the common master module does not depend on worklist. */\n @Column('varchar', { nullable: true }) activityInstanceId: string | null\n}\n\n/** An append-only generation of the complete schedule for one recipe. */\n@Entity('ops_recipe_plan')\n@Index('ix_recipe_plan_generation', ['domain', 'recipe', 'generation'], { unique: true })\n@Index('ix_recipe_plan_identity', ['domain', 'recipe', 'id'], { unique: true })\n@Index('ix_recipe_plan_decision', ['activityInstanceId'], { unique: true })\n@Index('ix_recipe_plan_publication', ['publishedRevision'], { unique: true })\n@ForeignKey(() => MasterChangePolicy, ['domain', 'policy'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipeRevision, ['domain', 'recipe', 'publishedRevision'], ['domain', 'recipe', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\nexport class RecipePlan {\n @ManyToOne(() => MasterChangePolicy, { nullable: false, createForeignKeyConstraints: false })\n policy: MasterChangePolicy\n @RelationId((row: RecipePlan) => row.policy)\n policyId: string\n @ManyToOne(() => RecipeRevision, { nullable: false, createForeignKeyConstraints: false })\n publishedRevision: RecipeRevision\n @RelationId((row: RecipePlan) => row.publishedRevision)\n publishedRevisionId: string\n @Column('varchar') releaseKind: 'approved' | 'policy'\n @ManyToOne(() => User, { nullable: false }) publisher: User\n @RelationId((row: RecipePlan) => row.publisher) publisherId: string\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipePlan) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipePlan) => row.recipe)\n recipeId: string\n @Column('int') generation: number\n @Column({ type: Date }) recordedAt: Date\n @Column('varchar', { nullable: true }) activityInstanceId: string | null\n @ManyToOne(() => User, { nullable: true })\n approver: User\n @RelationId((row: RecipePlan) => row.approver)\n approverId: string\n @Column('varchar') submissionHash: string\n}\n\n@Entity('ops_recipe_segment')\n@Index('ix_recipe_segment_lookup', ['domain', 'recipe', 'plan', 'validFrom'])\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipePlan, ['domain', 'recipe', 'plan'], ['domain', 'recipe', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipeRevision, ['domain', 'recipe', 'revision'], ['domain', 'recipe', 'id'], {\n onDelete: 'RESTRICT'\n})\nexport class RecipeSegment {\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipeSegment) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipeSegment) => row.recipe)\n recipeId: string\n @ManyToOne(() => RecipePlan, { nullable: false, createForeignKeyConstraints: false })\n plan: RecipePlan\n @RelationId((row: RecipeSegment) => row.plan)\n planId: string\n @ManyToOne(() => RecipeRevision, { nullable: false, createForeignKeyConstraints: false })\n revision: RecipeRevision\n @RelationId((row: RecipeSegment) => row.revision)\n revisionId: string\n @Column({ type: Date }) validFrom: Date\n @Column({ type: Date, nullable: true }) validTo: Date | null\n}\n\n/** Permanent evidence of the selected revision, also the retroactivity interlock. */\n@Entity('ops_recipe_basis')\n@Index('ix_recipe_basis_domain_id', ['domain', 'id'], { unique: true })\n@Index('ix_recipe_basis_identity', ['domain', 'id', 'revision'], { unique: true })\n@Index('ix_recipe_basis_record', ['domain', 'recordKey'], { unique: true })\n@Index('ix_recipe_basis_time', ['domain', 'recipe', 'businessTime'])\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipePlan, ['domain', 'recipe', 'plan'], ['domain', 'recipe', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipeRevision, ['domain', 'recipe', 'revision'], ['domain', 'recipe', 'id'], {\n onDelete: 'RESTRICT'\n})\nexport class RecipeBasis {\n /** Execution snapshot of an independently owned route; not a route master revision. */\n @Column('simple-json', { nullable: true }) routeSnapshot: {\n id: string; name: string; steps: string[]; procedure: Record<string, unknown>[] | null\n } | null\n @Column('varchar', { nullable: true }) routeHash: string | null\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipeBasis) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipeBasis) => row.recipe)\n recipeId: string\n @Column('varchar') recordKey: string\n @ManyToOne(() => RecipeRevision, { nullable: false, createForeignKeyConstraints: false })\n revision: RecipeRevision\n @RelationId((row: RecipeBasis) => row.revision)\n revisionId: string\n @ManyToOne(() => RecipePlan, { nullable: false, createForeignKeyConstraints: false })\n plan: RecipePlan\n @RelationId((row: RecipeBasis) => row.plan)\n planId: string\n @Column('varchar') contentHash: string\n @Column({ type: Date }) businessTime: Date\n @Column({ type: Date }) resolvedAt: Date\n}\n\nexport const RECIPE_REVISION_ENTITIES = [\n RecipeRevision,\n RecipePlan,\n RecipeSegment,\n RecipeBasis,\n RecipeRevisionInput,\n RecipeRevisionOutput\n]\n"]}
@@ -107,6 +107,7 @@ tslib_1.__decorate([
107
107
  ], RouteDefinition.prototype, "updaterId", void 0);
108
108
  exports.RouteDefinition = RouteDefinition = tslib_1.__decorate([
109
109
  (0, typeorm_1.Entity)({ name: 'ops_route_definition' }),
110
+ (0, typeorm_1.Index)('ix_ops_route_domain_identity', ['domain', 'id'], { unique: true }),
110
111
  (0, typeorm_1.Index)('ix_ops_route_definition_0', (route) => [route.domain, route.name], { unique: true }),
111
112
  (0, type_graphql_1.ObjectType)({ description: 'Route — the ordered operations a material passes through (kernel RouteDef).' })
112
113
  ], RouteDefinition);
@@ -1 +1 @@
1
- {"version":3,"file":"route-definition.js","sourceRoot":"","sources":["../../../server/service/recipe/route-definition.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 라우팅 — **공정을 지나는 순서**. 커널 `RouteDef` 와 같은 모양이다.\n *\n * ── 왜 마스터에 있나 (2026-08-31) ────────────────────────────────────────\n * 커널에 `RouteDef` 가 있고 `JobOrder.routeKey` 가 그것을 가리키는데, **그 값이 가리킬 표가 없었다.**\n * 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n *\n * 트윈 모델 JSON 에 선언하는 길도 있었는데 그러지 않은 이유가 셋이다.\n *\n * · **유효기간을 담을 자리가 없다.** 라우팅은 개정된다. 모델에 두면 「언제부터 이 라우팅였나」가 없고,\n * 그러면 지난 실적이 **어느 라우팅으로 만든 것인지** 잃는다. 정산과 추적이 그 위에 선다.\n * · **MES 가 트윈 없이 배포된다.** 라우팅이 모델에 살면 MES 가 트윈을 세워야 자기 지시서를 만든다.\n * · **표준이 그 자리에 둔다.** `OperationsDefinition` 은 Level 4·3 의 마스터이고 실행 모델이 아니다.\n *\n * ── 순서의 정본은 `steps` 다 ──────────────────────────────────────────────\n * `procedure`(ISA-88 절차 계층)는 그 위에 이름을 붙이는 것이다. 트리를 따로 두면 두 곳이 어긋날 수\n * 있고, 어긋났을 때 어느 쪽이 맞는지 말할 근거가 없다(§`RouteDef.procedure`).\n */\n@Entity({ name: 'ops_route_definition' })\n@Index('ix_ops_route_definition_0', (route: RouteDefinition) => [route.domain, route.name], { unique: true })\n@ObjectType({ description: 'Route — the ordered operations a material passes through (kernel RouteDef).' })\nexport class RouteDefinition {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((route: RouteDefinition) => route.domain)\n domainId?: string\n\n /** 라우팅 키 — 레시피와 오더가 이 값을 가리킨다(커널 `RouteDef.key`). */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /**\n * 지나는 공정의 **순서** — `OperationDefinition.name` 의 목록(커널 `RouteDef.steps`).\n *\n * 이름으로 가리킨다. 외래키로 묶지 않는 이유는, 공정이 유효기간으로 끝나도 그것을 지나간 지난 기록이\n * 가리킬 것을 잃지 않아야 하기 때문이다 — 마스터의 다른 축도 같은 규율이다.\n */\n @Column({ type: 'simple-json' })\n @Field(type => [String])\n steps: string[]\n\n /** ISA-88 절차 계층 — 선언하지 않으면 `steps` 만으로 돈다(커널 `RouteDef.procedure`). */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'ProcedureElementDef[]' })\n procedure?: Record<string, unknown>[]\n\n /** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /**\n * 언제까지 유효한가 — 표준 `EffectiveEndDate`.\n *\n * **없앨 때 행을 지우지 않고 이 칸을 채운다.** 지우면 그것으로 만든 지난 기록이 가리킬 것을 잃는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((route: RouteDefinition) => route.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((route: RouteDefinition) => route.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"route-definition.js","sourceRoot":"","sources":["../../../server/service/recipe/route-definition.ts"],"names":[],"mappings":";;;;AAAA,qCAAkI;AAClI,+CAAoD;AAEpD,iDAA4D;AAC5D,yDAAgD;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AAKI,IAAM,eAAe,GAArB,MAAM,eAAe;CAuE3B,CAAA;AAvEY,0CAAe;AAGjB;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,CAAC;;2CACC;AAInB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;IACzB,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,CAAC;sCACb,cAAM;+CAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC;;iDACpC;AAKjB;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,oBAAK,GAAE;;6CACI;AAIZ;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACN;AAUpB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC/B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;;8CACT;AAKf;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;kDACjD;AAKrC;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACT,IAAI;IAErB;;;;OAIG;;uDANkB;AASrB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACX,IAAI;qDAAA;AAInB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACd,IAAI;kDAAA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCACd,IAAI;kDAAA;AAIhB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAC9B,gBAAI;gDAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;;kDACpC;AAIlB;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;sCAC9B,gBAAI;gDAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;;kDACpC;0BAtEP,eAAe;IAJ3B,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;IACxC,IAAA,eAAK,EAAC,8BAA8B,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzE,IAAA,eAAK,EAAC,2BAA2B,EAAE,CAAC,KAAsB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5G,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,6EAA6E,EAAE,CAAC;GAC9F,eAAe,CAuE3B","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 라우팅 — **공정을 지나는 순서**. 커널 `RouteDef` 와 같은 모양이다.\n *\n * ── 왜 마스터에 있나 (2026-08-31) ────────────────────────────────────────\n * 커널에 `RouteDef` 가 있고 `JobOrder.routeKey` 가 그것을 가리키는데, **그 값이 가리킬 표가 없었다.**\n * 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n *\n * 트윈 모델 JSON 에 선언하는 길도 있었는데 그러지 않은 이유가 셋이다.\n *\n * · **유효기간을 담을 자리가 없다.** 라우팅은 개정된다. 모델에 두면 「언제부터 이 라우팅였나」가 없고,\n * 그러면 지난 실적이 **어느 라우팅으로 만든 것인지** 잃는다. 정산과 추적이 그 위에 선다.\n * · **MES 가 트윈 없이 배포된다.** 라우팅이 모델에 살면 MES 가 트윈을 세워야 자기 지시서를 만든다.\n * · **표준이 그 자리에 둔다.** `OperationsDefinition` 은 Level 4·3 의 마스터이고 실행 모델이 아니다.\n *\n * ── 순서의 정본은 `steps` 다 ──────────────────────────────────────────────\n * `procedure`(ISA-88 절차 계층)는 그 위에 이름을 붙이는 것이다. 트리를 따로 두면 두 곳이 어긋날 수\n * 있고, 어긋났을 때 어느 쪽이 맞는지 말할 근거가 없다(§`RouteDef.procedure`).\n */\n@Entity({ name: 'ops_route_definition' })\n@Index('ix_ops_route_domain_identity', ['domain', 'id'], { unique: true })\n@Index('ix_ops_route_definition_0', (route: RouteDefinition) => [route.domain, route.name], { unique: true })\n@ObjectType({ description: 'Route — the ordered operations a material passes through (kernel RouteDef).' })\nexport class RouteDefinition {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((route: RouteDefinition) => route.domain)\n domainId?: string\n\n /** 라우팅 키 — 레시피와 오더가 이 값을 가리킨다(커널 `RouteDef.key`). */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /**\n * 지나는 공정의 **순서** — `OperationDefinition.name` 의 목록(커널 `RouteDef.steps`).\n *\n * 이름으로 가리킨다. 외래키로 묶지 않는 이유는, 공정이 유효기간으로 끝나도 그것을 지나간 지난 기록이\n * 가리킬 것을 잃지 않아야 하기 때문이다 — 마스터의 다른 축도 같은 규율이다.\n */\n @Column({ type: 'simple-json' })\n @Field(type => [String])\n steps: string[]\n\n /** ISA-88 절차 계층 — 선언하지 않으면 `steps` 만으로 돈다(커널 `RouteDef.procedure`). */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'ProcedureElementDef[]' })\n procedure?: Record<string, unknown>[]\n\n /** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /**\n * 언제까지 유효한가 — 표준 `EffectiveEndDate`.\n *\n * **없앨 때 행을 지우지 않고 이 칸을 채운다.** 지우면 그것으로 만든 지난 기록이 가리킬 것을 잃는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((route: RouteDefinition) => route.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((route: RouteDefinition) => route.updater)\n updaterId?: string\n}\n"]}
@@ -4,6 +4,8 @@ exports.MEMBER_SCAN_LIMIT = void 0;
4
4
  exports.scanClassMembers = scanClassMembers;
5
5
  const shell_1 = require("@things-factory/shell");
6
6
  const referrers_1 = require("./referrers");
7
+ const material_definition_1 = require("../material/material-definition");
8
+ const material_effective_list_1 = require("../material/material-effective-list");
7
9
  /**
8
10
  * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.
9
11
  *
@@ -34,6 +36,13 @@ exports.MEMBER_SCAN_LIMIT = 2000;
34
36
  */
35
37
  async function scanClassMembers(Entity, field, domain) {
36
38
  const repo = (0, shell_1.getRepository)(Entity);
39
+ if (Entity === material_definition_1.MaterialDefinition) {
40
+ const { items, total } = await (0, material_effective_list_1.listEffectiveMaterialDefinitions)(repo.manager, domain, {
41
+ filters: [{ name: 'revisionNumber', operator: 'is_not_null', value: true }],
42
+ pagination: { page: 1, limit: exports.MEMBER_SCAN_LIMIT }
43
+ });
44
+ return { members: (0, referrers_1.indexMultiReferrers)(items, one => one.materialClassIds ?? [], one => one.name), capped: total > items.length };
45
+ }
37
46
  const total = await repo.count({ where: { domain: { id: domain.id } } });
38
47
  /*
39
48
  * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를
@@ -1 +1 @@
1
- {"version":3,"file":"class-members.js","sourceRoot":"","sources":["../../../server/service/resource/class-members.ts"],"names":[],"mappings":";;;AAyCA,4CAqBC;AA9DD,iDAA6D;AAE7D,2CAAiD;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4CAA4C;AAC/B,QAAA,iBAAiB,GAAG,IAAI,CAAA;AASrC;;;;GAIG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAW,EAAE,KAAa,EAAE,MAAc;IAC/E,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,MAAM,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAE/E;;;OAGG;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAQ;QACpC,IAAI,EAAE,yBAAiB;KACxB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAA,+BAAmB,EACjC,IAAa,EACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EACvB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAChB,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAI,IAAc,CAAC,MAAM,EAAE,CAAA;AAC5D,CAAC","sourcesContent":["import { Domain, getRepository } from '@things-factory/shell'\n\nimport { indexMultiReferrers } from './referrers'\n\n/**\n * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그\n * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다.\n *\n * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.\n *\n * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────\n * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로\n * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면\n * 한쪽만 고쳐지는 날이 온다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무\n * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.\n */\n\n/** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */\nexport const MEMBER_SCAN_LIMIT = 2000\n\nexport interface ClassMembers {\n /** 등급 이름 → 그 등급에 든 것들의 이름. */\n members: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\n/**\n * 소속 표를 훑어 등급별로 뒤집는다.\n *\n * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.\n */\nexport async function scanClassMembers(Entity: any, field: string, domain: Domain): Promise<ClassMembers> {\n const repo = getRepository(Entity)\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를\n * 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', field] as any,\n take: MEMBER_SCAN_LIMIT\n })\n\n const members = indexMultiReferrers(\n rows as any[],\n one => one[field] ?? [],\n one => one.name\n )\n\n return { members, capped: total > (rows as any[]).length }\n}\n"]}
1
+ {"version":3,"file":"class-members.js","sourceRoot":"","sources":["../../../server/service/resource/class-members.ts"],"names":[],"mappings":";;;AA2CA,4CA4BC;AAvED,iDAA6D;AAE7D,2CAAiD;AACjD,yEAAoE;AACpE,iFAAsF;AAEtF;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4CAA4C;AAC/B,QAAA,iBAAiB,GAAG,IAAI,CAAA;AASrC;;;;GAIG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAW,EAAE,KAAa,EAAE,MAAc;IAC/E,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,MAAM,CAAC,CAAA;IAClC,IAAI,MAAM,KAAK,wCAAkB,EAAE,CAAC;QAClC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAA,0DAAgC,EAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE;YACpF,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3E,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,yBAAiB,EAAE;SAC3C,CAAC,CAAA;QACT,OAAO,EAAE,OAAO,EAAE,IAAA,+BAAmB,EAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAA;IAClI,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAE/E;;;OAGG;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAQ;QACpC,IAAI,EAAE,yBAAiB;KACxB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAA,+BAAmB,EACjC,IAAa,EACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EACvB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAChB,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAI,IAAc,CAAC,MAAM,EAAE,CAAA;AAC5D,CAAC","sourcesContent":["import { Domain, getRepository } from '@things-factory/shell'\n\nimport { indexMultiReferrers } from './referrers'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { listEffectiveMaterialDefinitions } from '../material/material-effective-list'\n\n/**\n * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그\n * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다.\n *\n * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.\n *\n * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────\n * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로\n * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면\n * 한쪽만 고쳐지는 날이 온다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무\n * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.\n */\n\n/** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */\nexport const MEMBER_SCAN_LIMIT = 2000\n\nexport interface ClassMembers {\n /** 등급 이름 → 그 등급에 든 것들의 이름. */\n members: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\n/**\n * 소속 표를 훑어 등급별로 뒤집는다.\n *\n * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.\n */\nexport async function scanClassMembers(Entity: any, field: string, domain: Domain): Promise<ClassMembers> {\n const repo = getRepository(Entity)\n if (Entity === MaterialDefinition) {\n const { items, total } = await listEffectiveMaterialDefinitions(repo.manager, domain, {\n filters: [{ name: 'revisionNumber', operator: 'is_not_null', value: true }],\n pagination: { page: 1, limit: MEMBER_SCAN_LIMIT }\n } as any)\n return { members: indexMultiReferrers(items, one => one.materialClassIds ?? [], one => one.name), capped: total > items.length }\n }\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를\n * 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', field] as any,\n take: MEMBER_SCAN_LIMIT\n })\n\n const members = indexMultiReferrers(\n rows as any[],\n one => one[field] ?? [],\n one => one.name\n )\n\n return { members, capped: total > (rows as any[]).length }\n}\n"]}
@@ -11,6 +11,7 @@ const equipment_class_1 = require("../equipment/equipment-class");
11
11
  const operational_location_1 = require("../location/operational-location");
12
12
  const material_class_1 = require("../material/material-class");
13
13
  const material_definition_1 = require("../material/material-definition");
14
+ const material_axis_status_1 = require("../material/material-axis-status");
14
15
  const operation_definition_1 = require("../operation/operation-definition");
15
16
  const person_1 = require("../personnel/person");
16
17
  const personnel_class_1 = require("../personnel/personnel-class");
@@ -105,7 +106,7 @@ let OpsMasterStatusQuery = class OpsMasterStatusQuery {
105
106
  axisStatus('assets', (0, shell_1.getRepository)(physical_asset_1.PhysicalAsset), domain.id),
106
107
  axisStatus('locations', (0, shell_1.getRepository)(operational_location_1.OperationalLocation), domain.id),
107
108
  axisStatus('materialClasses', (0, shell_1.getRepository)(material_class_1.MaterialClass), domain.id),
108
- axisStatus('materialDefinitions', (0, shell_1.getRepository)(material_definition_1.MaterialDefinition), domain.id),
109
+ (0, material_axis_status_1.materialAxisStatus)((0, shell_1.getRepository)(material_definition_1.MaterialDefinition).manager, domain.id),
109
110
  axisStatus('operations', (0, shell_1.getRepository)(operation_definition_1.OperationDefinition), domain.id),
110
111
  axisStatus('recipes', (0, shell_1.getRepository)(recipe_definition_1.RecipeDefinition), domain.id),
111
112
  axisStatus('routes', (0, shell_1.getRepository)(route_definition_1.RouteDefinition), domain.id),
@@ -1 +1 @@
1
- {"version":3,"file":"master-status.js","sourceRoot":"","sources":["../../../server/service/status/master-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\nimport type { ObjectLiteral, Repository } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { PhysicalAssetClass } from '../asset/physical-asset-class'\nimport { Equipment } from '../equipment/equipment'\nimport { EquipmentClass } from '../equipment/equipment-class'\nimport { OperationalLocation } from '../location/operational-location'\nimport { MaterialClass } from '../material/material-class'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { OperationDefinition } from '../operation/operation-definition'\nimport { Person } from '../personnel/person'\nimport { PersonnelClass } from '../personnel/personnel-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { TestSpecification } from '../test-spec/test-specification'\n\nimport { axisStamp } from './axis-stamp'\n\n/**\n * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 트윈은 마스터를 주기적으로 당긴다. 그런데 인원 마스터가 수천 건이면 바뀐 것이 없어도 매번 그만큼을\n * 옮기게 된다. 이 질의는 **축마다 마지막으로 바뀐 시각과 건수만** 내므로, 트윈이 자기가 마지막으로 본\n * 값과 비교해 바뀐 축만 당길 수 있다.\n *\n * ── 왜 지우지 않는가 ────────────────────────────────────────────────────────\n * 건수가 줄지 않는 것을 전제로 한다. 마스터에서 무엇을 「없앨」 때 행을 지우지 않고 유효 기간으로\n * 끝내기 때문이다(§`effectiveEnd`). 지우면 그 개체를 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n@ObjectType({ description: 'When one master axis last changed' })\nexport class MasterAxisStatus {\n @Field({ description: 'axis name, matching the pull query' })\n axis: string\n\n @Field(type => Int, { description: 'number of rows in this axis' })\n count: number\n\n /** 마지막으로 바뀐 시각. 축이 비어 있으면 없다 — 「비어 있다」와 「바뀐 적 없다」를 섞지 않는다. */\n @Field({ nullable: true, description: 'last change time, absent when the axis is empty' })\n changedAt?: Date\n\n /**\n * 이 축의 상태를 대표하는 값 하나 — **크기로 비교하지 말고 같은지만 본다.**\n *\n * 시각을 크기로 비교하면 시계가 되감길 때(백업 복원 · 시각 보정) 바뀐 것을 놓친다. 개수를 함께 담아\n * 두므로 같은 초에 두 번 바뀌어도 표시가 달라진다. 읽는 쪽은 자기가 마지막으로 본 표시와 **다르면**\n * 당기면 된다.\n */\n @Field({ description: 'opaque value — compare for equality, never for order' })\n stamp: string\n}\n\n@ObjectType({ description: 'Change status of every master axis' })\nexport class OpsMasterStatus {\n @Field(type => [MasterAxisStatus])\n axes: MasterAxisStatus[]\n\n}\n\n/** 축 하나의 건수와 마지막 변경 시각. 두 값을 한 질의로 읽는다. */\nasync function axisStatus<T extends ObjectLiteral>(\n axis: string,\n repository: Repository<T>,\n domainId: string\n): Promise<MasterAxisStatus> {\n const row = await repository\n .createQueryBuilder('row')\n .select('COUNT(1)', 'count')\n .addSelect('MAX(row.updatedAt)', 'changedAt')\n .where('row.domain = :domainId', { domainId })\n .getRawOne<{ count: string | number; changedAt: Date | string | null }>()\n\n const count = Number(row?.count ?? 0)\n const changedAt = row?.changedAt ? new Date(row.changedAt) : undefined\n\n return {\n axis,\n count,\n stamp: axisStamp(count, changedAt),\n ...(changedAt ? { changedAt } : {})\n }\n}\n\n\n@Resolver(OpsMasterStatus)\nexport class OpsMasterStatusQuery {\n /**\n * 모든 축의 변경 상태 — 트윈이 주기적으로 부르는 자리.\n *\n * 한 번에 낸다. 축마다 따로 물으면 일곱 번 오가고, 그 사이에 마스터가 바뀌면 축들이 서로 다른 시점을\n * 보게 된다.\n */\n @Query(returns => OpsMasterStatus, { description: 'Change status of every master axis' })\n async opsMasterStatus(@Ctx() context: ResolverContext): Promise<OpsMasterStatus> {\n const { domain } = context.state\n\n const axes = await Promise.all([\n axisStatus('personnelClasses', getRepository(PersonnelClass), domain.id),\n axisStatus('persons', getRepository(Person), domain.id),\n axisStatus('equipmentClasses', getRepository(EquipmentClass), domain.id),\n axisStatus('equipment', getRepository(Equipment), domain.id),\n axisStatus('assetClasses', getRepository(PhysicalAssetClass), domain.id),\n axisStatus('assets', getRepository(PhysicalAsset), domain.id),\n axisStatus('locations', getRepository(OperationalLocation), domain.id),\n axisStatus('materialClasses', getRepository(MaterialClass), domain.id),\n axisStatus('materialDefinitions', getRepository(MaterialDefinition), domain.id),\n axisStatus('operations', getRepository(OperationDefinition), domain.id),\n axisStatus('recipes', getRepository(RecipeDefinition), domain.id),\n axisStatus('routes', getRepository(RouteDefinition), domain.id),\n axisStatus('testSpecifications', getRepository(TestSpecification), domain.id)\n ])\n\n return { axes }\n }\n}\n"]}
1
+ {"version":3,"file":"master-status.js","sourceRoot":"","sources":["../../../server/service/status/master-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\nimport type { ObjectLiteral, Repository } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { PhysicalAssetClass } from '../asset/physical-asset-class'\nimport { Equipment } from '../equipment/equipment'\nimport { EquipmentClass } from '../equipment/equipment-class'\nimport { OperationalLocation } from '../location/operational-location'\nimport { MaterialClass } from '../material/material-class'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { materialAxisStatus } from '../material/material-axis-status'\nimport { OperationDefinition } from '../operation/operation-definition'\nimport { Person } from '../personnel/person'\nimport { PersonnelClass } from '../personnel/personnel-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { TestSpecification } from '../test-spec/test-specification'\n\nimport { axisStamp } from './axis-stamp'\n\n/**\n * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 트윈은 마스터를 주기적으로 당긴다. 그런데 인원 마스터가 수천 건이면 바뀐 것이 없어도 매번 그만큼을\n * 옮기게 된다. 이 질의는 **축마다 마지막으로 바뀐 시각과 건수만** 내므로, 트윈이 자기가 마지막으로 본\n * 값과 비교해 바뀐 축만 당길 수 있다.\n *\n * ── 왜 지우지 않는가 ────────────────────────────────────────────────────────\n * 건수가 줄지 않는 것을 전제로 한다. 마스터에서 무엇을 「없앨」 때 행을 지우지 않고 유효 기간으로\n * 끝내기 때문이다(§`effectiveEnd`). 지우면 그 개체를 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n@ObjectType({ description: 'When one master axis last changed' })\nexport class MasterAxisStatus {\n @Field({ description: 'axis name, matching the pull query' })\n axis: string\n\n @Field(type => Int, { description: 'number of rows in this axis' })\n count: number\n\n /** 마지막으로 바뀐 시각. 축이 비어 있으면 없다 — 「비어 있다」와 「바뀐 적 없다」를 섞지 않는다. */\n @Field({ nullable: true, description: 'last change time, absent when the axis is empty' })\n changedAt?: Date\n\n /**\n * 이 축의 상태를 대표하는 값 하나 — **크기로 비교하지 말고 같은지만 본다.**\n *\n * 시각을 크기로 비교하면 시계가 되감길 때(백업 복원 · 시각 보정) 바뀐 것을 놓친다. 개수를 함께 담아\n * 두므로 같은 초에 두 번 바뀌어도 표시가 달라진다. 읽는 쪽은 자기가 마지막으로 본 표시와 **다르면**\n * 당기면 된다.\n */\n @Field({ description: 'opaque value — compare for equality, never for order' })\n stamp: string\n}\n\n@ObjectType({ description: 'Change status of every master axis' })\nexport class OpsMasterStatus {\n @Field(type => [MasterAxisStatus])\n axes: MasterAxisStatus[]\n\n}\n\n/** 축 하나의 건수와 마지막 변경 시각. 두 값을 한 질의로 읽는다. */\nasync function axisStatus<T extends ObjectLiteral>(\n axis: string,\n repository: Repository<T>,\n domainId: string\n): Promise<MasterAxisStatus> {\n const row = await repository\n .createQueryBuilder('row')\n .select('COUNT(1)', 'count')\n .addSelect('MAX(row.updatedAt)', 'changedAt')\n .where('row.domain = :domainId', { domainId })\n .getRawOne<{ count: string | number; changedAt: Date | string | null }>()\n\n const count = Number(row?.count ?? 0)\n const changedAt = row?.changedAt ? new Date(row.changedAt) : undefined\n\n return {\n axis,\n count,\n stamp: axisStamp(count, changedAt),\n ...(changedAt ? { changedAt } : {})\n }\n}\n\n\n@Resolver(OpsMasterStatus)\nexport class OpsMasterStatusQuery {\n /**\n * 모든 축의 변경 상태 — 트윈이 주기적으로 부르는 자리.\n *\n * 한 번에 낸다. 축마다 따로 물으면 일곱 번 오가고, 그 사이에 마스터가 바뀌면 축들이 서로 다른 시점을\n * 보게 된다.\n */\n @Query(returns => OpsMasterStatus, { description: 'Change status of every master axis' })\n async opsMasterStatus(@Ctx() context: ResolverContext): Promise<OpsMasterStatus> {\n const { domain } = context.state\n\n const axes = await Promise.all([\n axisStatus('personnelClasses', getRepository(PersonnelClass), domain.id),\n axisStatus('persons', getRepository(Person), domain.id),\n axisStatus('equipmentClasses', getRepository(EquipmentClass), domain.id),\n axisStatus('equipment', getRepository(Equipment), domain.id),\n axisStatus('assetClasses', getRepository(PhysicalAssetClass), domain.id),\n axisStatus('assets', getRepository(PhysicalAsset), domain.id),\n axisStatus('locations', getRepository(OperationalLocation), domain.id),\n axisStatus('materialClasses', getRepository(MaterialClass), domain.id),\n materialAxisStatus(getRepository(MaterialDefinition).manager, domain.id),\n axisStatus('operations', getRepository(OperationDefinition), domain.id),\n axisStatus('recipes', getRepository(RecipeDefinition), domain.id),\n axisStatus('routes', getRepository(RouteDefinition), domain.id),\n axisStatus('testSpecifications', getRepository(TestSpecification), domain.id)\n ])\n\n return { axes }\n }\n}\n"]}
@@ -4,6 +4,7 @@ import { TestSpecification } from './test-specification';
4
4
  /** Filter, sort, paginate and display the same time projection. No per-row resolution queries. */
5
5
  export declare function listEffectiveTestSpecifications(manager: EntityManager, domain: Domain, params: ListParam, at?: Date): Promise<{
6
6
  items: (TestSpecification & {
7
+ revisionNumber?: number;
7
8
  id: string;
8
9
  domain?: Domain;
9
10
  domainId?: string;
@@ -23,6 +24,7 @@ export declare function listEffectiveTestSpecifications(manager: EntityManager,
23
24
  updaterId?: string;
24
25
  } & {
25
26
  description: string;
27
+ revisionNumber: number;
26
28
  version: string;
27
29
  criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
28
30
  effectiveStart: Date;
@@ -12,8 +12,9 @@ async function listEffectiveTestSpecifications(manager, domain, params, at = new
12
12
  params,
13
13
  alias: 'spec',
14
14
  searchables: ['name', 'description'],
15
- sortables: ['id', 'name', 'description', 'version', 'effectiveStart', 'effectiveEnd', 'createdAt', 'updatedAt'],
15
+ sortables: ['id', 'name', 'revisionNumber', 'description', 'version', 'effectiveStart', 'effectiveEnd', 'createdAt', 'updatedAt'],
16
16
  columnExpressions: {
17
+ revisionNumber: 'revision.revisionNumber',
17
18
  description: 'revision.description',
18
19
  version: 'revision.version',
19
20
  effectiveStart: 'segment.validFrom',
@@ -29,6 +30,7 @@ async function listEffectiveTestSpecifications(manager, domain, params, at = new
29
30
  const { selectedRevision, selectedSegment, ...identity } = row;
30
31
  return Object.assign(new test_specification_1.TestSpecification(), identity, {
31
32
  description: selectedRevision?.description ?? null,
33
+ revisionNumber: selectedRevision?.revisionNumber ?? null,
32
34
  version: selectedRevision?.version ?? null,
33
35
  criteria: selectedRevision?.criteria ?? [],
34
36
  effectiveStart: selectedSegment?.validFrom ?? null,
@@ -1 +1 @@
1
- {"version":3,"file":"test-spec-effective-list.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-effective-list.ts"],"names":[],"mappings":";;AAWA,0EAmDC;AA7DD,iDAAwF;AACxF,6DAAwD;AACxD,6DAAiH;AAOjH,kGAAkG;AAC3F,KAAK,UAAU,+BAA+B,CACnD,OAAsB,EACtB,MAAc,EACd,MAAiB,EACjB,EAAE,GAAG,IAAI,IAAI,EAAE;IAEf,MAAM,KAAK,GAAG,IAAA,qCAA6B,EAAC;QAC1C,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,sCAAiB,CAAC;QACpD,MAAM;QACN,MAAM;QACN,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC;QACpC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC;QAC/G,iBAAiB,EAAE;YACjB,WAAW,EAAE,sBAAsB;YACnC,OAAO,EAAE,kBAAkB;YAC3B,cAAc,EAAE,mBAAmB;YACnC,YAAY,EAAE,iBAAiB;SAChC;KACF,CAAC,CAAA;IACF,KAAK;SACF,QAAQ,CACP,0CAAqB,EACrB,MAAM,EACN,uGAAuG,CACxG;SACA,iBAAiB,CAChB,sBAAsB,EACtB,6CAAwB,EACxB,SAAS,EACT,+GAA+G,EAC/G,EAAE,EAAE,EAAE,CACP;SACA,iBAAiB,CAChB,uBAAuB,EACvB,8CAAyB,EACzB,UAAU,EACV,mCAAmC,CACpC,CAAA;IACH,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAA;IACnD,MAAM,KAAK,GAAI,IAAuB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QAC/C,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAA;QAC9D,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,sCAAiB,EAAE,EAAE,QAAQ,EAAE;YACtD,WAAW,EAAE,gBAAgB,EAAE,WAAW,IAAI,IAAI;YAClD,OAAO,EAAE,gBAAgB,EAAE,OAAO,IAAI,IAAI;YAC1C,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,IAAI,EAAE;YAC1C,cAAc,EAAE,eAAe,EAAE,SAAS,IAAI,IAAI;YAClD,YAAY,EAAE,eAAe,EAAE,OAAO,IAAI,IAAI;SAC/C,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;AACzB,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\nimport { Domain, getQueryBuilderFromListParams, ListParam } from '@things-factory/shell'\nimport { TestSpecification } from './test-specification'\nimport { TestSpecificationPlan, TestSpecificationRevision, TestSpecificationSegment } from './test-spec-revision'\n\ntype EffectiveRow = TestSpecification & {\n selectedRevision?: TestSpecificationRevision\n selectedSegment?: TestSpecificationSegment\n}\n\n/** Filter, sort, paginate and display the same time projection. No per-row resolution queries. */\nexport async function listEffectiveTestSpecifications(\n manager: EntityManager,\n domain: Domain,\n params: ListParam,\n at = new Date()\n) {\n const query = getQueryBuilderFromListParams({\n repository: manager.getRepository(TestSpecification),\n domain,\n params,\n alias: 'spec',\n searchables: ['name', 'description'],\n sortables: ['id', 'name', 'description', 'version', 'effectiveStart', 'effectiveEnd', 'createdAt', 'updatedAt'],\n columnExpressions: {\n description: 'revision.description',\n version: 'revision.version',\n effectiveStart: 'segment.validFrom',\n effectiveEnd: 'segment.validTo'\n }\n })\n query\n .leftJoin(\n TestSpecificationPlan,\n 'plan',\n 'plan.spec_id = spec.id AND plan.domain_id = spec.domain_id AND plan.generation = spec.plan_generation'\n )\n .leftJoinAndMapOne(\n 'spec.selectedSegment',\n TestSpecificationSegment,\n 'segment',\n 'segment.plan_id = plan.id AND segment.validFrom <= :at AND (segment.validTo IS NULL OR segment.validTo > :at)',\n { at }\n )\n .leftJoinAndMapOne(\n 'spec.selectedRevision',\n TestSpecificationRevision,\n 'revision',\n 'revision.id = segment.revision_id'\n )\n const [rows, total] = await query.getManyAndCount()\n const items = (rows as EffectiveRow[]).map(row => {\n const { selectedRevision, selectedSegment, ...identity } = row\n return Object.assign(new TestSpecification(), identity, {\n description: selectedRevision?.description ?? null,\n version: selectedRevision?.version ?? null,\n criteria: selectedRevision?.criteria ?? [],\n effectiveStart: selectedSegment?.validFrom ?? null,\n effectiveEnd: selectedSegment?.validTo ?? null\n })\n })\n return { items, total }\n}\n"]}
1
+ {"version":3,"file":"test-spec-effective-list.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-effective-list.ts"],"names":[],"mappings":";;AAWA,0EAqDC;AA/DD,iDAAwF;AACxF,6DAAwD;AACxD,6DAAiH;AAOjH,kGAAkG;AAC3F,KAAK,UAAU,+BAA+B,CACnD,OAAsB,EACtB,MAAc,EACd,MAAiB,EACjB,EAAE,GAAG,IAAI,IAAI,EAAE;IAEf,MAAM,KAAK,GAAG,IAAA,qCAA6B,EAAC;QAC1C,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,sCAAiB,CAAC;QACpD,MAAM;QACN,MAAM;QACN,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC;QACpC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC;QACjI,iBAAiB,EAAE;YACjB,cAAc,EAAE,yBAAyB;YACzC,WAAW,EAAE,sBAAsB;YACnC,OAAO,EAAE,kBAAkB;YAC3B,cAAc,EAAE,mBAAmB;YACnC,YAAY,EAAE,iBAAiB;SAChC;KACF,CAAC,CAAA;IACF,KAAK;SACF,QAAQ,CACP,0CAAqB,EACrB,MAAM,EACN,uGAAuG,CACxG;SACA,iBAAiB,CAChB,sBAAsB,EACtB,6CAAwB,EACxB,SAAS,EACT,+GAA+G,EAC/G,EAAE,EAAE,EAAE,CACP;SACA,iBAAiB,CAChB,uBAAuB,EACvB,8CAAyB,EACzB,UAAU,EACV,mCAAmC,CACpC,CAAA;IACH,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAA;IACnD,MAAM,KAAK,GAAI,IAAuB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QAC/C,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAA;QAC9D,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,sCAAiB,EAAE,EAAE,QAAQ,EAAE;YACtD,WAAW,EAAE,gBAAgB,EAAE,WAAW,IAAI,IAAI;YAClD,cAAc,EAAE,gBAAgB,EAAE,cAAc,IAAI,IAAI;YACxD,OAAO,EAAE,gBAAgB,EAAE,OAAO,IAAI,IAAI;YAC1C,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,IAAI,EAAE;YAC1C,cAAc,EAAE,eAAe,EAAE,SAAS,IAAI,IAAI;YAClD,YAAY,EAAE,eAAe,EAAE,OAAO,IAAI,IAAI;SAC/C,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;AACzB,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\nimport { Domain, getQueryBuilderFromListParams, ListParam } from '@things-factory/shell'\nimport { TestSpecification } from './test-specification'\nimport { TestSpecificationPlan, TestSpecificationRevision, TestSpecificationSegment } from './test-spec-revision'\n\ntype EffectiveRow = TestSpecification & {\n selectedRevision?: TestSpecificationRevision\n selectedSegment?: TestSpecificationSegment\n}\n\n/** Filter, sort, paginate and display the same time projection. No per-row resolution queries. */\nexport async function listEffectiveTestSpecifications(\n manager: EntityManager,\n domain: Domain,\n params: ListParam,\n at = new Date()\n) {\n const query = getQueryBuilderFromListParams({\n repository: manager.getRepository(TestSpecification),\n domain,\n params,\n alias: 'spec',\n searchables: ['name', 'description'],\n sortables: ['id', 'name', 'revisionNumber', 'description', 'version', 'effectiveStart', 'effectiveEnd', 'createdAt', 'updatedAt'],\n columnExpressions: {\n revisionNumber: 'revision.revisionNumber',\n description: 'revision.description',\n version: 'revision.version',\n effectiveStart: 'segment.validFrom',\n effectiveEnd: 'segment.validTo'\n }\n })\n query\n .leftJoin(\n TestSpecificationPlan,\n 'plan',\n 'plan.spec_id = spec.id AND plan.domain_id = spec.domain_id AND plan.generation = spec.plan_generation'\n )\n .leftJoinAndMapOne(\n 'spec.selectedSegment',\n TestSpecificationSegment,\n 'segment',\n 'segment.plan_id = plan.id AND segment.validFrom <= :at AND (segment.validTo IS NULL OR segment.validTo > :at)',\n { at }\n )\n .leftJoinAndMapOne(\n 'spec.selectedRevision',\n TestSpecificationRevision,\n 'revision',\n 'revision.id = segment.revision_id'\n )\n const [rows, total] = await query.getManyAndCount()\n const items = (rows as EffectiveRow[]).map(row => {\n const { selectedRevision, selectedSegment, ...identity } = row\n return Object.assign(new TestSpecification(), identity, {\n description: selectedRevision?.description ?? null,\n revisionNumber: selectedRevision?.revisionNumber ?? null,\n version: selectedRevision?.version ?? null,\n criteria: selectedRevision?.criteria ?? [],\n effectiveStart: selectedSegment?.validFrom ?? null,\n effectiveEnd: selectedSegment?.validTo ?? null\n })\n })\n return { items, total }\n}\n"]}
@@ -23,6 +23,7 @@ export declare class TestSpecificationRevisionResolver {
23
23
  }[];
24
24
  revisions: {
25
25
  state: string;
26
+ revisionNumber: number;
26
27
  policy: import("..").MasterChangePolicy;
27
28
  policyId: string;
28
29
  id: string;
@@ -12,11 +12,31 @@ export declare function testSpecificationApprovalInput(manager: EntityManager, d
12
12
  reason: string;
13
13
  effectiveFrom: string;
14
14
  before: {
15
+ revisionNumber: number;
16
+ detail: {
17
+ validFrom?: string;
18
+ validTo?: string;
19
+ revisionId: string;
20
+ createdAt: string;
21
+ frozenAt: string;
22
+ effectiveFrom: string;
23
+ reason: string;
24
+ };
15
25
  version: string;
16
26
  description: string;
17
27
  criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
18
28
  };
19
29
  after: {
30
+ revisionNumber: number;
31
+ detail: {
32
+ validFrom?: string;
33
+ validTo?: string;
34
+ revisionId: string;
35
+ createdAt: string;
36
+ frozenAt: string;
37
+ effectiveFrom: string;
38
+ reason: string;
39
+ };
20
40
  version: string;
21
41
  description: string;
22
42
  criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
@@ -83,6 +103,7 @@ export declare function testSpecificationRevisionOverview(manager: EntityManager
83
103
  }[];
84
104
  revisions: {
85
105
  state: string;
106
+ revisionNumber: number;
86
107
  policy: import("../master-change-policy").MasterChangePolicy;
87
108
  policyId: string;
88
109
  id: string;
@@ -95,6 +95,7 @@ function testSpecificationSubmissionHash(revision) {
95
95
  return (0, node_crypto_1.createHash)('sha256')
96
96
  .update(canonical({
97
97
  schemaVersion: 1,
98
+ revisionNumber: revision.revisionNumber,
98
99
  policyId: revision.policyId,
99
100
  domainId: revision.domainId,
100
101
  specId: revision.specId,
@@ -115,7 +116,19 @@ async function testSpecificationApprovalInput(manager, domainId, revisionId) {
115
116
  refuse('INVALID_SUBMISSION');
116
117
  const spec = await subject(manager, domainId, draft.specId);
117
118
  const base = draft.baseRevisionId ? await revision(manager, domainId, draft.baseRevisionId) : null;
119
+ const plan = base ? await manager.findOneBy(test_spec_revision_1.TestSpecificationPlan, {
120
+ domain: { id: domainId }, spec: { id: draft.specId }, generation: draft.baseGeneration
121
+ }) : null;
122
+ const segment = plan && base ? await manager.findOneBy(test_spec_revision_1.TestSpecificationSegment, {
123
+ domain: { id: domainId }, plan: { id: plan.id }, revision: { id: base.id }
124
+ }) : null;
118
125
  const values = (row) => ({
126
+ revisionNumber: row.revisionNumber,
127
+ detail: {
128
+ revisionId: row.id, createdAt: row.createdAt.toISOString(), frozenAt: row.frozenAt?.toISOString(),
129
+ effectiveFrom: row.effectiveFrom.toISOString(), reason: row.reason,
130
+ ...(row.id === base?.id && segment ? { validFrom: segment.validFrom.toISOString(), validTo: segment.validTo?.toISOString() ?? null } : {})
131
+ },
119
132
  version: row.version,
120
133
  description: row.description,
121
134
  criteria: row.criteria
@@ -170,6 +183,18 @@ async function subject(manager, domainId, specId) {
170
183
  });
171
184
  if (!spec)
172
185
  refuse('SPECIFICATION_NOT_FOUND');
186
+ // Compare the current head and latest persisted plan in one SQL snapshot. A historical
187
+ // knownAt query must not compare its selected old plan with the current head.
188
+ const head = await manager.createQueryBuilder(test_specification_1.TestSpecification, 'head')
189
+ .leftJoin(test_spec_revision_1.TestSpecificationPlan, 'plan', 'plan.spec = head.id AND plan.domain = head.domain')
190
+ .select('head.planGeneration', 'generation')
191
+ .addSelect('MAX(plan.generation)', 'latest')
192
+ .where({ id: specId, domain: { id: domainId } })
193
+ .groupBy('head.id').addGroupBy('head.planGeneration')
194
+ .getRawOne();
195
+ if (!head || Number(head.generation) !== Number(head.latest ?? 0)) {
196
+ throw new shell_1.Refusal('PLAN_INTEGRITY_FAILURE', {}, 'The current plan generation does not match the latest published plan. Contact an administrator.');
197
+ }
173
198
  return spec;
174
199
  }
175
200
  async function revision(manager, domainId, revisionId, locked = true) {
@@ -212,6 +237,11 @@ async function createTestSpecificationDraft(manager, domainId, creatorId, asked)
212
237
  updater: { id: creatorId }
213
238
  }));
214
239
  }
240
+ const previous = await manager.findOne(test_spec_revision_1.TestSpecificationRevision, {
241
+ where: { domain: { id: domainId }, spec: { id: spec.id } }, order: { revisionNumber: 'DESC' },
242
+ ...currentLock(manager)
243
+ });
244
+ const revisionNumber = (previous?.revisionNumber ?? 0) + 1;
215
245
  let baseRevisionId = null;
216
246
  if (spec.planGeneration > 0) {
217
247
  baseRevisionId = (await resolveTestSpecification(manager, domainId, spec.id, accepted.effectiveFrom)).revision.id;
@@ -225,6 +255,7 @@ async function createTestSpecificationDraft(manager, domainId, creatorId, asked)
225
255
  spec: { id: spec.id },
226
256
  baseRevision: baseRevisionId ? { id: baseRevisionId } : null,
227
257
  baseGeneration: spec.planGeneration,
258
+ revisionNumber,
228
259
  editGeneration: 0,
229
260
  creator: { id: creatorId },
230
261
  createdAt: new Date(),
@@ -425,6 +456,8 @@ async function resolveTestSpecification(manager, domainId, specId, businessTime,
425
456
  });
426
457
  if (!plan)
427
458
  refuse('NO_EFFECTIVE_REVISION');
459
+ /* 실행 조회도 뿌리와 최신 Plan 의 불변식을 지난다 — material·recipe 와 같은 자리다. */
460
+ await subject(manager, domainId, specId);
428
461
  const query = manager
429
462
  .createQueryBuilder(test_spec_revision_1.TestSpecificationSegment, 'segment')
430
463
  .where({ domain: { id: domainId }, spec: { id: specId }, plan: { id: plan.id } })