@things-factory/ops-master 10.0.18 → 10.1.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 (269) hide show
  1. package/dist-client/bootstrap.d.ts +18 -0
  2. package/dist-client/bootstrap.js +36 -0
  3. package/dist-client/bootstrap.js.map +1 -0
  4. package/dist-client/index.d.ts +15 -1
  5. package/dist-client/index.js +18 -1
  6. package/dist-client/index.js.map +1 -1
  7. package/dist-client/pages/master/equipment-class.d.ts +12 -0
  8. package/dist-client/pages/master/equipment-class.js +42 -0
  9. package/dist-client/pages/master/equipment-class.js.map +1 -0
  10. package/dist-client/pages/master/equipment.d.ts +12 -0
  11. package/dist-client/pages/master/equipment.js +57 -0
  12. package/dist-client/pages/master/equipment.js.map +1 -0
  13. package/dist-client/pages/master/grist-iso-duration.d.ts +20 -0
  14. package/dist-client/pages/master/grist-iso-duration.js +70 -0
  15. package/dist-client/pages/master/grist-iso-duration.js.map +1 -0
  16. package/dist-client/pages/master/grist-reference.d.ts +91 -0
  17. package/dist-client/pages/master/grist-reference.js +419 -0
  18. package/dist-client/pages/master/grist-reference.js.map +1 -0
  19. package/dist-client/pages/master/import-rows.d.ts +35 -0
  20. package/dist-client/pages/master/import-rows.js +66 -0
  21. package/dist-client/pages/master/import-rows.js.map +1 -0
  22. package/dist-client/pages/master/iso-duration.d.ts +34 -0
  23. package/dist-client/pages/master/iso-duration.js +76 -0
  24. package/dist-client/pages/master/iso-duration.js.map +1 -0
  25. package/dist-client/pages/master/master-options.d.ts +33 -0
  26. package/dist-client/pages/master/master-options.js +47 -0
  27. package/dist-client/pages/master/master-options.js.map +1 -0
  28. package/dist-client/pages/master/master-page-base.d.ts +357 -0
  29. package/dist-client/pages/master/master-page-base.js +626 -0
  30. package/dist-client/pages/master/master-page-base.js.map +1 -0
  31. package/dist-client/pages/master/material-class.d.ts +12 -0
  32. package/dist-client/pages/master/material-class.js +42 -0
  33. package/dist-client/pages/master/material-class.js.map +1 -0
  34. package/dist-client/pages/master/material-definition.d.ts +12 -0
  35. package/dist-client/pages/master/material-definition.js +62 -0
  36. package/dist-client/pages/master/material-definition.js.map +1 -0
  37. package/dist-client/pages/master/operation-definition.d.ts +12 -0
  38. package/dist-client/pages/master/operation-definition.js +49 -0
  39. package/dist-client/pages/master/operation-definition.js.map +1 -0
  40. package/dist-client/pages/master/operational-location.d.ts +84 -0
  41. package/dist-client/pages/master/operational-location.js +426 -0
  42. package/dist-client/pages/master/operational-location.js.map +1 -0
  43. package/dist-client/pages/master/person.d.ts +12 -0
  44. package/dist-client/pages/master/person.js +41 -0
  45. package/dist-client/pages/master/person.js.map +1 -0
  46. package/dist-client/pages/master/personnel-class.d.ts +12 -0
  47. package/dist-client/pages/master/personnel-class.js +43 -0
  48. package/dist-client/pages/master/personnel-class.js.map +1 -0
  49. package/dist-client/pages/master/physical-asset-class.d.ts +12 -0
  50. package/dist-client/pages/master/physical-asset-class.js +40 -0
  51. package/dist-client/pages/master/physical-asset-class.js.map +1 -0
  52. package/dist-client/pages/master/physical-asset.d.ts +12 -0
  53. package/dist-client/pages/master/physical-asset.js +41 -0
  54. package/dist-client/pages/master/physical-asset.js.map +1 -0
  55. package/dist-client/pages/master/recipe-definition.d.ts +136 -0
  56. package/dist-client/pages/master/recipe-definition.js +404 -0
  57. package/dist-client/pages/master/recipe-definition.js.map +1 -0
  58. package/dist-client/pages/master/reference-targets.d.ts +42 -0
  59. package/dist-client/pages/master/reference-targets.js +82 -0
  60. package/dist-client/pages/master/reference-targets.js.map +1 -0
  61. package/dist-client/pages/master/route-definition.d.ts +19 -0
  62. package/dist-client/pages/master/route-definition.js +40 -0
  63. package/dist-client/pages/master/route-definition.js.map +1 -0
  64. package/dist-client/pages/master/stop-reason.d.ts +12 -0
  65. package/dist-client/pages/master/stop-reason.js +48 -0
  66. package/dist-client/pages/master/stop-reason.js.map +1 -0
  67. package/dist-client/pages/master/test-specification.d.ts +93 -0
  68. package/dist-client/pages/master/test-specification.js +449 -0
  69. package/dist-client/pages/master/test-specification.js.map +1 -0
  70. package/dist-client/pages/master-axes.d.ts +20 -29
  71. package/dist-client/pages/master-axes.js +32 -169
  72. package/dist-client/pages/master-axes.js.map +1 -1
  73. package/dist-client/route.d.ts +1 -1
  74. package/dist-client/route.js +76 -3
  75. package/dist-client/route.js.map +1 -1
  76. package/dist-client/tsconfig.tsbuildinfo +1 -1
  77. package/dist-server/service/asset/asset-lifecycle.d.ts +30 -7
  78. package/dist-server/service/asset/asset-lifecycle.js +33 -6
  79. package/dist-server/service/asset/asset-lifecycle.js.map +1 -1
  80. package/dist-server/service/asset/asset-master-resolver.d.ts +9 -0
  81. package/dist-server/service/asset/asset-master-resolver.js +41 -0
  82. package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
  83. package/dist-server/service/asset/asset-master.js +1 -1
  84. package/dist-server/service/asset/asset-master.js.map +1 -1
  85. package/dist-server/service/asset/asset-query.d.ts +9 -1
  86. package/dist-server/service/asset/asset-query.js +28 -6
  87. package/dist-server/service/asset/asset-query.js.map +1 -1
  88. package/dist-server/service/asset/physical-asset-class.js +1 -1
  89. package/dist-server/service/asset/physical-asset-class.js.map +1 -1
  90. package/dist-server/service/asset/physical-asset.d.ts +17 -4
  91. package/dist-server/service/asset/physical-asset.js +6 -6
  92. package/dist-server/service/asset/physical-asset.js.map +1 -1
  93. package/dist-server/service/equipment/equipment-class.js +1 -1
  94. package/dist-server/service/equipment/equipment-class.js.map +1 -1
  95. package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -0
  96. package/dist-server/service/equipment/equipment-master-resolver.js +58 -0
  97. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  98. package/dist-server/service/equipment/equipment-master.d.ts +3 -2
  99. package/dist-server/service/equipment/equipment-master.js +21 -4
  100. package/dist-server/service/equipment/equipment-master.js.map +1 -1
  101. package/dist-server/service/equipment/equipment-query.d.ts +9 -1
  102. package/dist-server/service/equipment/equipment-query.js +28 -6
  103. package/dist-server/service/equipment/equipment-query.js.map +1 -1
  104. package/dist-server/service/equipment/equipment.d.ts +27 -1
  105. package/dist-server/service/equipment/equipment.js +8 -3
  106. package/dist-server/service/equipment/equipment.js.map +1 -1
  107. package/dist-server/service/equipment/state-period-rules.d.ts +19 -0
  108. package/dist-server/service/equipment/state-period-rules.js +35 -1
  109. package/dist-server/service/equipment/state-period-rules.js.map +1 -1
  110. package/dist-server/service/index.d.ts +12 -2
  111. package/dist-server/service/index.js +21 -1
  112. package/dist-server/service/index.js.map +1 -1
  113. package/dist-server/service/location/location-master-resolver.d.ts +29 -0
  114. package/dist-server/service/location/location-master-resolver.js +228 -1
  115. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  116. package/dist-server/service/location/location-query.d.ts +9 -1
  117. package/dist-server/service/location/location-query.js +24 -9
  118. package/dist-server/service/location/location-query.js.map +1 -1
  119. package/dist-server/service/location/operational-location.d.ts +37 -0
  120. package/dist-server/service/location/operational-location.js +22 -2
  121. package/dist-server/service/location/operational-location.js.map +1 -1
  122. package/dist-server/service/master-axis.d.ts +1 -1
  123. package/dist-server/service/master-axis.js +16 -1
  124. package/dist-server/service/master-axis.js.map +1 -1
  125. package/dist-server/service/master-conformance-resolver.d.ts +34 -0
  126. package/dist-server/service/master-conformance-resolver.js +143 -0
  127. package/dist-server/service/master-conformance-resolver.js.map +1 -0
  128. package/dist-server/service/master-conformance.d.ts +80 -0
  129. package/dist-server/service/master-conformance.js +266 -0
  130. package/dist-server/service/master-conformance.js.map +1 -0
  131. package/dist-server/service/master-page-query.d.ts +33 -0
  132. package/dist-server/service/master-page-query.js +55 -0
  133. package/dist-server/service/master-page-query.js.map +1 -0
  134. package/dist-server/service/master-page.d.ts +85 -0
  135. package/dist-server/service/master-page.js +104 -0
  136. package/dist-server/service/master-page.js.map +1 -0
  137. package/dist-server/service/material/material-class.js +1 -1
  138. package/dist-server/service/material/material-class.js.map +1 -1
  139. package/dist-server/service/material/material-definition.js +2 -2
  140. package/dist-server/service/material/material-definition.js.map +1 -1
  141. package/dist-server/service/material/material-master-resolver.d.ts +62 -0
  142. package/dist-server/service/material/material-master-resolver.js +213 -0
  143. package/dist-server/service/material/material-master-resolver.js.map +1 -1
  144. package/dist-server/service/material/material-query.d.ts +9 -1
  145. package/dist-server/service/material/material-query.js +28 -6
  146. package/dist-server/service/material/material-query.js.map +1 -1
  147. package/dist-server/service/material/recipe-index.d.ts +23 -0
  148. package/dist-server/service/material/recipe-index.js +44 -0
  149. package/dist-server/service/material/recipe-index.js.map +1 -0
  150. package/dist-server/service/operation/index.d.ts +2 -1
  151. package/dist-server/service/operation/index.js +2 -1
  152. package/dist-server/service/operation/index.js.map +1 -1
  153. package/dist-server/service/operation/operation-definition.d.ts +1 -1
  154. package/dist-server/service/operation/operation-definition.js +4 -4
  155. package/dist-server/service/operation/operation-definition.js.map +1 -1
  156. package/dist-server/service/operation/operation-master-resolver.d.ts +24 -0
  157. package/dist-server/service/operation/operation-master-resolver.js +107 -0
  158. package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
  159. package/dist-server/service/operation/operation-query.d.ts +10 -2
  160. package/dist-server/service/operation/operation-query.js +25 -10
  161. package/dist-server/service/operation/operation-query.js.map +1 -1
  162. package/dist-server/service/operation/operation-readiness-resolver.d.ts +39 -0
  163. package/dist-server/service/operation/operation-readiness-resolver.js +111 -0
  164. package/dist-server/service/operation/operation-readiness-resolver.js.map +1 -0
  165. package/dist-server/service/operation/operation-readiness.d.ts +34 -0
  166. package/dist-server/service/operation/operation-readiness.js +133 -0
  167. package/dist-server/service/operation/operation-readiness.js.map +1 -0
  168. package/dist-server/service/personnel/person.js +3 -3
  169. package/dist-server/service/personnel/person.js.map +1 -1
  170. package/dist-server/service/personnel/personnel-class-readiness.d.ts +22 -0
  171. package/dist-server/service/personnel/personnel-class-readiness.js +98 -0
  172. package/dist-server/service/personnel/personnel-class-readiness.js.map +1 -0
  173. package/dist-server/service/personnel/personnel-class.d.ts +1 -1
  174. package/dist-server/service/personnel/personnel-class.js +2 -2
  175. package/dist-server/service/personnel/personnel-class.js.map +1 -1
  176. package/dist-server/service/personnel/personnel-master-resolver.d.ts +9 -0
  177. package/dist-server/service/personnel/personnel-master-resolver.js +43 -0
  178. package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
  179. package/dist-server/service/personnel/personnel-master.js +1 -1
  180. package/dist-server/service/personnel/personnel-master.js.map +1 -1
  181. package/dist-server/service/personnel/personnel-query.d.ts +9 -1
  182. package/dist-server/service/personnel/personnel-query.js +28 -6
  183. package/dist-server/service/personnel/personnel-query.js.map +1 -1
  184. package/dist-server/service/personnel/qualification.d.ts +7 -0
  185. package/dist-server/service/personnel/qualification.js +25 -0
  186. package/dist-server/service/personnel/qualification.js.map +1 -1
  187. package/dist-server/service/recipe/index.d.ts +9 -0
  188. package/dist-server/service/recipe/index.js +14 -0
  189. package/dist-server/service/recipe/index.js.map +1 -0
  190. package/dist-server/service/recipe/recipe-definition.d.ts +71 -0
  191. package/dist-server/service/recipe/recipe-definition.js +130 -0
  192. package/dist-server/service/recipe/recipe-definition.js.map +1 -0
  193. package/dist-server/service/recipe/recipe-master-resolver.d.ts +28 -0
  194. package/dist-server/service/recipe/recipe-master-resolver.js +124 -0
  195. package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -0
  196. package/dist-server/service/recipe/recipe-master.d.ts +37 -0
  197. package/dist-server/service/recipe/recipe-master.js +47 -0
  198. package/dist-server/service/recipe/recipe-master.js.map +1 -0
  199. package/dist-server/service/recipe/recipe-query.d.ts +21 -0
  200. package/dist-server/service/recipe/recipe-query.js +75 -0
  201. package/dist-server/service/recipe/recipe-query.js.map +1 -0
  202. package/dist-server/service/recipe/route-definition.d.ts +51 -0
  203. package/dist-server/service/recipe/route-definition.js +113 -0
  204. package/dist-server/service/recipe/route-definition.js.map +1 -0
  205. package/dist-server/service/resource/class-lineage.d.ts +46 -0
  206. package/dist-server/service/resource/class-lineage.js +80 -0
  207. package/dist-server/service/resource/class-lineage.js.map +1 -0
  208. package/dist-server/service/resource/class-members.d.ts +36 -0
  209. package/dist-server/service/resource/class-members.js +50 -0
  210. package/dist-server/service/resource/class-members.js.map +1 -0
  211. package/dist-server/service/resource/referrers.d.ts +54 -0
  212. package/dist-server/service/resource/referrers.js +99 -0
  213. package/dist-server/service/resource/referrers.js.map +1 -0
  214. package/dist-server/service/status/master-status.d.ts +0 -7
  215. package/dist-server/service/status/master-status.js +5 -6
  216. package/dist-server/service/status/master-status.js.map +1 -1
  217. package/dist-server/service/test-spec/criteria-check.d.ts +53 -0
  218. package/dist-server/service/test-spec/criteria-check.js +142 -0
  219. package/dist-server/service/test-spec/criteria-check.js.map +1 -0
  220. package/dist-server/service/test-spec/criteria-rows.d.ts +89 -0
  221. package/dist-server/service/test-spec/criteria-rows.js +139 -0
  222. package/dist-server/service/test-spec/criteria-rows.js.map +1 -0
  223. package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +33 -7
  224. package/dist-server/service/test-spec/test-spec-master-resolver.js +137 -2
  225. package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
  226. package/dist-server/service/test-spec/test-spec-query.d.ts +9 -1
  227. package/dist-server/service/test-spec/test-spec-query.js +23 -9
  228. package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
  229. package/dist-server/service/test-spec/test-specification.js +1 -1
  230. package/dist-server/service/test-spec/test-specification.js.map +1 -1
  231. package/dist-server/tsconfig.tsbuildinfo +1 -1
  232. package/helps/ops-master/equipment-class.ko.md +11 -0
  233. package/helps/ops-master/equipment-class.md +11 -0
  234. package/helps/ops-master/equipment.ko.md +11 -0
  235. package/helps/ops-master/equipment.md +11 -0
  236. package/helps/ops-master/material-class.ko.md +11 -0
  237. package/helps/ops-master/material-class.md +11 -0
  238. package/helps/ops-master/material-definition.ko.md +11 -0
  239. package/helps/ops-master/material-definition.md +11 -0
  240. package/helps/ops-master/operation-definition.ko.md +11 -0
  241. package/helps/ops-master/operation-definition.md +11 -0
  242. package/helps/ops-master/operational-location.ko.md +11 -0
  243. package/helps/ops-master/operational-location.md +11 -0
  244. package/helps/ops-master/person.ko.md +11 -0
  245. package/helps/ops-master/person.md +11 -0
  246. package/helps/ops-master/personnel-class.ko.md +11 -0
  247. package/helps/ops-master/personnel-class.md +11 -0
  248. package/helps/ops-master/physical-asset-class.ko.md +11 -0
  249. package/helps/ops-master/physical-asset-class.md +11 -0
  250. package/helps/ops-master/physical-asset.ko.md +11 -0
  251. package/helps/ops-master/physical-asset.md +11 -0
  252. package/helps/ops-master/recipe-definition.ko.md +11 -0
  253. package/helps/ops-master/recipe-definition.md +11 -0
  254. package/helps/ops-master/route-definition.ko.md +11 -0
  255. package/helps/ops-master/route-definition.md +11 -0
  256. package/helps/ops-master/stop-reason.ko.md +11 -0
  257. package/helps/ops-master/stop-reason.md +11 -0
  258. package/helps/ops-master/test-specification.ko.md +11 -0
  259. package/helps/ops-master/test-specification.md +11 -0
  260. package/package.json +5 -5
  261. package/things-factory.config.js +22 -2
  262. package/translations/en.json +89 -6
  263. package/translations/ja.json +96 -1
  264. package/translations/ko.json +96 -13
  265. package/translations/ms.json +96 -1
  266. package/translations/zh.json +96 -1
  267. package/dist-client/pages/master-list.d.ts +0 -68
  268. package/dist-client/pages/master-list.js +0 -267
  269. package/dist-client/pages/master-list.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"location-query.js","sourceRoot":"","sources":["../../../server/service/location/location-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAsE;AAEtE,iDAAqD;AAErD,uDAAyE;AACzE,iEAA4D;AAGrD,IAAM,cAAc,GAApB,MAAM,cAAc;CAG1B,CAAA;AAHY,wCAAc;AAEzB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;;iDAClE;yBAFN,cAAc;IAD1B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;GAC1D,cAAc,CAG1B;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,0CAA0C;IAEpC,AAAN,KAAK,CAAC,cAAc,CAAQ,OAAwB;QAClD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,SAAS,GAAG,MAAM,IAAA,qBAAa,EAAC,0CAAmB,CAAC,CAAC,IAAI,CAAC;YAC9D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE;YACpC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;SACvB,CAAC,CAAA;QAEF,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAA,gCAAmB,EAAC,SAAS,CAAC,CAAC,SAAS,CAAC,EAAE,CAAA;IAChF,CAAC;CACF,CAAA;AAbY,sCAAa;AAGlB;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;IACpE,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAS1B;wBAZU,aAAa;IADzB,IAAA,uBAAQ,EAAC,0CAAmB,CAAC;GACjB,aAAa,CAazB","sourcesContent":["import { Ctx, Field, ObjectType, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { locationMaster as buildLocationMaster } from './location-master'\nimport { OperationalLocation } from './operational-location'\n\n@ObjectType({ description: 'Location master as the twin receives it' })\nexport class LocationMaster {\n @Field(type => String, { description: 'ReferenceMaster locations[] as JSON text' })\n locations: string\n}\n\n@Resolver(OperationalLocation)\nexport class LocationQuery {\n /** 자리 축 전체 — 자리가 상위 자리를 가리키므로 한 번에 낸다. */\n @Query(returns => LocationMaster, { description: 'Location master for the twin to pull' })\n async locationMaster(@Ctx() context: ResolverContext): Promise<LocationMaster> {\n const { domain } = context.state\n\n const locations = await getRepository(OperationalLocation).find({\n where: { domain: { id: domain.id } },\n order: { name: 'ASC' }\n })\n\n return { locations: JSON.stringify(buildLocationMaster(locations).locations) }\n }\n}\n"]}
1
+ {"version":3,"file":"location-query.js","sourceRoot":"","sources":["../../../server/service/location/location-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAgF;AAIhF,4DAAiE;AAEjE,uDAAyE;AACzE,iEAA4D;AAGrD,IAAM,cAAc,GAApB,MAAM,cAAc;CAgB1B,CAAA;AAhBY,wCAAc;AAEzB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;;iDAClE;AAQjB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;;+CACvE;AAIhB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;4CACnG;yBAdF,cAAc;IAD1B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;GAC1D,cAAc,CAgB1B;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,0CAA0C;IAEpC,AAAN,KAAK,CAAC,cAAc,CACmF,KAAyB,EACO,KAAyB,EACvJ,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;QAChC,MAAM,IAAI,GAAG,MAAM,IAAA,4BAAQ,EAAsB,0CAAmB,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAEtF,0DAA0D;QAC1D,IAAA,oCAAgB,EAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;QAErC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAA;QAE3B,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAA,gCAAmB,EAAC,SAAS,CAAC,CAAC,SAAS,CAAC;YACnE,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAA;IACH,CAAC;CACF,CAAA;AAxBY,sCAAa;AAGlB;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;IAEvF,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAA;IACnG,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC,CAAA;IACnI,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAiBP;wBAvBU,aAAa;IADzB,IAAA,uBAAQ,EAAC,0CAAmB,CAAC;GACjB,aAAa,CAwBzB","sourcesContent":["import { Arg, Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { pullPage, refuseIfTooLarge } from '../master-page-query'\n\nimport { locationMaster as buildLocationMaster } from './location-master'\nimport { OperationalLocation } from './operational-location'\n\n@ObjectType({ description: 'Location master as the twin receives it' })\nexport class LocationMaster {\n @Field(type => String, { description: 'ReferenceMaster locations[] as JSON text' })\n locations: string\n\n /**\n * 더 있나 — **이 값을 안 보고 쓰면 조용히 잘린 마스터로 트윈을 세우게 된다.**\n *\n * 거짓이 될 때까지 `next` 를 `after` 로 넘겨 이어 받는다.\n */\n @Field({ description: 'true when more rows remain — keep pulling with next as after' })\n hasMore: boolean\n\n /** 이어 받을 자리. 더 없으면 없다. */\n @Field(type => String, { nullable: true, description: 'cursor for the next page — the last name in this page' })\n next?: string\n\n}\n\n@Resolver(OperationalLocation)\nexport class LocationQuery {\n /** 자리 축 전체 — 자리가 상위 자리를 가리키므로 한 번에 낸다. */\n @Query(returns => LocationMaster, { description: 'Location master for the twin to pull' })\n async locationMaster(\n @Arg('after', { nullable: true, description: 'resume after this name — the previous page\\'s next' }) after: string | undefined,\n @Arg('limit', type => Int, { nullable: true, description: 'rows per page — omit only when the axis is small enough to pull whole' }) limit: number | undefined,\n @Ctx() context: ResolverContext\n ): Promise<LocationMaster> {\n const { domain } = context.state\n\n const request = { after, limit }\n const page = await pullPage<OperationalLocation>(OperationalLocation, domain, request)\n\n /* 통째로 달라고 했는데 한도를 넘으면 **자르지 않고 거절한다**(§`master-page`). */\n refuseIfTooLarge('자리', request, page)\n\n const locations = page.rows\n\n return {\n locations: JSON.stringify(buildLocationMaster(locations).locations),\n hasMore: page.hasMore,\n ...(page.next ? { next: page.next } : {})\n }\n }\n}\n"]}
@@ -31,8 +31,45 @@ export declare class OperationalLocation {
31
31
  parallelism?: number;
32
32
  /** 상위 자리 — 표준 `OperationalLocationChild`(재귀)의 반대 방향. */
33
33
  parentId?: string;
34
+ /**
35
+ * 바로 아래에 몇이 있나. **저장하지 않는다** — 목록 리졸버가 채운다(`attachChildCounts`).
36
+ *
37
+ * 계층을 한 단씩 걷는 화면이 이 값으로 「그 줄에 들어갈 것이 있나」를 말한다. 세어서 넣는 값이라
38
+ * 컬럼이 아니다 — 컬럼으로 두면 자식이 생기고 없어질 때마다 이 줄을 함께 고쳐야 하고, 한 번 빠뜨리면
39
+ * 틀린 개수가 남는다.
40
+ */
41
+ childCount?: number;
34
42
  /** 자리 속성 — 표준 `OperationalLocationProperty`. */
35
43
  properties?: Record<string, unknown>[];
44
+ /**
45
+ * 이 자리에서 도는 **근무조 이름들** — 아래 단이 물려받는다.
46
+ *
47
+ * ── 왜 자리가 드나 (2026-09-07) ─────────────────────────────────────────
48
+ * 근무조는 지금까지 도메인 단위 선언이고 **어디에 걸리는지가 없었다.** 그래서 씨앗이 설비마다
49
+ * 사본을 뿌렸고(`Equipment.workCalendar`), 한 공장 22대가 같은 값을 22벌 들고 있었다. 3교대를
50
+ * 2교대로 바꾸면 22줄을 고쳐야 하고, 한 줄을 빠뜨리면 그 설비만 조용히 옛 근무로 계산된다.
51
+ *
52
+ * 근무조는 **사람의 일정**이다. 사람은 기계 한 대가 아니라 라인·셀에 배치된다. 그래서 자리가
53
+ * 들고 아래가 물려받는다 — 공장에 한 줄 적으면 그 아래 전부가 그것으로 돈다.
54
+ *
55
+ * ── 비어 있는 것이 정상이다 ───────────────────────────────────────────────
56
+ * 채우는 것은 **위와 다를 때뿐**이다. 라인 하나만 주간이면 그 라인에만 적는다. 아무 데도 안 적으면
57
+ * 도메인의 근무조 전부로 떨어져 지금 거동 그대로다 — 이 칸이 생겼다고 기존 공장이 바뀌지 않는다.
58
+ *
59
+ * ── 설비에는 적지 않는다 ──────────────────────────────────────────────────
60
+ * 「이 기계만 24시간 돈다」는 근무조가 다른 것이 아니라 **자기 가동 계획이 다른 것**이다(에이징
61
+ * 로가 그렇다). 그 사실은 `Equipment.workCalendar` — 이 설비가 가동하도록 계획된 시간 — 에 적힌다.
62
+ * 근무조로 적으면 아무도 근무하지 않는 시간이 근무 시간으로 기록된다.
63
+ *
64
+ * ── 거는 단은 work center 급 이상이다 — 규칙이지 제약이 아니다 ────────────
65
+ * ISA-95 Part 1 이 일정과 능력을 work center 에 둔다. 사람은 라인에 배치되지 기계 한 대에
66
+ * 배치되지 않으므로, `Site` · `Area` · `ProductionLine` · `WorkCenter` 가 이 칸을 든다.
67
+ *
68
+ * **거절하지는 않는다.** 아래 단에 적어도 상속이 그것을 먼저 만나 그냥 돈다. 셀 단위 근무가
69
+ * 실제로 있는 현장에서 저장을 막으면 그 현장은 모델링 자체가 안 된다 — 적합성 표에 줄 하나가
70
+ * 서는 것보다 나쁘다. 어긋난 것은 성숙도가 이름과 함께 낸다.
71
+ */
72
+ workShiftNames?: string[];
36
73
  /** 이 자리에 걸리는 `TestSpecification` 참조들 — 구역 온도 기준 같은 것이 여기 붙는다. */
37
74
  testSpecificationIds?: string[];
38
75
  /** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */
@@ -68,11 +68,23 @@ tslib_1.__decorate([
68
68
  (0, type_graphql_1.Field)({ nullable: true }),
69
69
  tslib_1.__metadata("design:type", String)
70
70
  ], OperationalLocation.prototype, "parentId", void 0);
71
+ tslib_1.__decorate([
72
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'How many locations sit directly under this one' }),
73
+ tslib_1.__metadata("design:type", Number)
74
+ ], OperationalLocation.prototype, "childCount", void 0);
71
75
  tslib_1.__decorate([
72
76
  (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
73
- (0, type_graphql_1.Field)(type => String, { nullable: true, description: 'ResourceProperty[] as JSON text' }),
77
+ (0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true, description: 'ResourceProperty[]' }),
74
78
  tslib_1.__metadata("design:type", Array)
75
79
  ], OperationalLocation.prototype, "properties", void 0);
80
+ tslib_1.__decorate([
81
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
82
+ (0, type_graphql_1.Field)(type => [String], {
83
+ nullable: true,
84
+ description: 'work shift names that run here — locations below inherit this unless they say their own'
85
+ }),
86
+ tslib_1.__metadata("design:type", Array)
87
+ ], OperationalLocation.prototype, "workShiftNames", void 0);
76
88
  tslib_1.__decorate([
77
89
  (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
78
90
  (0, type_graphql_1.Field)(type => [String], { nullable: true }),
@@ -126,7 +138,15 @@ exports.OperationalLocation = OperationalLocation = tslib_1.__decorate([
126
138
  (0, typeorm_1.Entity)({ name: 'ops_operational_location' }),
127
139
  (0, typeorm_1.Index)('ix_ops_operational_location_0', (location) => [location.domain, location.name], {
128
140
  unique: true
129
- }),
141
+ })
142
+ /*
143
+ * 상위로 찾는 색인.
144
+ *
145
+ * 화면이 계층을 한 단씩 걷는다 — 「이 자리의 자식」을 부를 때마다 이 칸으로 거른다. 그리고 줄마다 하위
146
+ * 개수를 세므로(`childCount`), 색인이 없으면 한 화면에 표 전체 훑기가 스무 번 일어난다.
147
+ */
148
+ ,
149
+ (0, typeorm_1.Index)('ix_ops_operational_location_1', (location) => [location.domain, location.parentId]),
130
150
  (0, type_graphql_1.ObjectType)({ description: 'ISA-95 OperationalLocation — where resources are placed' })
131
151
  ], OperationalLocation);
132
152
  //# sourceMappingURL=operational-location.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"operational-location.js","sourceRoot":"","sources":["../../../server/service/location/operational-location.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 자리 — **ISA-95 `OperationalLocation`**.\n *\n * 표준 원문은 「자원이 놓이거나 놓일 것으로 예상되는 논리적·물리적 장소」다. 자기 계층을 재귀로 갖고,\n * 설비는 자기 자리를 이것으로 가리킨다. 자리와 설비를 나눠 두는 것은 표준의 구조다.\n *\n * ── 용량을 지어내지 않는다 ──────────────────────────────────────────────────\n * `capacity` 는 그 자리에 몇이 들어가는가다. 모르면 비워 둔다. 실측에서 임의로 1 을 넣었더니 물품\n * 하나에 포화 경고가 났다 — 없는 것보다 나쁜 수다.\n */\n@Entity({ name: 'ops_operational_location' })\n@Index('ix_ops_operational_location_0', (location: OperationalLocation) => [location.domain, location.name], {\n unique: true\n})\n@ObjectType({ description: 'ISA-95 OperationalLocation — where resources are placed' })\nexport class OperationalLocation {\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((location: OperationalLocation) => location.domain)\n domainId?: string\n\n /** 표준 `ID` — 트윈으로 나갈 때 `locations[].id` 가 된다. */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /** 자리 타입 — 도메인 카탈로그가 정한 키(`raw-store` · `weld-station` 같은 것). */\n @Column()\n @Field()\n type: string\n\n /**\n * ISA-95 설비 계층 단 — `ProductionLine` · `WorkCell` · `StorageZone` · `ProcessCell` · `Unit` 등.\n *\n * 적지 않으면 타입으로 알아낸다. 적으면 그것이 기준이다 — 현장이 자기 계층을 더 잘 안다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n level?: string\n\n /** 이 자리에 몇이 들어가나. **모르면 비운다.** */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true })\n capacity?: number\n\n /** 동시에 몇을 처리하나(대기 이론의 서버 수). 표준에 없는 우리 축이다. */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true })\n parallelism?: number\n\n /** 상위 자리 — 표준 `OperationalLocationChild`(재귀)의 반대 방향. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n parentId?: string\n\n /** 자리 속성 — 표준 `OperationalLocationProperty`. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => String, { nullable: true, description: 'ResourceProperty[] as JSON text' })\n properties?: Record<string, unknown>[]\n\n /** 이 자리에 걸리는 `TestSpecification` 참조들 — 구역 온도 기준 같은 것이 여기 붙는다. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], { nullable: true })\n testSpecificationIds?: string[]\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((location: OperationalLocation) => location.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((location: OperationalLocation) => location.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"operational-location.js","sourceRoot":"","sources":["../../../server/service/location/operational-location.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 자리 — **ISA-95 `OperationalLocation`**.\n *\n * 표준 원문은 「자원이 놓이거나 놓일 것으로 예상되는 논리적·물리적 장소」다. 자기 계층을 재귀로 갖고,\n * 설비는 자기 자리를 이것으로 가리킨다. 자리와 설비를 나눠 두는 것은 표준의 구조다.\n *\n * ── 용량을 지어내지 않는다 ──────────────────────────────────────────────────\n * `capacity` 는 그 자리에 몇이 들어가는가다. 모르면 비워 둔다. 실측에서 임의로 1 을 넣었더니 물품\n * 하나에 포화 경고가 났다 — 없는 것보다 나쁜 수다.\n */\n@Entity({ name: 'ops_operational_location' })\n@Index('ix_ops_operational_location_0', (location: OperationalLocation) => [location.domain, location.name], {\n unique: true\n})\n/*\n * 상위로 찾는 색인.\n *\n * 화면이 계층을 한 단씩 걷는다 — 「이 자리의 자식」을 부를 때마다 이 칸으로 거른다. 그리고 줄마다 하위\n * 개수를 세므로(`childCount`), 색인이 없으면 한 화면에 표 전체 훑기가 스무 번 일어난다.\n */\n@Index('ix_ops_operational_location_1', (location: OperationalLocation) => [location.domain, location.parentId])\n@ObjectType({ description: 'ISA-95 OperationalLocation — where resources are placed' })\nexport class OperationalLocation {\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((location: OperationalLocation) => location.domain)\n domainId?: string\n\n /** 표준 `ID` — 트윈으로 나갈 때 `locations[].id` 가 된다. */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /** 자리 타입 — 도메인 카탈로그가 정한 키(`raw-store` · `weld-station` 같은 것). */\n @Column()\n @Field()\n type: string\n\n /**\n * ISA-95 설비 계층 단 — `ProductionLine` · `WorkCell` · `StorageZone` · `ProcessCell` · `Unit` 등.\n *\n * 적지 않으면 타입으로 알아낸다. 적으면 그것이 기준이다 — 현장이 자기 계층을 더 잘 안다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n level?: string\n\n /** 이 자리에 몇이 들어가나. **모르면 비운다.** */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true })\n capacity?: number\n\n /** 동시에 몇을 처리하나(대기 이론의 서버 수). 표준에 없는 우리 축이다. */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true })\n parallelism?: number\n\n /** 상위 자리 — 표준 `OperationalLocationChild`(재귀)의 반대 방향. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n parentId?: string\n\n /**\n * 바로 아래에 몇이 있나. **저장하지 않는다** — 목록 리졸버가 채운다(`attachChildCounts`).\n *\n * 계층을 한 단씩 걷는 화면이 이 값으로 「그 줄에 들어갈 것이 있나」를 말한다. 세어서 넣는 값이라\n * 컬럼이 아니다 — 컬럼으로 두면 자식이 생기고 없어질 때마다 이 줄을 함께 고쳐야 하고, 한 번 빠뜨리면\n * 틀린 개수가 남는다.\n */\n @Field(type => Int, { nullable: true, description: 'How many locations sit directly under this one' })\n childCount?: number\n\n /** 자리 속성 — 표준 `OperationalLocationProperty`. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'ResourceProperty[]' })\n properties?: Record<string, unknown>[]\n\n /**\n * 이 자리에서 도는 **근무조 이름들** — 아래 단이 물려받는다.\n *\n * ── 왜 자리가 드나 (2026-09-07) ─────────────────────────────────────────\n * 근무조는 지금까지 도메인 단위 선언이고 **어디에 걸리는지가 없었다.** 그래서 씨앗이 설비마다\n * 사본을 뿌렸고(`Equipment.workCalendar`), 한 공장 22대가 같은 값을 22벌 들고 있었다. 3교대를\n * 2교대로 바꾸면 22줄을 고쳐야 하고, 한 줄을 빠뜨리면 그 설비만 조용히 옛 근무로 계산된다.\n *\n * 근무조는 **사람의 일정**이다. 사람은 기계 한 대가 아니라 라인·셀에 배치된다. 그래서 자리가\n * 들고 아래가 물려받는다 — 공장에 한 줄 적으면 그 아래 전부가 그것으로 돈다.\n *\n * ── 비어 있는 것이 정상이다 ───────────────────────────────────────────────\n * 채우는 것은 **위와 다를 때뿐**이다. 라인 하나만 주간이면 그 라인에만 적는다. 아무 데도 안 적으면\n * 도메인의 근무조 전부로 떨어져 지금 거동 그대로다 — 이 칸이 생겼다고 기존 공장이 바뀌지 않는다.\n *\n * ── 설비에는 적지 않는다 ──────────────────────────────────────────────────\n * 「이 기계만 24시간 돈다」는 근무조가 다른 것이 아니라 **자기 가동 계획이 다른 것**이다(에이징\n * 로가 그렇다). 그 사실은 `Equipment.workCalendar` — 이 설비가 가동하도록 계획된 시간 — 에 적힌다.\n * 근무조로 적으면 아무도 근무하지 않는 시간이 근무 시간으로 기록된다.\n *\n * ── 거는 단은 work center 급 이상이다 — 규칙이지 제약이 아니다 ────────────\n * ISA-95 Part 1 이 일정과 능력을 work center 에 둔다. 사람은 라인에 배치되지 기계 한 대에\n * 배치되지 않으므로, `Site` · `Area` · `ProductionLine` · `WorkCenter` 가 이 칸을 든다.\n *\n * **거절하지는 않는다.** 아래 단에 적어도 상속이 그것을 먼저 만나 그냥 돈다. 셀 단위 근무가\n * 실제로 있는 현장에서 저장을 막으면 그 현장은 모델링 자체가 안 된다 — 적합성 표에 줄 하나가\n * 서는 것보다 나쁘다. 어긋난 것은 성숙도가 이름과 함께 낸다.\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], {\n nullable: true,\n description: 'work shift names that run here — locations below inherit this unless they say their own'\n })\n workShiftNames?: string[]\n\n /** 이 자리에 걸리는 `TestSpecification` 참조들 — 구역 온도 기준 같은 것이 여기 붙는다. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], { nullable: true })\n testSpecificationIds?: string[]\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((location: OperationalLocation) => location.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((location: OperationalLocation) => location.updater)\n updaterId?: string\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { ObjectLiteral } from 'typeorm';
2
2
  /**
3
- * 마스터 열두 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.
3
+ * 마스터 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.
4
4
  *
5
5
  * ── 왜 표로 두나 ───────────────────────────────────────────────────────────
6
6
  * 축이 열둘인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열두 벌이 되고,
@@ -13,6 +13,8 @@ const person_1 = require("./personnel/person");
13
13
  const personnel_class_1 = require("./personnel/personnel-class");
14
14
  const physical_asset_1 = require("./asset/physical-asset");
15
15
  const physical_asset_class_1 = require("./asset/physical-asset-class");
16
+ const recipe_definition_1 = require("./recipe/recipe-definition");
17
+ const route_definition_1 = require("./recipe/route-definition");
16
18
  const test_specification_1 = require("./test-spec/test-specification");
17
19
  exports.MASTER_AXES = [
18
20
  { key: 'personnelClass', entity: personnel_class_1.PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true },
@@ -26,7 +28,20 @@ exports.MASTER_AXES = [
26
28
  { key: 'materialDefinition', entity: material_definition_1.MaterialDefinition, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },
27
29
  { key: 'operationalLocation', entity: operational_location_1.OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true },
28
30
  { key: 'operationDefinition', entity: operation_definition_1.OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
29
- { key: 'testSpecification', entity: test_specification_1.TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true }
31
+ { key: 'testSpecification', entity: test_specification_1.TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true },
32
+ /*
33
+ * 라우팅과 레시피 (2026-08-31) — 커널에 `RouteDef`·`RecipeDef` 가 있고 `JobOrder.routeKey` 가 그것을
34
+ * 가리키는데 **그 값이 가리킬 표가 없었다.** 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은
35
+ * 작업지시에 라우팅을 자동으로 붙일 수 없었다.
36
+ */
37
+ { key: 'routeDefinition', entity: route_definition_1.RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
38
+ {
39
+ key: 'recipeDefinition',
40
+ entity: recipe_definition_1.RecipeDefinition,
41
+ /* 만드는 품목으로도 찾는다 — 「이 품목을 만드는 레시피」가 MES 가 지시마다 묻는 물음이다. */
42
+ searchables: ['name', 'description', 'materialDefinitionName'],
43
+ hasEffectivePeriod: true
44
+ }
30
45
  ];
31
46
  function masterAxisOf(key) {
32
47
  const axis = exports.MASTER_AXES.find(candidate => candidate.key === key);
@@ -1 +1 @@
1
- {"version":3,"file":"master-axis.js","sourceRoot":"","sources":["../../server/service/master-axis.ts"],"names":[],"mappings":";;;AAwDA,oCASC;AA/DD,qDAAiD;AACjD,iEAA4D;AAC5D,yDAAoD;AACpD,8DAAyD;AACzD,wEAAmE;AACnE,2EAAsE;AACtE,0EAAqE;AACrE,+CAA2C;AAC3C,iEAA4D;AAC5D,2DAAsD;AACtD,uEAAiE;AACjE,uEAAkE;AA4BrD,QAAA,WAAW,GAA0B;IAChD,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,gCAAc,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACjH,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAM,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACjG,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,gCAAc,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACjH,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,qBAAS,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACvG,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,wBAAU,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACzG,EAAE,GAAG,EAAE,oBAAoB,EAAE,MAAM,EAAE,yCAAkB,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACzH,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,8BAAa,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IAC3H,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,8BAAa,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IAC/G,EAAE,GAAG,EAAE,oBAAoB,EAAE,MAAM,EAAE,wCAAkB,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IACrI,EAAE,GAAG,EAAE,qBAAqB,EAAE,MAAM,EAAE,0CAAmB,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IAC3H,EAAE,GAAG,EAAE,qBAAqB,EAAE,MAAM,EAAE,0CAAmB,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;IAC3H,EAAE,GAAG,EAAE,mBAAmB,EAAE,MAAM,EAAE,sCAAiB,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE;CACxH,CAAA;AAED,SAAgB,YAAY,CAAC,GAAW;IACtC,MAAM,IAAI,GAAG,mBAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAA;IAEjE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,2DAA2D;QAC3D,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,GAAG,CAAC,CAAA;IAC/D,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import { ObjectLiteral } from 'typeorm'\n\nimport { Equipment } from './equipment/equipment'\nimport { EquipmentClass } from './equipment/equipment-class'\nimport { StopReason } from './equipment/stop-reason'\nimport { MaterialClass } from './material/material-class'\nimport { MaterialDefinition } from './material/material-definition'\nimport { OperationDefinition } from './operation/operation-definition'\nimport { OperationalLocation } from './location/operational-location'\nimport { Person } from './personnel/person'\nimport { PersonnelClass } from './personnel/personnel-class'\nimport { PhysicalAsset } from './asset/physical-asset'\nimport { PhysicalAssetClass } from './asset/physical-asset-class'\nimport { TestSpecification } from './test-spec/test-specification'\n\n/**\n * 마스터 열두 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.\n *\n * ── 왜 표로 두나 ───────────────────────────────────────────────────────────\n * 축이 열둘인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열두 벌이 되고,\n * 규칙 하나를 고칠 때 열두 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데\n * 오류는 나지 않는다.\n *\n * 그래서 **다른 것만** 적는다 — 어느 표인가, 무엇으로 찾는가, 없앨 때 지우는가 끝을 채우는가.\n *\n * ── 왜 유효 기간 여부가 여기 있나 ──────────────────────────────────────────\n * 지난 기록이 가리키는 축은 지우면 안 된다. 그것이 축마다 다르므로 축의 성질로 적는다. 정지 사유는\n * 유효 기간이 있고, 등급도 있다. 지금 열둘 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이\n * 생기면 그때 이 표만 고치면 된다.\n */\n\nexport interface MasterAxis {\n /** GraphQL 이름의 바탕. `person` → `persons` · `updateMultiplePerson`. */\n key: string\n entity: { new (): ObjectLiteral }\n /** 검색어가 걸리는 칸들. */\n searchables: string[]\n /** 없앨 때 행을 지우지 않고 `effectiveEnd` 를 채우나. */\n hasEffectivePeriod: boolean\n}\n\nexport const MASTER_AXES: readonly MasterAxis[] = [\n { key: 'personnelClass', entity: PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'person', entity: Person, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipmentClass', entity: EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipment', entity: Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'stopReason', entity: StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAssetClass', entity: PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAsset', entity: PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'materialClass', entity: MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'materialDefinition', entity: MaterialDefinition, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'operationalLocation', entity: OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'operationDefinition', entity: OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'testSpecification', entity: TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true }\n]\n\nexport function masterAxisOf(key: string): MasterAxis {\n const axis = MASTER_AXES.find(candidate => candidate.key === key)\n\n if (!axis) {\n /* 없는 축을 조용히 지나치지 않는다 — 지나치면 그 축의 화면이 빈 목록을 보이고 아무도 모른다. */\n throw new Error(`masterAxisOf: unknown master axis \"${key}\"`)\n }\n\n return axis\n}\n"]}
1
+ {"version":3,"file":"master-axis.js","sourceRoot":"","sources":["../../server/service/master-axis.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectLiteral } from 'typeorm'\n\nimport { Equipment } from './equipment/equipment'\nimport { EquipmentClass } from './equipment/equipment-class'\nimport { StopReason } from './equipment/stop-reason'\nimport { MaterialClass } from './material/material-class'\nimport { MaterialDefinition } from './material/material-definition'\nimport { OperationDefinition } from './operation/operation-definition'\nimport { OperationalLocation } from './location/operational-location'\nimport { Person } from './personnel/person'\nimport { PersonnelClass } from './personnel/personnel-class'\nimport { PhysicalAsset } from './asset/physical-asset'\nimport { PhysicalAssetClass } from './asset/physical-asset-class'\nimport { RecipeDefinition } from './recipe/recipe-definition'\nimport { RouteDefinition } from './recipe/route-definition'\nimport { TestSpecification } from './test-spec/test-specification'\n\n/**\n * 마스터 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.\n *\n * ── 왜 표로 두나 ───────────────────────────────────────────────────────────\n * 축이 열둘인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열두 벌이 되고,\n * 규칙 하나를 고칠 때 열두 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데\n * 오류는 나지 않는다.\n *\n * 그래서 **다른 것만** 적는다 — 어느 표인가, 무엇으로 찾는가, 없앨 때 지우는가 끝을 채우는가.\n *\n * ── 왜 유효 기간 여부가 여기 있나 ──────────────────────────────────────────\n * 지난 기록이 가리키는 축은 지우면 안 된다. 그것이 축마다 다르므로 축의 성질로 적는다. 정지 사유는\n * 유효 기간이 있고, 등급도 있다. 지금 열둘 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이\n * 생기면 그때 이 표만 고치면 된다.\n */\n\nexport interface MasterAxis {\n /** GraphQL 이름의 바탕. `person` → `persons` · `updateMultiplePerson`. */\n key: string\n entity: { new (): ObjectLiteral }\n /** 검색어가 걸리는 칸들. */\n searchables: string[]\n /** 없앨 때 행을 지우지 않고 `effectiveEnd` 를 채우나. */\n hasEffectivePeriod: boolean\n}\n\nexport const MASTER_AXES: readonly MasterAxis[] = [\n { key: 'personnelClass', entity: PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'person', entity: Person, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipmentClass', entity: EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipment', entity: Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'stopReason', entity: StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAssetClass', entity: PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAsset', entity: PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'materialClass', entity: MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'materialDefinition', entity: MaterialDefinition, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'operationalLocation', entity: OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'operationDefinition', entity: OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'testSpecification', entity: TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true },\n /*\n * 라우팅과 레시피 (2026-08-31) — 커널에 `RouteDef`·`RecipeDef` 가 있고 `JobOrder.routeKey` 가 그것을\n * 가리키는데 **그 값이 가리킬 표가 없었다.** 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은\n * 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n */\n { key: 'routeDefinition', entity: RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },\n {\n key: 'recipeDefinition',\n entity: RecipeDefinition,\n /* 만드는 품목으로도 찾는다 — 「이 품목을 만드는 레시피」가 MES 가 지시마다 묻는 물음이다. */\n searchables: ['name', 'description', 'materialDefinitionName'],\n hasEffectivePeriod: true\n }\n]\n\nexport function masterAxisOf(key: string): MasterAxis {\n const axis = MASTER_AXES.find(candidate => candidate.key === key)\n\n if (!axis) {\n /* 없는 축을 조용히 지나치지 않는다 — 지나치면 그 축의 화면이 빈 목록을 보이고 아무도 모른다. */\n throw new Error(`masterAxisOf: unknown master axis \"${key}\"`)\n }\n\n return axis\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 마스터 정합성 — **사람이 눌러서 도는 검사.**
3
+ *
4
+ * 목록 화면이 매번 부르는 것이 아니다. 표를 통째로 읽어야 답이 나오고, 상한을 두면 「어긋남 0건」이
5
+ * 거짓이 되기 때문이다. 정합성은 「전부 봤나」가 곧 답이다.
6
+ */
7
+ export declare class MasterFinding {
8
+ axis: string;
9
+ subject?: string;
10
+ kind: string;
11
+ field: string;
12
+ value?: string;
13
+ }
14
+ export declare class MasterFindingCount {
15
+ axis: string;
16
+ kind: string;
17
+ count: number;
18
+ }
19
+ export declare class MasterScanned {
20
+ axis: string;
21
+ rows: number;
22
+ }
23
+ export declare class MasterConformance {
24
+ /**
25
+ * 무엇을 몇 개 봤나. **어긋남 0건이 「멀쩡하다」인지 「안 돌았다」인지는 이것으로만 갈린다.**
26
+ */
27
+ scanned: MasterScanned[];
28
+ total: number;
29
+ counts: MasterFindingCount[];
30
+ findings: MasterFinding[];
31
+ }
32
+ export declare class MasterConformanceResolver {
33
+ masterConformance(context: ResolverContext): Promise<MasterConformance>;
34
+ }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MasterConformanceResolver = exports.MasterConformance = exports.MasterScanned = exports.MasterFindingCount = exports.MasterFinding = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const physical_asset_1 = require("./asset/physical-asset");
8
+ const physical_asset_class_1 = require("./asset/physical-asset-class");
9
+ const equipment_1 = require("./equipment/equipment");
10
+ const equipment_class_1 = require("./equipment/equipment-class");
11
+ const stop_reason_1 = require("./equipment/stop-reason");
12
+ const operational_location_1 = require("./location/operational-location");
13
+ const material_class_1 = require("./material/material-class");
14
+ const material_definition_1 = require("./material/material-definition");
15
+ const operation_definition_1 = require("./operation/operation-definition");
16
+ const person_1 = require("./personnel/person");
17
+ const personnel_class_1 = require("./personnel/personnel-class");
18
+ const recipe_definition_1 = require("./recipe/recipe-definition");
19
+ const route_definition_1 = require("./recipe/route-definition");
20
+ const test_specification_1 = require("./test-spec/test-specification");
21
+ const master_conformance_1 = require("./master-conformance");
22
+ /**
23
+ * 마스터 정합성 — **사람이 눌러서 도는 검사.**
24
+ *
25
+ * 목록 화면이 매번 부르는 것이 아니다. 표를 통째로 읽어야 답이 나오고, 상한을 두면 「어긋남 0건」이
26
+ * 거짓이 되기 때문이다. 정합성은 「전부 봤나」가 곧 답이다.
27
+ */
28
+ let MasterFinding = class MasterFinding {
29
+ };
30
+ exports.MasterFinding = MasterFinding;
31
+ tslib_1.__decorate([
32
+ (0, type_graphql_1.Field)(),
33
+ tslib_1.__metadata("design:type", String)
34
+ ], MasterFinding.prototype, "axis", void 0);
35
+ tslib_1.__decorate([
36
+ (0, type_graphql_1.Field)({ nullable: true }),
37
+ tslib_1.__metadata("design:type", String)
38
+ ], MasterFinding.prototype, "subject", void 0);
39
+ tslib_1.__decorate([
40
+ (0, type_graphql_1.Field)(),
41
+ tslib_1.__metadata("design:type", String)
42
+ ], MasterFinding.prototype, "kind", void 0);
43
+ tslib_1.__decorate([
44
+ (0, type_graphql_1.Field)(),
45
+ tslib_1.__metadata("design:type", String)
46
+ ], MasterFinding.prototype, "field", void 0);
47
+ tslib_1.__decorate([
48
+ (0, type_graphql_1.Field)({ nullable: true }),
49
+ tslib_1.__metadata("design:type", String)
50
+ ], MasterFinding.prototype, "value", void 0);
51
+ exports.MasterFinding = MasterFinding = tslib_1.__decorate([
52
+ (0, type_graphql_1.ObjectType)({ description: 'One master data conformance finding' })
53
+ ], MasterFinding);
54
+ let MasterFindingCount = class MasterFindingCount {
55
+ };
56
+ exports.MasterFindingCount = MasterFindingCount;
57
+ tslib_1.__decorate([
58
+ (0, type_graphql_1.Field)(),
59
+ tslib_1.__metadata("design:type", String)
60
+ ], MasterFindingCount.prototype, "axis", void 0);
61
+ tslib_1.__decorate([
62
+ (0, type_graphql_1.Field)(),
63
+ tslib_1.__metadata("design:type", String)
64
+ ], MasterFindingCount.prototype, "kind", void 0);
65
+ tslib_1.__decorate([
66
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int),
67
+ tslib_1.__metadata("design:type", Number)
68
+ ], MasterFindingCount.prototype, "count", void 0);
69
+ exports.MasterFindingCount = MasterFindingCount = tslib_1.__decorate([
70
+ (0, type_graphql_1.ObjectType)({ description: 'How many findings per axis and kind' })
71
+ ], MasterFindingCount);
72
+ let MasterScanned = class MasterScanned {
73
+ };
74
+ exports.MasterScanned = MasterScanned;
75
+ tslib_1.__decorate([
76
+ (0, type_graphql_1.Field)(),
77
+ tslib_1.__metadata("design:type", String)
78
+ ], MasterScanned.prototype, "axis", void 0);
79
+ tslib_1.__decorate([
80
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int),
81
+ tslib_1.__metadata("design:type", Number)
82
+ ], MasterScanned.prototype, "rows", void 0);
83
+ exports.MasterScanned = MasterScanned = tslib_1.__decorate([
84
+ (0, type_graphql_1.ObjectType)({ description: 'How many rows were scanned per axis' })
85
+ ], MasterScanned);
86
+ let MasterConformance = class MasterConformance {
87
+ };
88
+ exports.MasterConformance = MasterConformance;
89
+ tslib_1.__decorate([
90
+ (0, type_graphql_1.Field)(type => [MasterScanned]),
91
+ tslib_1.__metadata("design:type", Array)
92
+ ], MasterConformance.prototype, "scanned", void 0);
93
+ tslib_1.__decorate([
94
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'How many findings in total' }),
95
+ tslib_1.__metadata("design:type", Number)
96
+ ], MasterConformance.prototype, "total", void 0);
97
+ tslib_1.__decorate([
98
+ (0, type_graphql_1.Field)(type => [MasterFindingCount]),
99
+ tslib_1.__metadata("design:type", Array)
100
+ ], MasterConformance.prototype, "counts", void 0);
101
+ tslib_1.__decorate([
102
+ (0, type_graphql_1.Field)(type => [MasterFinding]),
103
+ tslib_1.__metadata("design:type", Array)
104
+ ], MasterConformance.prototype, "findings", void 0);
105
+ exports.MasterConformance = MasterConformance = tslib_1.__decorate([
106
+ (0, type_graphql_1.ObjectType)({ description: 'Master data conformance report' })
107
+ ], MasterConformance);
108
+ let MasterConformanceResolver = class MasterConformanceResolver {
109
+ async masterConformance(context) {
110
+ const { domain } = context.state;
111
+ const snapshot = await (0, master_conformance_1.readMasterSnapshot)(domain, shell_1.getRepository, {
112
+ Person: person_1.Person,
113
+ PersonnelClass: personnel_class_1.PersonnelClass,
114
+ Equipment: equipment_1.Equipment,
115
+ EquipmentClass: equipment_class_1.EquipmentClass,
116
+ PhysicalAsset: physical_asset_1.PhysicalAsset,
117
+ PhysicalAssetClass: physical_asset_class_1.PhysicalAssetClass,
118
+ MaterialDefinition: material_definition_1.MaterialDefinition,
119
+ MaterialClass: material_class_1.MaterialClass,
120
+ OperationDefinition: operation_definition_1.OperationDefinition,
121
+ RouteDefinition: route_definition_1.RouteDefinition,
122
+ RecipeDefinition: recipe_definition_1.RecipeDefinition,
123
+ OperationalLocation: operational_location_1.OperationalLocation,
124
+ StopReason: stop_reason_1.StopReason,
125
+ TestSpecification: test_specification_1.TestSpecification
126
+ });
127
+ const findings = (0, master_conformance_1.checkMaster)(snapshot);
128
+ return { scanned: (0, master_conformance_1.scannedCounts)(snapshot), total: findings.length, counts: (0, master_conformance_1.summarize)(findings), findings };
129
+ }
130
+ };
131
+ exports.MasterConformanceResolver = MasterConformanceResolver;
132
+ tslib_1.__decorate([
133
+ (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
134
+ (0, type_graphql_1.Query)(returns => MasterConformance, { description: 'Check master data for missing fields and dangling references' }),
135
+ tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
136
+ tslib_1.__metadata("design:type", Function),
137
+ tslib_1.__metadata("design:paramtypes", [Object]),
138
+ tslib_1.__metadata("design:returntype", Promise)
139
+ ], MasterConformanceResolver.prototype, "masterConformance", null);
140
+ exports.MasterConformanceResolver = MasterConformanceResolver = tslib_1.__decorate([
141
+ (0, type_graphql_1.Resolver)()
142
+ ], MasterConformanceResolver);
143
+ //# sourceMappingURL=master-conformance-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"master-conformance-resolver.js","sourceRoot":"","sources":["../../server/service/master-conformance-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Directive, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\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 { StopReason } from './equipment/stop-reason'\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'\nimport { checkMaster, readMasterSnapshot, scannedCounts, summarize } from './master-conformance'\n\n/**\n * 마스터 정합성 — **사람이 눌러서 도는 검사.**\n *\n * 목록 화면이 매번 부르는 것이 아니다. 표를 통째로 읽어야 답이 나오고, 상한을 두면 「어긋남 0건」이\n * 거짓이 되기 때문이다. 정합성은 「전부 봤나」가 곧 답이다.\n */\n\n@ObjectType({ description: 'One master data conformance finding' })\nexport class MasterFinding {\n @Field()\n axis: string\n\n @Field({ nullable: true })\n subject?: string\n\n @Field()\n kind: string\n\n @Field()\n field: string\n\n @Field({ nullable: true })\n value?: string\n}\n\n@ObjectType({ description: 'How many findings per axis and kind' })\nexport class MasterFindingCount {\n @Field()\n axis: string\n\n @Field()\n kind: string\n\n @Field(type => Int)\n count: number\n}\n\n@ObjectType({ description: 'How many rows were scanned per axis' })\nexport class MasterScanned {\n @Field()\n axis: string\n\n @Field(type => Int)\n rows: number\n}\n\n@ObjectType({ description: 'Master data conformance report' })\nexport class MasterConformance {\n /**\n * 무엇을 몇 개 봤나. **어긋남 0건이 「멀쩡하다」인지 「안 돌았다」인지는 이것으로만 갈린다.**\n */\n @Field(type => [MasterScanned])\n scanned: MasterScanned[]\n\n @Field(type => Int, { description: 'How many findings in total' })\n total: number\n\n @Field(type => [MasterFindingCount])\n counts: MasterFindingCount[]\n\n @Field(type => [MasterFinding])\n findings: MasterFinding[]\n}\n\n@Resolver()\nexport class MasterConformanceResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => MasterConformance, { description: 'Check master data for missing fields and dangling references' })\n async masterConformance(@Ctx() context: ResolverContext): Promise<MasterConformance> {\n const { domain } = context.state\n\n const snapshot = await readMasterSnapshot(domain, getRepository, {\n Person,\n PersonnelClass,\n Equipment,\n EquipmentClass,\n PhysicalAsset,\n PhysicalAssetClass,\n MaterialDefinition,\n MaterialClass,\n OperationDefinition,\n RouteDefinition,\n RecipeDefinition,\n OperationalLocation,\n StopReason,\n TestSpecification\n })\n\n const findings = checkMaster(snapshot)\n\n return { scanned: scannedCounts(snapshot), total: findings.length, counts: summarize(findings), findings }\n }\n}\n"]}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * 마스터가 **말이 되나** — 축을 가로질러 한 번에 본다.
3
+ *
4
+ * ── 왜 필요한가 ────────────────────────────────────────────────────────────
5
+ * 축마다 화면이 있고 축마다 값이 차 있는데, 그 사이가 이어지는지는 아무도 안 봤다. 설비 등급 다섯이
6
+ * 서 있고 설비 스물둘이 어느 등급에도 안 들어 있던 일이 그랬다 — 두 화면 다 멀쩡해 보였다.
7
+ * 카메라 자산 다섯이 ECU 등급에 들어 있던 것도, 정지 사유 고르기가 빈 목록으로 보이던 것도 같다.
8
+ *
9
+ * 사람이 열두 화면을 오가며 맞춰 보는 것으로는 안 된다. 맞춰 보지 않으면 아무도 모르고, 맞춰 봤는지
10
+ * 아닌지도 남지 않는다.
11
+ *
12
+ * ── 두 가지만 본다 ─────────────────────────────────────────────────────────
13
+ * **비어 있으면 안 되는 칸**과 **가리키는데 없는 것**이다. 둘 다 사실 판정이라 다툼이 없다.
14
+ * 「이 값이 옳은가」는 여기서 안 본다 — 그것은 현장이 정한다.
15
+ *
16
+ * ── 없는 것을 없다고 말한다 ────────────────────────────────────────────────
17
+ * 표가 통째로 비었으면 그것도 낸다. 「빠진 것 0건」과 「아직 아무것도 안 세웠다」는 다르고, 세지
18
+ * 않으면 둘이 같아 보인다.
19
+ */
20
+ /** 한 건의 어긋남. 사실만 담는다 — 무엇을, 어디서, 무엇이. */
21
+ export interface Finding {
22
+ /** 어느 축인가. `person` · `equipment` · `recipeDefinition` … */
23
+ axis: string;
24
+ /** 어느 줄인가. 표가 통째로 빈 경우에는 없다. */
25
+ subject?: string;
26
+ /** `missing` = 비어 있으면 안 되는 칸이 비었다 · `dangling` = 가리키는데 그런 것이 없다 · `empty` = 표가 비었다 · `unused` = 아무도 안 가리킨다 */
27
+ kind: 'missing' | 'dangling' | 'empty' | 'unused';
28
+ /** 어느 칸인가(`missing`·`dangling`), 또는 무엇이 비었나(`empty`·`unused`). */
29
+ field: string;
30
+ /** 가리키던 값. `dangling` 일 때만. */
31
+ value?: string;
32
+ }
33
+ /** 한 축이 내놓는 줄들. 이름과 칸 몇 개만 본다. */
34
+ export type Row = Record<string, any>;
35
+ export interface MasterSnapshot {
36
+ persons: Row[];
37
+ equipment: Row[];
38
+ physicalAssets: Row[];
39
+ materialDefinitions: Row[];
40
+ operationDefinitions: Row[];
41
+ routeDefinitions: Row[];
42
+ recipeDefinitions: Row[];
43
+ operationalLocations: Row[];
44
+ stopReasons: Row[];
45
+ testSpecifications: Row[];
46
+ personnelClasses: Row[];
47
+ equipmentClasses: Row[];
48
+ physicalAssetClasses: Row[];
49
+ materialClasses: Row[];
50
+ }
51
+ /**
52
+ * 마스터 전체를 잰다.
53
+ *
54
+ * **순서에 뜻이 없다.** 어느 축부터 보든 같은 답이 나와야 하고, 그래야 이 결과를 두 번 재서
55
+ * 견줄 수 있다.
56
+ */
57
+ export declare function checkMaster(snapshot: MasterSnapshot): Finding[];
58
+ /**
59
+ * **무엇을 몇 개 봤나.**
60
+ *
61
+ * 어긋남 0건이 「멀쩡하다」인지 「안 돌았다」인지는 이것으로만 갈린다. 본 수를 안 내면 두 답이
62
+ * 같은 글자로 나온다 — 검사기가 스스로를 못 믿게 만드는 가장 흔한 방식이다.
63
+ */
64
+ export declare function scannedCounts(snapshot: MasterSnapshot): {
65
+ axis: string;
66
+ rows: number;
67
+ }[];
68
+ /** 축·종류별로 몇 건인지. 사람이 먼저 볼 것을 고르는 데 쓴다. */
69
+ export declare function summarize(findings: Finding[]): {
70
+ axis: string;
71
+ kind: string;
72
+ count: number;
73
+ }[];
74
+ /**
75
+ * 한 도메인의 마스터를 통째로 읽어 잰다.
76
+ *
77
+ * **상한을 두지 않는다.** 정합성은 「전부 봤나」가 곧 답이라, 일부만 보고 「어긋남 0건」이라고 하면
78
+ * 그 답이 거짓이 된다. 그래서 이것은 목록 화면이 부르는 것이 아니라 **사람이 눌러서 도는 검사**다.
79
+ */
80
+ export declare function readMasterSnapshot(domain: any, getRepository: any, entities: Record<string, any>): Promise<MasterSnapshot>;