@things-factory/ops-master 10.1.31 → 10.1.34

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 (59) hide show
  1. package/dist-client/index.d.ts +1 -0
  2. package/dist-client/index.js +1 -0
  3. package/dist-client/index.js.map +1 -1
  4. package/dist-client/master-change-approval.d.ts +7 -0
  5. package/dist-client/master-change-approval.js +20 -0
  6. package/dist-client/master-change-approval.js.map +1 -0
  7. package/dist-client/pages/master/test-specification.d.ts +22 -85
  8. package/dist-client/pages/master/test-specification.js +190 -405
  9. package/dist-client/pages/master/test-specification.js.map +1 -1
  10. package/dist-client/tsconfig.tsbuildinfo +1 -1
  11. package/dist-server/service/asset/asset-master-resolver.js +38 -34
  12. package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
  13. package/dist-server/service/equipment/equipment-master-resolver.js +54 -49
  14. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  15. package/dist-server/service/index.d.ts +8 -2
  16. package/dist-server/service/index.js +8 -0
  17. package/dist-server/service/index.js.map +1 -1
  18. package/dist-server/service/location/location-master-resolver.js +34 -31
  19. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  20. package/dist-server/service/master-change-policy.d.ts +23 -0
  21. package/dist-server/service/master-change-policy.js +129 -0
  22. package/dist-server/service/master-change-policy.js.map +1 -0
  23. package/dist-server/service/master-crud.js +6 -0
  24. package/dist-server/service/master-crud.js.map +1 -1
  25. package/dist-server/service/material/material-master-resolver.js +55 -51
  26. package/dist-server/service/material/material-master-resolver.js.map +1 -1
  27. package/dist-server/service/operation/operation-master-resolver.js +28 -25
  28. package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
  29. package/dist-server/service/personnel/personnel-master-resolver.js +38 -34
  30. package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
  31. package/dist-server/service/quality/nonconformance-cause-resolver.js +17 -14
  32. package/dist-server/service/quality/nonconformance-cause-resolver.js.map +1 -1
  33. package/dist-server/service/recipe/recipe-master-resolver.js +32 -28
  34. package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -1
  35. package/dist-server/service/test-spec/index.d.ts +3 -2
  36. package/dist-server/service/test-spec/index.js +4 -2
  37. package/dist-server/service/test-spec/index.js.map +1 -1
  38. package/dist-server/service/test-spec/test-spec-effective-list.d.ts +32 -0
  39. package/dist-server/service/test-spec/test-spec-effective-list.js +40 -0
  40. package/dist-server/service/test-spec/test-spec-effective-list.js.map +1 -0
  41. package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +4 -14
  42. package/dist-server/service/test-spec/test-spec-master-resolver.js +12 -83
  43. package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
  44. package/dist-server/service/test-spec/test-spec-query.js +7 -1
  45. package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
  46. package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +52 -0
  47. package/dist-server/service/test-spec/test-spec-revision-resolver.js +76 -0
  48. package/dist-server/service/test-spec/test-spec-revision-resolver.js.map +1 -0
  49. package/dist-server/service/test-spec/test-spec-revision-service.d.ts +117 -0
  50. package/dist-server/service/test-spec/test-spec-revision-service.js +519 -0
  51. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -0
  52. package/dist-server/service/test-spec/test-spec-revision.d.ts +82 -0
  53. package/dist-server/service/test-spec/test-spec-revision.js +329 -0
  54. package/dist-server/service/test-spec/test-spec-revision.js.map +1 -0
  55. package/dist-server/service/test-spec/test-specification.d.ts +4 -0
  56. package/dist-server/service/test-spec/test-specification.js +11 -2
  57. package/dist-server/service/test-spec/test-specification.js.map +1 -1
  58. package/dist-server/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +9 -6
@@ -4,6 +4,8 @@ exports.MaterialDefinitionResolver = exports.MaterialDefinitionList = exports.Ma
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const shell_1 = require("@things-factory/shell");
7
+ const type_graphql_2 = require("type-graphql");
8
+ const shell_2 = require("@things-factory/shell");
7
9
  const master_crud_1 = require("../master-crud");
8
10
  const master_axis_1 = require("../master-axis");
9
11
  const master_list_1 = require("../master-list");
@@ -27,15 +29,15 @@ let MaterialClassList = class MaterialClassList {
27
29
  };
28
30
  exports.MaterialClassList = MaterialClassList;
29
31
  tslib_1.__decorate([
30
- (0, type_graphql_1.Field)(type => [material_class_1.MaterialClass]),
32
+ (0, type_graphql_2.Field)(type => [material_class_1.MaterialClass]),
31
33
  tslib_1.__metadata("design:type", Array)
32
34
  ], MaterialClassList.prototype, "items", void 0);
33
35
  tslib_1.__decorate([
34
- (0, type_graphql_1.Field)(type => type_graphql_1.Int),
36
+ (0, type_graphql_2.Field)(type => type_graphql_2.Int),
35
37
  tslib_1.__metadata("design:type", Number)
36
38
  ], MaterialClassList.prototype, "total", void 0);
37
39
  exports.MaterialClassList = MaterialClassList = tslib_1.__decorate([
38
- (0, type_graphql_1.ObjectType)({ description: 'A page of material classes' })
40
+ (0, type_graphql_2.ObjectType)({ description: 'A page of material classes' })
39
41
  ], MaterialClassList);
40
42
  /**
41
43
  * **이 등급에 무엇이 드나** — 들어오는 관계.
@@ -87,56 +89,57 @@ let MaterialClassResolver = class MaterialClassResolver {
87
89
  };
88
90
  exports.MaterialClassResolver = MaterialClassResolver;
89
91
  tslib_1.__decorate([
90
- (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
91
- (0, type_graphql_1.Query)(returns => MaterialClassList, { description: 'To fetch material classes' }),
92
- tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
93
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
92
+ (0, type_graphql_2.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
93
+ (0, type_graphql_2.Query)(returns => MaterialClassList, { description: 'To fetch material classes' }),
94
+ tslib_1.__param(0, (0, type_graphql_2.Args)(type => shell_2.ListParam)),
95
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
94
96
  tslib_1.__metadata("design:type", Function),
95
- tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
97
+ tslib_1.__metadata("design:paramtypes", [shell_2.ListParam, Object]),
96
98
  tslib_1.__metadata("design:returntype", Promise)
97
99
  ], MaterialClassResolver.prototype, "materialClasses", null);
98
100
  tslib_1.__decorate([
99
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Materials belonging to this class' }),
100
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
101
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
101
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Materials belonging to this class' }),
102
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
103
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
102
104
  tslib_1.__metadata("design:type", Function),
103
105
  tslib_1.__metadata("design:paramtypes", [material_class_1.MaterialClass, Object]),
104
106
  tslib_1.__metadata("design:returntype", Promise)
105
107
  ], MaterialClassResolver.prototype, "members", null);
106
108
  tslib_1.__decorate([
107
- (0, type_graphql_1.FieldResolver)(type => Boolean, { description: 'True when members were too many to scan fully' }),
108
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
109
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
109
+ (0, type_graphql_2.FieldResolver)(type => Boolean, { description: 'True when members were too many to scan fully' }),
110
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
111
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
110
112
  tslib_1.__metadata("design:type", Function),
111
113
  tslib_1.__metadata("design:paramtypes", [material_class_1.MaterialClass, Object]),
112
114
  tslib_1.__metadata("design:returntype", Promise)
113
115
  ], MaterialClassResolver.prototype, "memberScanCapped", null);
114
116
  tslib_1.__decorate([
115
- (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
116
- (0, type_graphql_1.Directive)('@transaction'),
117
- (0, type_graphql_1.Mutation)(returns => [material_class_1.MaterialClass], { description: 'To create, change or retire material classes' }),
118
- tslib_1.__param(0, (0, type_graphql_1.Arg)('patches', type => [shell_1.ScalarObject])),
119
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
117
+ (0, type_graphql_2.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
118
+ (0, type_graphql_2.Directive)('@transaction'),
119
+ (0, type_graphql_2.Mutation)(returns => [material_class_1.MaterialClass], { description: 'To create, change or retire material classes' }),
120
+ tslib_1.__param(0, (0, type_graphql_2.Arg)('patches', type => [shell_2.ScalarObject])),
121
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
120
122
  tslib_1.__metadata("design:type", Function),
121
123
  tslib_1.__metadata("design:paramtypes", [Array, Object]),
122
124
  tslib_1.__metadata("design:returntype", Promise)
123
125
  ], MaterialClassResolver.prototype, "updateMultipleMaterialClass", null);
124
126
  exports.MaterialClassResolver = MaterialClassResolver = tslib_1.__decorate([
125
- (0, type_graphql_1.Resolver)(material_class_1.MaterialClass)
127
+ (0, type_graphql_2.Resolver)(material_class_1.MaterialClass),
128
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
126
129
  ], MaterialClassResolver);
127
130
  let MaterialDefinitionList = class MaterialDefinitionList {
128
131
  };
129
132
  exports.MaterialDefinitionList = MaterialDefinitionList;
130
133
  tslib_1.__decorate([
131
- (0, type_graphql_1.Field)(type => [material_definition_1.MaterialDefinition]),
134
+ (0, type_graphql_2.Field)(type => [material_definition_1.MaterialDefinition]),
132
135
  tslib_1.__metadata("design:type", Array)
133
136
  ], MaterialDefinitionList.prototype, "items", void 0);
134
137
  tslib_1.__decorate([
135
- (0, type_graphql_1.Field)(type => type_graphql_1.Int),
138
+ (0, type_graphql_2.Field)(type => type_graphql_2.Int),
136
139
  tslib_1.__metadata("design:type", Number)
137
140
  ], MaterialDefinitionList.prototype, "total", void 0);
138
141
  exports.MaterialDefinitionList = MaterialDefinitionList = tslib_1.__decorate([
139
- (0, type_graphql_1.ObjectType)({ description: 'A page of material definitions' })
142
+ (0, type_graphql_2.ObjectType)({ description: 'A page of material definitions' })
140
143
  ], MaterialDefinitionList);
141
144
  /**
142
145
  * 이 도메인의 제조법을 **한 요청에 한 번만** 읽는다.
@@ -158,7 +161,7 @@ exports.MaterialDefinitionList = MaterialDefinitionList = tslib_1.__decorate([
158
161
  */
159
162
  const RECIPE_SCAN_LIMIT = 2000;
160
163
  async function scanRecipes(domain) {
161
- const repo = (0, shell_1.getRepository)(recipe_definition_1.RecipeDefinition);
164
+ const repo = (0, shell_2.getRepository)(recipe_definition_1.RecipeDefinition);
162
165
  const total = await repo.count({ where: { domain: { id: domain.id } } });
163
166
  const rows = await repo.find({
164
167
  where: { domain: { id: domain.id } },
@@ -180,7 +183,7 @@ async function scanRecipes(domain) {
180
183
  * 두면 계보가 중간에서 끊긴 채 짧은 답이 나오고, 그것은 「뿌리가 여기다」와 구별되지 않는다.
181
184
  */
182
185
  async function scanMaterialClasses(domain) {
183
- const rows = await (0, shell_1.getRepository)(material_class_1.MaterialClass).find({
186
+ const rows = await (0, shell_2.getRepository)(material_class_1.MaterialClass).find({
184
187
  where: { domain: { id: domain.id } },
185
188
  select: ['id', 'name', 'baseIds']
186
189
  });
@@ -289,65 +292,66 @@ let MaterialDefinitionResolver = class MaterialDefinitionResolver {
289
292
  };
290
293
  exports.MaterialDefinitionResolver = MaterialDefinitionResolver;
291
294
  tslib_1.__decorate([
292
- (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
293
- (0, type_graphql_1.Query)(returns => MaterialDefinitionList, { description: 'To fetch material definitions' }),
294
- tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
295
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
295
+ (0, type_graphql_2.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
296
+ (0, type_graphql_2.Query)(returns => MaterialDefinitionList, { description: 'To fetch material definitions' }),
297
+ tslib_1.__param(0, (0, type_graphql_2.Args)(type => shell_2.ListParam)),
298
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
296
299
  tslib_1.__metadata("design:type", Function),
297
- tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
300
+ tslib_1.__metadata("design:paramtypes", [shell_2.ListParam, Object]),
298
301
  tslib_1.__metadata("design:returntype", Promise)
299
302
  ], MaterialDefinitionResolver.prototype, "materialDefinitions", null);
300
303
  tslib_1.__decorate([
301
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Recipes that produce this material' }),
302
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
303
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
304
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Recipes that produce this material' }),
305
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
306
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
304
307
  tslib_1.__metadata("design:type", Function),
305
308
  tslib_1.__metadata("design:paramtypes", [material_definition_1.MaterialDefinition, Object]),
306
309
  tslib_1.__metadata("design:returntype", Promise)
307
310
  ], MaterialDefinitionResolver.prototype, "madeBy", null);
308
311
  tslib_1.__decorate([
309
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Recipes that consume this material' }),
310
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
311
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
312
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Recipes that consume this material' }),
313
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
314
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
312
315
  tslib_1.__metadata("design:type", Function),
313
316
  tslib_1.__metadata("design:paramtypes", [material_definition_1.MaterialDefinition, Object]),
314
317
  tslib_1.__metadata("design:returntype", Promise)
315
318
  ], MaterialDefinitionResolver.prototype, "usedIn", null);
316
319
  tslib_1.__decorate([
317
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Root material classes this material belongs to' }),
318
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
319
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
320
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Root material classes this material belongs to' }),
321
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
322
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
320
323
  tslib_1.__metadata("design:type", Function),
321
324
  tslib_1.__metadata("design:paramtypes", [material_definition_1.MaterialDefinition, Object]),
322
325
  tslib_1.__metadata("design:returntype", Promise)
323
326
  ], MaterialDefinitionResolver.prototype, "rootClasses", null);
324
327
  tslib_1.__decorate([
325
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Class lineage from root to own class' }),
326
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
327
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
328
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Class lineage from root to own class' }),
329
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
330
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
328
331
  tslib_1.__metadata("design:type", Function),
329
332
  tslib_1.__metadata("design:paramtypes", [material_definition_1.MaterialDefinition, Object]),
330
333
  tslib_1.__metadata("design:returntype", Promise)
331
334
  ], MaterialDefinitionResolver.prototype, "classPath", null);
332
335
  tslib_1.__decorate([
333
- (0, type_graphql_1.FieldResolver)(type => Boolean, { description: 'True when recipes were too many to scan fully' }),
334
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
335
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
336
+ (0, type_graphql_2.FieldResolver)(type => Boolean, { description: 'True when recipes were too many to scan fully' }),
337
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
338
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
336
339
  tslib_1.__metadata("design:type", Function),
337
340
  tslib_1.__metadata("design:paramtypes", [material_definition_1.MaterialDefinition, Object]),
338
341
  tslib_1.__metadata("design:returntype", Promise)
339
342
  ], MaterialDefinitionResolver.prototype, "recipeScanCapped", null);
340
343
  tslib_1.__decorate([
341
- (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
342
- (0, type_graphql_1.Directive)('@transaction'),
343
- (0, type_graphql_1.Mutation)(returns => [material_definition_1.MaterialDefinition], { description: 'To create, change or retire material definitions' }),
344
- tslib_1.__param(0, (0, type_graphql_1.Arg)('patches', type => [shell_1.ScalarObject])),
345
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
344
+ (0, type_graphql_2.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
345
+ (0, type_graphql_2.Directive)('@transaction'),
346
+ (0, type_graphql_2.Mutation)(returns => [material_definition_1.MaterialDefinition], { description: 'To create, change or retire material definitions' }),
347
+ tslib_1.__param(0, (0, type_graphql_2.Arg)('patches', type => [shell_2.ScalarObject])),
348
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
346
349
  tslib_1.__metadata("design:type", Function),
347
350
  tslib_1.__metadata("design:paramtypes", [Array, Object]),
348
351
  tslib_1.__metadata("design:returntype", Promise)
349
352
  ], MaterialDefinitionResolver.prototype, "updateMultipleMaterialDefinition", null);
350
353
  exports.MaterialDefinitionResolver = MaterialDefinitionResolver = tslib_1.__decorate([
351
- (0, type_graphql_1.Resolver)(material_definition_1.MaterialDefinition)
354
+ (0, type_graphql_2.Resolver)(material_definition_1.MaterialDefinition),
355
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
352
356
  ], MaterialDefinitionResolver);
353
357
  //# sourceMappingURL=material-master-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"material-master-resolver.js","sourceRoot":"","sources":["../../../server/service/material/material-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Args, Arg, Ctx, Directive, FieldResolver, Int, Mutation, ObjectType, Field, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { ClassMembers, scanClassMembers } from '../resource/class-members'\nimport { MaterialClass } from './material-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { indexRecipes } from './recipe-index'\nimport { classPathOf, rootClassesOfMaterial } from '../resource/class-lineage'\nimport { MaterialDefinition } from './material-definition'\n\n/**\n * 자재 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of material classes' })\nexport class MaterialClassList {\n @Field(type => [MaterialClass])\n items: MaterialClass[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * **이 등급에 무엇이 드나** — 들어오는 관계.\n *\n * 등급을 만들어 놓고 아무도 안 들어 있는 일이 실제로 있었다. 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다. 없앨 때 무엇이 딸려 자격을 잃는지도 알 수 없었다.\n *\n * 세는 방식과 상한은 `class-members.ts` 머리말에 적었다 — 등급 넷이 같은 함수를 쓴다.\n */\nfunction materialClassMembersOf(context: ResolverContext): Promise<ClassMembers> {\n const state = context.state as { domain: Domain; materialClassMembers?: Promise<ClassMembers> }\n\n state.materialClassMembers = state.materialClassMembers || scanClassMembers(MaterialDefinition, 'materialClassIds', state.domain)\n\n return state.materialClassMembers\n}\n\n@Resolver(MaterialClass)\nexport class MaterialClassResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => MaterialClassList, { description: 'To fetch material classes' })\n async materialClasses(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<MaterialClassList> {\n const { domain } = context.state\n const { items, total } = await listMaster('materialClass', params, domain)\n\n return { items: items as MaterialClass[], total }\n }\n\n\n /**\n * 이 등급에 든 품목들\n *\n * 아무도 안 들면 빈 목록이다. 다만 그것이 「없다」인지 「다 못 봤다」인지는 `memberScanCapped`\n * 가 말한다 — 조용히 자르면 고쳐야 할 선언과 멀쩡한 등급이 같아 보인다.\n */\n @FieldResolver(type => [String], { description: 'Materials belonging to this class' })\n async members(@Root() klass: MaterialClass, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await materialClassMembersOf(context)).members.get(klass.name) ?? []\n }\n\n /** 소속을 다 보지 못했나. */\n @FieldResolver(type => Boolean, { description: 'True when members were too many to scan fully' })\n async memberScanCapped(@Root() klass: MaterialClass, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await materialClassMembersOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [MaterialClass], { description: 'To create, change or retire material classes' })\n async updateMultipleMaterialClass(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<MaterialClass[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('materialClass')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as MaterialClass[]\n }\n}\n\n@ObjectType({ description: 'A page of material definitions' })\nexport class MaterialDefinitionList {\n @Field(type => [MaterialDefinition])\n items: MaterialDefinition[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * 이 도메인의 제조법을 **한 요청에 한 번만** 읽는다.\n *\n * ── 왜 통째로 읽나 ─────────────────────────────────────────────────────────\n * 「이 품목이 어디에 쓰이나」는 제조법의 `inputs` 안을 봐야 안다. 그런데 그것은 JSON 문자열 칸이라\n * **다섯 드라이버에서 같은 방식으로 물을 수 없다.** 드라이버마다 다른 JSON 함수를 쓰면 그것이 곧\n * raw SQL 이고, 한 드라이버에서만 도는 코드가 된다.\n *\n * 그래서 제조법을 읽어 와 메모리에서 센다. 제조법은 품목보다 훨씬 적다 — 품목이 백만이어도 제조법은\n * 수백에서 수천이다.\n *\n * ── 그래도 무한하지는 않다 ─────────────────────────────────────────────────\n * 상한을 둔다. 넘으면 **본 만큼만 답하고 그 사실을 함께 낸다**(`usedInCapped`) — 조용히 잘라 내면\n * 「쓰이는 데가 없다」와 「못 봤다」가 같아 보인다.\n *\n * 제대로 된 답은 투입을 줄로 갖는 표(`RecipePart`)를 두고 품목으로 색인하는 것이다. 그때 이 함수는\n * 사라진다. 지금은 그 표가 없다.\n */\nconst RECIPE_SCAN_LIMIT = 2000\n\ninterface RecipeScan {\n /** 만드는 품목 → 제조법 이름들. */\n makes: Map<string, string[]>\n /** 투입 품목 → 제조법 이름들. */\n uses: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\nasync function scanRecipes(domain: Domain): Promise<RecipeScan> {\n const repo = getRepository(RecipeDefinition)\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.RecipeDefinition_id」로\n * 터진다 — `take` 를 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면\n * 감쌀 것이 없다. [확인함] 2026-09-04\n */\n select: ['id', 'name', 'materialDefinitionName', 'inputs'] as any,\n take: RECIPE_SCAN_LIMIT\n })\n\n const { makes, uses } = indexRecipes(rows)\n\n return { makes, uses, capped: total > rows.length }\n}\n\n/**\n * 등급 색인 — **이름 → 등급.** 상속을 걸으려면 등급 표 전체가 손에 있어야 한다.\n *\n * 등급은 품목과 달리 수가 적다(한 도메인에 수십 개). 그래서 상한을 두지 않고 다 읽는다 — 상한을\n * 두면 계보가 중간에서 끊긴 채 짧은 답이 나오고, 그것은 「뿌리가 여기다」와 구별되지 않는다.\n */\nasync function scanMaterialClasses(domain: Domain): Promise<Map<string, MaterialClass>> {\n const rows = await getRepository(MaterialClass).find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', 'baseIds'] as any\n })\n\n return new Map(rows.map(one => [one.name, one]))\n}\n\n/** 요청 안에서 한 번만 읽는다 — 목록 한 화면이 이 칸을 스무 줄에서 묻는다. */\nfunction classIndexOf(context: ResolverContext): Promise<Map<string, MaterialClass>> {\n const state = context.state as { domain: Domain; materialClassIndex?: Promise<Map<string, MaterialClass>> }\n state.materialClassIndex = state.materialClassIndex || scanMaterialClasses(state.domain)\n\n return state.materialClassIndex\n}\n\n/** 요청 안에서 한 번만 훑는다. **약속을 담는다** — 줄들이 거의 동시에 묻는다. */\nfunction scanOf(context: ResolverContext): Promise<RecipeScan> {\n const state = context.state as { domain: Domain; recipeScan?: Promise<RecipeScan> }\n state.recipeScan = state.recipeScan || scanRecipes(state.domain)\n\n return state.recipeScan\n}\n\n@Resolver(MaterialDefinition)\nexport class MaterialDefinitionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => MaterialDefinitionList, { description: 'To fetch material definitions' })\n async materialDefinitions(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<MaterialDefinitionList> {\n const { domain } = context.state\n const { items, total } = await listMaster('materialDefinition', params, domain)\n\n return { items: items as MaterialDefinition[], total }\n }\n\n /**\n * 이 품목을 **만드는** 제조법들.\n *\n * ── 왜 품목 화면에 이 칸이 있나 ──────────────────────────────────────────\n * BOM 은 제조법에 있는데 품목 화면에서 거기로 가는 문이 없었다. 완제품 한 줄을 보면서 「이 한 대에\n * 무엇이 들어가나」를 물을 수 없었다 — 제조법 화면을 따로 열고 이름으로 찾아야 했다.\n *\n * 여럿일 수 있다. 같은 품목을 다른 라우팅으로 만드는 일이 있다(라인 증설 · 시제품). 하나로 줄이면\n * 그중 하나가 조용히 사라진다.\n *\n * **묻는 요청만 훑는다** — GraphQL 이 이 칸을 물어본 조회에서만 이 리졸버가 불린다.\n */\n @FieldResolver(type => [String], { description: 'Recipes that produce this material' })\n async madeBy(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await scanOf(context)).makes.get(material.name) ?? []\n }\n\n /**\n * 이 품목이 **들어가는** 제조법들 — 역전개.\n *\n * 「이 나사가 어디에 쓰이나」의 답이다. 품목 하나를 바꾸거나 끊을 때 무엇이 영향을 받는지가 이\n * 칸에 있다.\n */\n @FieldResolver(type => [String], { description: 'Recipes that consume this material' })\n async usedIn(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await scanOf(context)).uses.get(material.name) ?? []\n }\n\n /**\n * 제조법을 다 보지 못했나.\n *\n * **조용히 자르지 않는다.** 상한에 걸리면 위의 두 칸이 덜 찬 답이고, 그것을 말하지 않으면\n * 「쓰이는 데가 없다」와 「못 봤다」가 같아 보인다.\n */\n /**\n * 이 품목이 어느 **뿌리 등급**에 드는가 — 「완제품인가 · 반제품인가 · 원재료인가」의 답.\n *\n * ── 왜 새 칸을 안 만들었나 ──────────────────────────────────────────────\n * ISA-95 `MaterialDefinitionType` 에는 그 구분을 담는 전용 요소가 없다. 표준이 그것을 두는 자리는\n * **등급**이고, 등급 표의 주석도 「원자재·완제품처럼 품목을 묶는 상위 개념」이라고 적고 있다.\n * 그래서 `materialType` 같은 칸을 새로 만들지 않고 이미 있는 `baseIds` 상속을 걷는다.\n *\n * 그 칸은 네 등급 표에 다 있었고 화면에서 편집도 됐고 트윈에도 나갔는데 **읽는 곳이 없었다.**\n *\n * 여럿일 수 있다. 품목이 등급 여럿에 들 수 있고(표준 `MaterialClassID` 가 복수다), 그 등급들의\n * 뿌리가 다를 수 있다. 하나로 줄이면 「완제품이면서 원재료」인 모순이 화면에서 사라진다.\n *\n * 등급이 하나도 없으면 빈 배열이다 — 「원재료」로 넘겨짚지 않는다.\n */\n @FieldResolver(type => [String], { description: 'Root material classes this material belongs to' })\n async rootClasses(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return rootClassesOfMaterial(material.materialClassIds, await classIndexOf(context))\n }\n\n /**\n * 등급 계보를 뿌리부터 자기까지 — `['원재료', 'ECU 기구부품']`.\n *\n * 목록에서 이 한 칸만 보면 「무엇인가」가 읽힌다. 등급 이름만 있으면 `MC-ECU-MECH` 가 원재료인지\n * 반제품인지 사람이 외우고 있어야 한다.\n *\n * **첫 갈래만 낸다** — 보여 주기 위한 칸이라 한 줄에 담아야 한다. 판정은 위의 `rootClasses` 로\n * 한다. 품목이 등급 여럿에 들면 첫 등급의 계보다.\n */\n @FieldResolver(type => [String], { description: 'Class lineage from root to own class' })\n async classPath(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n const first = (material.materialClassIds ?? [])[0]\n\n if (!first) {\n return []\n }\n\n return classPathOf(first, await classIndexOf(context))\n }\n\n @FieldResolver(type => Boolean, { description: 'True when recipes were too many to scan fully' })\n async recipeScanCapped(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await scanOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [MaterialDefinition], { description: 'To create, change or retire material definitions' })\n async updateMultipleMaterialDefinition(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<MaterialDefinition[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('materialDefinition')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as MaterialDefinition[]\n }\n}\n"]}
1
+ {"version":3,"file":"material-master-resolver.js","sourceRoot":"","sources":["../../../server/service/material/material-master-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAA4C;AAC5C,iDAAqD;AACrD,+CAAgI;AAEhI,iDAAsF;AAEtF,gDAA+D;AAC/D,gDAA6C;AAC7C,gDAA2C;AAC3C,6DAA0E;AAC1E,qDAAgD;AAChD,mEAA8D;AAC9D,iDAA6C;AAC7C,6DAA8E;AAC9E,+DAA0D;AAE1D;;;;;;;;;GASG;AAGI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAM7B,CAAA;AANY,8CAAiB;AAE5B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,8BAAa,CAAC,CAAC;;gDACT;AAGtB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,CAAC;;gDACN;4BALF,iBAAiB;IAD7B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;GAC7C,iBAAiB,CAM7B;AAED;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,OAAwB;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAyE,CAAA;IAE/F,KAAK,CAAC,oBAAoB,GAAG,KAAK,CAAC,oBAAoB,IAAI,IAAA,gCAAgB,EAAC,wCAAkB,EAAE,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAEjI,OAAO,KAAK,CAAC,oBAAoB,CAAA;AACnC,CAAC;AAIM,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAG1B,AAAN,KAAK,CAAC,eAAe,CAA0B,MAAiB,EAAS,OAAwB;QAC/F,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAA,wBAAU,EAAC,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAE1E,OAAO,EAAE,KAAK,EAAE,KAAwB,EAAE,KAAK,EAAE,CAAA;IACnD,CAAC;IAGD;;;;;OAKG;IAEG,AAAN,KAAK,CAAC,OAAO,CAAS,KAAoB,EAAS,OAAwB;QACzE,OAAO,CAAC,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IAC9E,CAAC;IAED,oBAAoB;IAEd,AAAN,KAAK,CAAC,gBAAgB,CAAS,KAAoB,EAAS,OAAwB;QAClF,OAAO,CAAC,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IACvD,CAAC;IAED;;;;;;OAMG;IAIG,AAAN,KAAK,CAAC,2BAA2B,CACS,OAAsB,EACvD,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,0BAAY,EAAC,eAAe,CAAC,CAAA;QAE1C,MAAM,KAAK,GAAG,MAAM,IAAA,+BAAiB,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;YAC5E,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;SAC5C,CAAC,CAAA;QAEF,OAAO,KAAwB,CAAA;IACjC,CAAC;CACF,CAAA;AAnDY,sDAAqB;AAG1B;IAFL,IAAA,wBAAS,EAAC,kFAAkF,CAAC;IAC7F,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,iBAAiB,EAAE,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;IAC3D,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;4DAK/D;AAUK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;IACvE,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAwB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAArB,8BAAa;;oDAEzC;AAIK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;IACzE,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAwB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAArB,8BAAa;;6DAElD;AAYK;IAHL,IAAA,wBAAS,EAAC,qFAAqF,CAAC;IAChG,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,8BAAa,CAAC,EAAE,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;IAEnG,mBAAA,IAAA,kBAAG,EAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAY,CAAC,CAAC,CAAA;IACtC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;wEAUP;gCAlDU,qBAAqB;IAFjC,IAAA,uBAAQ,EAAC,8BAAa,CAAC;IACvB,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,qBAAqB,CAmDjC;AAGM,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;CAMlC,CAAA;AANY,wDAAsB;AAEjC;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,wCAAkB,CAAC,CAAC;;qDACT;AAG3B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,CAAC;;qDACN;iCALF,sBAAsB;IADlC,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;GACjD,sBAAsB,CAMlC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,iBAAiB,GAAG,IAAI,CAAA;AAW9B,KAAK,UAAU,WAAW,CAAC,MAAc;IACvC,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,oCAAgB,CAAC,CAAA;IAC5C,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;IAC/E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C;;;;WAIG;QACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,QAAQ,CAAQ;QACjE,IAAI,EAAE,iBAAiB;KACxB,CAAC,CAAA;IAEF,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAA,2BAAY,EAAC,IAAI,CAAC,CAAA;IAE1C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;AACrD,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,mBAAmB,CAAC,MAAc;IAC/C,MAAM,IAAI,GAAG,MAAM,IAAA,qBAAa,EAAC,8BAAa,CAAC,CAAC,IAAI,CAAC;QACnD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAQ;KACzC,CAAC,CAAA;IAEF,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;AAClD,CAAC;AAED,kDAAkD;AAClD,SAAS,YAAY,CAAC,OAAwB;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAqF,CAAA;IAC3G,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,IAAI,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAExF,OAAO,KAAK,CAAC,kBAAkB,CAAA;AACjC,CAAC;AAED,qDAAqD;AACrD,SAAS,MAAM,CAAC,OAAwB;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAA6D,CAAA;IACnF,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAEhE,OAAO,KAAK,CAAC,UAAU,CAAA;AACzB,CAAC;AAIM,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IAG/B,AAAN,KAAK,CAAC,mBAAmB,CAA0B,MAAiB,EAAS,OAAwB;QACnG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAA,wBAAU,EAAC,oBAAoB,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAE/E,OAAO,EAAE,KAAK,EAAE,KAA6B,EAAE,KAAK,EAAE,CAAA;IACxD,CAAC;IAED;;;;;;;;;;;OAWG;IAEG,AAAN,KAAK,CAAC,MAAM,CAAS,QAA4B,EAAS,OAAwB;QAChF,OAAO,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IAC/D,CAAC;IAED;;;;;OAKG;IAEG,AAAN,KAAK,CAAC,MAAM,CAAS,QAA4B,EAAS,OAAwB;QAChF,OAAO,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IAC9D,CAAC;IAED;;;;;OAKG;IACH;;;;;;;;;;;;;;OAcG;IAEG,AAAN,KAAK,CAAC,WAAW,CAAS,QAA4B,EAAS,OAAwB;QACrF,OAAO,IAAA,qCAAqB,EAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;IACtF,CAAC;IAED;;;;;;;;OAQG;IAEG,AAAN,KAAK,CAAC,SAAS,CAAS,QAA4B,EAAS,OAAwB;QACnF,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QAElD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,EAAE,CAAA;QACX,CAAC;QAED,OAAO,IAAA,2BAAW,EAAC,KAAK,EAAE,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;IACxD,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAS,QAA4B,EAAS,OAAwB;QAC1F,OAAO,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IACvC,CAAC;IAED;;;;;;OAMG;IAIG,AAAN,KAAK,CAAC,gCAAgC,CACI,OAAsB,EACvD,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAA,0BAAY,EAAC,oBAAoB,CAAC,CAAA;QAE/C,MAAM,KAAK,GAAG,MAAM,IAAA,+BAAiB,EAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;YAC5E,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;SAC5C,CAAC,CAAA;QAEF,OAAO,KAA6B,CAAA;IACtC,CAAC;CACF,CAAA;AAhHY,gEAA0B;AAG/B;IAFL,IAAA,wBAAS,EAAC,kFAAkF,CAAC;IAC7F,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,sBAAsB,EAAE,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;IAChE,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;qEAKnE;AAeK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;IACzE,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAgC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAA1B,wCAAkB;;wDAEhD;AASK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;IACzE,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAgC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAA1B,wCAAkB;;wDAEhD;AAwBK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC;IAChF,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAgC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAA1B,wCAAkB;;6DAErD;AAYK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;IACxE,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAgC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAA1B,wCAAkB;;2DAQnD;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;IACzE,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAgC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAA1B,wCAAkB;;kEAE1D;AAYK;IAHL,IAAA,wBAAS,EAAC,qFAAqF,CAAC;IAChG,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,wCAAkB,CAAC,EAAE,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;IAE5G,mBAAA,IAAA,kBAAG,EAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAY,CAAC,CAAC,CAAA;IACtC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kFAUP;qCA/GU,0BAA0B;IAFtC,IAAA,uBAAQ,EAAC,wCAAkB,CAAC;IAC5B,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,0BAA0B,CAgHtC","sourcesContent":["import { UseMiddleware } from 'type-graphql'\nimport { SpeakRefusals } from '@things-factory/shell'\nimport { Args, Arg, Ctx, Directive, FieldResolver, Int, Mutation, ObjectType, Field, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { ClassMembers, scanClassMembers } from '../resource/class-members'\nimport { MaterialClass } from './material-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { indexRecipes } from './recipe-index'\nimport { classPathOf, rootClassesOfMaterial } from '../resource/class-lineage'\nimport { MaterialDefinition } from './material-definition'\n\n/**\n * 자재 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of material classes' })\nexport class MaterialClassList {\n @Field(type => [MaterialClass])\n items: MaterialClass[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * **이 등급에 무엇이 드나** — 들어오는 관계.\n *\n * 등급을 만들어 놓고 아무도 안 들어 있는 일이 실제로 있었다. 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다. 없앨 때 무엇이 딸려 자격을 잃는지도 알 수 없었다.\n *\n * 세는 방식과 상한은 `class-members.ts` 머리말에 적었다 — 등급 넷이 같은 함수를 쓴다.\n */\nfunction materialClassMembersOf(context: ResolverContext): Promise<ClassMembers> {\n const state = context.state as { domain: Domain; materialClassMembers?: Promise<ClassMembers> }\n\n state.materialClassMembers = state.materialClassMembers || scanClassMembers(MaterialDefinition, 'materialClassIds', state.domain)\n\n return state.materialClassMembers\n}\n\n@Resolver(MaterialClass)\n@UseMiddleware(SpeakRefusals)\nexport class MaterialClassResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => MaterialClassList, { description: 'To fetch material classes' })\n async materialClasses(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<MaterialClassList> {\n const { domain } = context.state\n const { items, total } = await listMaster('materialClass', params, domain)\n\n return { items: items as MaterialClass[], total }\n }\n\n\n /**\n * 이 등급에 든 품목들\n *\n * 아무도 안 들면 빈 목록이다. 다만 그것이 「없다」인지 「다 못 봤다」인지는 `memberScanCapped`\n * 가 말한다 — 조용히 자르면 고쳐야 할 선언과 멀쩡한 등급이 같아 보인다.\n */\n @FieldResolver(type => [String], { description: 'Materials belonging to this class' })\n async members(@Root() klass: MaterialClass, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await materialClassMembersOf(context)).members.get(klass.name) ?? []\n }\n\n /** 소속을 다 보지 못했나. */\n @FieldResolver(type => Boolean, { description: 'True when members were too many to scan fully' })\n async memberScanCapped(@Root() klass: MaterialClass, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await materialClassMembersOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [MaterialClass], { description: 'To create, change or retire material classes' })\n async updateMultipleMaterialClass(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<MaterialClass[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('materialClass')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as MaterialClass[]\n }\n}\n\n@ObjectType({ description: 'A page of material definitions' })\nexport class MaterialDefinitionList {\n @Field(type => [MaterialDefinition])\n items: MaterialDefinition[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * 이 도메인의 제조법을 **한 요청에 한 번만** 읽는다.\n *\n * ── 왜 통째로 읽나 ─────────────────────────────────────────────────────────\n * 「이 품목이 어디에 쓰이나」는 제조법의 `inputs` 안을 봐야 안다. 그런데 그것은 JSON 문자열 칸이라\n * **다섯 드라이버에서 같은 방식으로 물을 수 없다.** 드라이버마다 다른 JSON 함수를 쓰면 그것이 곧\n * raw SQL 이고, 한 드라이버에서만 도는 코드가 된다.\n *\n * 그래서 제조법을 읽어 와 메모리에서 센다. 제조법은 품목보다 훨씬 적다 — 품목이 백만이어도 제조법은\n * 수백에서 수천이다.\n *\n * ── 그래도 무한하지는 않다 ─────────────────────────────────────────────────\n * 상한을 둔다. 넘으면 **본 만큼만 답하고 그 사실을 함께 낸다**(`usedInCapped`) — 조용히 잘라 내면\n * 「쓰이는 데가 없다」와 「못 봤다」가 같아 보인다.\n *\n * 제대로 된 답은 투입을 줄로 갖는 표(`RecipePart`)를 두고 품목으로 색인하는 것이다. 그때 이 함수는\n * 사라진다. 지금은 그 표가 없다.\n */\nconst RECIPE_SCAN_LIMIT = 2000\n\ninterface RecipeScan {\n /** 만드는 품목 → 제조법 이름들. */\n makes: Map<string, string[]>\n /** 투입 품목 → 제조법 이름들. */\n uses: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\nasync function scanRecipes(domain: Domain): Promise<RecipeScan> {\n const repo = getRepository(RecipeDefinition)\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.RecipeDefinition_id」로\n * 터진다 — `take` 를 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면\n * 감쌀 것이 없다. [확인함] 2026-09-04\n */\n select: ['id', 'name', 'materialDefinitionName', 'inputs'] as any,\n take: RECIPE_SCAN_LIMIT\n })\n\n const { makes, uses } = indexRecipes(rows)\n\n return { makes, uses, capped: total > rows.length }\n}\n\n/**\n * 등급 색인 — **이름 → 등급.** 상속을 걸으려면 등급 표 전체가 손에 있어야 한다.\n *\n * 등급은 품목과 달리 수가 적다(한 도메인에 수십 개). 그래서 상한을 두지 않고 다 읽는다 — 상한을\n * 두면 계보가 중간에서 끊긴 채 짧은 답이 나오고, 그것은 「뿌리가 여기다」와 구별되지 않는다.\n */\nasync function scanMaterialClasses(domain: Domain): Promise<Map<string, MaterialClass>> {\n const rows = await getRepository(MaterialClass).find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', 'baseIds'] as any\n })\n\n return new Map(rows.map(one => [one.name, one]))\n}\n\n/** 요청 안에서 한 번만 읽는다 — 목록 한 화면이 이 칸을 스무 줄에서 묻는다. */\nfunction classIndexOf(context: ResolverContext): Promise<Map<string, MaterialClass>> {\n const state = context.state as { domain: Domain; materialClassIndex?: Promise<Map<string, MaterialClass>> }\n state.materialClassIndex = state.materialClassIndex || scanMaterialClasses(state.domain)\n\n return state.materialClassIndex\n}\n\n/** 요청 안에서 한 번만 훑는다. **약속을 담는다** — 줄들이 거의 동시에 묻는다. */\nfunction scanOf(context: ResolverContext): Promise<RecipeScan> {\n const state = context.state as { domain: Domain; recipeScan?: Promise<RecipeScan> }\n state.recipeScan = state.recipeScan || scanRecipes(state.domain)\n\n return state.recipeScan\n}\n\n@Resolver(MaterialDefinition)\n@UseMiddleware(SpeakRefusals)\nexport class MaterialDefinitionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => MaterialDefinitionList, { description: 'To fetch material definitions' })\n async materialDefinitions(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<MaterialDefinitionList> {\n const { domain } = context.state\n const { items, total } = await listMaster('materialDefinition', params, domain)\n\n return { items: items as MaterialDefinition[], total }\n }\n\n /**\n * 이 품목을 **만드는** 제조법들.\n *\n * ── 왜 품목 화면에 이 칸이 있나 ──────────────────────────────────────────\n * BOM 은 제조법에 있는데 품목 화면에서 거기로 가는 문이 없었다. 완제품 한 줄을 보면서 「이 한 대에\n * 무엇이 들어가나」를 물을 수 없었다 — 제조법 화면을 따로 열고 이름으로 찾아야 했다.\n *\n * 여럿일 수 있다. 같은 품목을 다른 라우팅으로 만드는 일이 있다(라인 증설 · 시제품). 하나로 줄이면\n * 그중 하나가 조용히 사라진다.\n *\n * **묻는 요청만 훑는다** — GraphQL 이 이 칸을 물어본 조회에서만 이 리졸버가 불린다.\n */\n @FieldResolver(type => [String], { description: 'Recipes that produce this material' })\n async madeBy(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await scanOf(context)).makes.get(material.name) ?? []\n }\n\n /**\n * 이 품목이 **들어가는** 제조법들 — 역전개.\n *\n * 「이 나사가 어디에 쓰이나」의 답이다. 품목 하나를 바꾸거나 끊을 때 무엇이 영향을 받는지가 이\n * 칸에 있다.\n */\n @FieldResolver(type => [String], { description: 'Recipes that consume this material' })\n async usedIn(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await scanOf(context)).uses.get(material.name) ?? []\n }\n\n /**\n * 제조법을 다 보지 못했나.\n *\n * **조용히 자르지 않는다.** 상한에 걸리면 위의 두 칸이 덜 찬 답이고, 그것을 말하지 않으면\n * 「쓰이는 데가 없다」와 「못 봤다」가 같아 보인다.\n */\n /**\n * 이 품목이 어느 **뿌리 등급**에 드는가 — 「완제품인가 · 반제품인가 · 원재료인가」의 답.\n *\n * ── 왜 새 칸을 안 만들었나 ──────────────────────────────────────────────\n * ISA-95 `MaterialDefinitionType` 에는 그 구분을 담는 전용 요소가 없다. 표준이 그것을 두는 자리는\n * **등급**이고, 등급 표의 주석도 「원자재·완제품처럼 품목을 묶는 상위 개념」이라고 적고 있다.\n * 그래서 `materialType` 같은 칸을 새로 만들지 않고 이미 있는 `baseIds` 상속을 걷는다.\n *\n * 그 칸은 네 등급 표에 다 있었고 화면에서 편집도 됐고 트윈에도 나갔는데 **읽는 곳이 없었다.**\n *\n * 여럿일 수 있다. 품목이 등급 여럿에 들 수 있고(표준 `MaterialClassID` 가 복수다), 그 등급들의\n * 뿌리가 다를 수 있다. 하나로 줄이면 「완제품이면서 원재료」인 모순이 화면에서 사라진다.\n *\n * 등급이 하나도 없으면 빈 배열이다 — 「원재료」로 넘겨짚지 않는다.\n */\n @FieldResolver(type => [String], { description: 'Root material classes this material belongs to' })\n async rootClasses(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return rootClassesOfMaterial(material.materialClassIds, await classIndexOf(context))\n }\n\n /**\n * 등급 계보를 뿌리부터 자기까지 — `['원재료', 'ECU 기구부품']`.\n *\n * 목록에서 이 한 칸만 보면 「무엇인가」가 읽힌다. 등급 이름만 있으면 `MC-ECU-MECH` 가 원재료인지\n * 반제품인지 사람이 외우고 있어야 한다.\n *\n * **첫 갈래만 낸다** — 보여 주기 위한 칸이라 한 줄에 담아야 한다. 판정은 위의 `rootClasses` 로\n * 한다. 품목이 등급 여럿에 들면 첫 등급의 계보다.\n */\n @FieldResolver(type => [String], { description: 'Class lineage from root to own class' })\n async classPath(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n const first = (material.materialClassIds ?? [])[0]\n\n if (!first) {\n return []\n }\n\n return classPathOf(first, await classIndexOf(context))\n }\n\n @FieldResolver(type => Boolean, { description: 'True when recipes were too many to scan fully' })\n async recipeScanCapped(@Root() material: MaterialDefinition, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await scanOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [MaterialDefinition], { description: 'To create, change or retire material definitions' })\n async updateMultipleMaterialDefinition(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<MaterialDefinition[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('materialDefinition')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as MaterialDefinition[]\n }\n}\n"]}
@@ -4,6 +4,8 @@ exports.OperationDefinitionResolver = exports.OperationDefinitionList = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const shell_1 = require("@things-factory/shell");
7
+ const type_graphql_2 = require("type-graphql");
8
+ const shell_2 = require("@things-factory/shell");
7
9
  const master_crud_1 = require("../master-crud");
8
10
  const master_axis_1 = require("../master-axis");
9
11
  const master_list_1 = require("../master-list");
@@ -25,15 +27,15 @@ let OperationDefinitionList = class OperationDefinitionList {
25
27
  };
26
28
  exports.OperationDefinitionList = OperationDefinitionList;
27
29
  tslib_1.__decorate([
28
- (0, type_graphql_1.Field)(type => [operation_definition_1.OperationDefinition]),
30
+ (0, type_graphql_2.Field)(type => [operation_definition_1.OperationDefinition]),
29
31
  tslib_1.__metadata("design:type", Array)
30
32
  ], OperationDefinitionList.prototype, "items", void 0);
31
33
  tslib_1.__decorate([
32
- (0, type_graphql_1.Field)(type => type_graphql_1.Int),
34
+ (0, type_graphql_2.Field)(type => type_graphql_2.Int),
33
35
  tslib_1.__metadata("design:type", Number)
34
36
  ], OperationDefinitionList.prototype, "total", void 0);
35
37
  exports.OperationDefinitionList = OperationDefinitionList = tslib_1.__decorate([
36
- (0, type_graphql_1.ObjectType)({ description: 'A page of operation definitions' })
38
+ (0, type_graphql_2.ObjectType)({ description: 'A page of operation definitions' })
37
39
  ], OperationDefinitionList);
38
40
  /**
39
41
  * **이 공정을 누가 쓰나** — 들어오는 관계.
@@ -53,8 +55,8 @@ exports.OperationDefinitionList = OperationDefinitionList = tslib_1.__decorate([
53
55
  */
54
56
  const OPERATION_SCAN_LIMIT = 2000;
55
57
  async function scanOperationUse(domain) {
56
- const routeRepo = (0, shell_1.getRepository)(route_definition_1.RouteDefinition);
57
- const recipeRepo = (0, shell_1.getRepository)(recipe_definition_1.RecipeDefinition);
58
+ const routeRepo = (0, shell_2.getRepository)(route_definition_1.RouteDefinition);
59
+ const recipeRepo = (0, shell_2.getRepository)(recipe_definition_1.RecipeDefinition);
58
60
  const [routeTotal, recipeTotal] = await Promise.all([
59
61
  routeRepo.count({ where: { domain: { id: domain.id } } }),
60
62
  recipeRepo.count({ where: { domain: { id: domain.id } } })
@@ -139,49 +141,50 @@ let OperationDefinitionResolver = class OperationDefinitionResolver {
139
141
  };
140
142
  exports.OperationDefinitionResolver = OperationDefinitionResolver;
141
143
  tslib_1.__decorate([
142
- (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
143
- (0, type_graphql_1.Query)(returns => OperationDefinitionList, { description: 'To fetch operation definitions' }),
144
- tslib_1.__param(0, (0, type_graphql_1.Args)(type => shell_1.ListParam)),
145
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
144
+ (0, type_graphql_2.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
145
+ (0, type_graphql_2.Query)(returns => OperationDefinitionList, { description: 'To fetch operation definitions' }),
146
+ tslib_1.__param(0, (0, type_graphql_2.Args)(type => shell_2.ListParam)),
147
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
146
148
  tslib_1.__metadata("design:type", Function),
147
- tslib_1.__metadata("design:paramtypes", [shell_1.ListParam, Object]),
149
+ tslib_1.__metadata("design:paramtypes", [shell_2.ListParam, Object]),
148
150
  tslib_1.__metadata("design:returntype", Promise)
149
151
  ], OperationDefinitionResolver.prototype, "operationDefinitions", null);
150
152
  tslib_1.__decorate([
151
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Routes that include this operation as a step' }),
152
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
153
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
153
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Routes that include this operation as a step' }),
154
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
155
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
154
156
  tslib_1.__metadata("design:type", Function),
155
157
  tslib_1.__metadata("design:paramtypes", [operation_definition_1.OperationDefinition, Object]),
156
158
  tslib_1.__metadata("design:returntype", Promise)
157
159
  ], OperationDefinitionResolver.prototype, "usedInRoutes", null);
158
160
  tslib_1.__decorate([
159
- (0, type_graphql_1.FieldResolver)(type => [String], { description: 'Recipes that consume materials at this operation' }),
160
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
161
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
161
+ (0, type_graphql_2.FieldResolver)(type => [String], { description: 'Recipes that consume materials at this operation' }),
162
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
163
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
162
164
  tslib_1.__metadata("design:type", Function),
163
165
  tslib_1.__metadata("design:paramtypes", [operation_definition_1.OperationDefinition, Object]),
164
166
  tslib_1.__metadata("design:returntype", Promise)
165
167
  ], OperationDefinitionResolver.prototype, "consumedAt", null);
166
168
  tslib_1.__decorate([
167
- (0, type_graphql_1.FieldResolver)(type => Boolean, { description: 'True when routes or recipes were too many to scan fully' }),
168
- tslib_1.__param(0, (0, type_graphql_1.Root)()),
169
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
169
+ (0, type_graphql_2.FieldResolver)(type => Boolean, { description: 'True when routes or recipes were too many to scan fully' }),
170
+ tslib_1.__param(0, (0, type_graphql_2.Root)()),
171
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
170
172
  tslib_1.__metadata("design:type", Function),
171
173
  tslib_1.__metadata("design:paramtypes", [operation_definition_1.OperationDefinition, Object]),
172
174
  tslib_1.__metadata("design:returntype", Promise)
173
175
  ], OperationDefinitionResolver.prototype, "referrerScanCapped", null);
174
176
  tslib_1.__decorate([
175
- (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
176
- (0, type_graphql_1.Directive)('@transaction'),
177
- (0, type_graphql_1.Mutation)(returns => [operation_definition_1.OperationDefinition], { description: 'To create, change or retire operation definitions' }),
178
- tslib_1.__param(0, (0, type_graphql_1.Arg)('patches', type => [shell_1.ScalarObject])),
179
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
177
+ (0, type_graphql_2.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
178
+ (0, type_graphql_2.Directive)('@transaction'),
179
+ (0, type_graphql_2.Mutation)(returns => [operation_definition_1.OperationDefinition], { description: 'To create, change or retire operation definitions' }),
180
+ tslib_1.__param(0, (0, type_graphql_2.Arg)('patches', type => [shell_2.ScalarObject])),
181
+ tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
180
182
  tslib_1.__metadata("design:type", Function),
181
183
  tslib_1.__metadata("design:paramtypes", [Array, Object]),
182
184
  tslib_1.__metadata("design:returntype", Promise)
183
185
  ], OperationDefinitionResolver.prototype, "updateMultipleOperationDefinition", null);
184
186
  exports.OperationDefinitionResolver = OperationDefinitionResolver = tslib_1.__decorate([
185
- (0, type_graphql_1.Resolver)(operation_definition_1.OperationDefinition)
187
+ (0, type_graphql_2.Resolver)(operation_definition_1.OperationDefinition),
188
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
186
189
  ], OperationDefinitionResolver);
187
190
  //# sourceMappingURL=operation-master-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"operation-master-resolver.js","sourceRoot":"","sources":["../../../server/service/operation/operation-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Args, Arg, Ctx, Directive, FieldResolver, Int, Mutation, ObjectType, Field, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { OperationDefinition } from './operation-definition'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { indexMultiReferrers } from '../resource/referrers'\n\n/**\n * 공정 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of operation definitions' })\nexport class OperationDefinitionList {\n @Field(type => [OperationDefinition])\n items: OperationDefinition[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * **이 공정을 누가 쓰나** — 들어오는 관계.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 공정 하나를 고치거나 없앨 때 무엇이 딸려 무너지는지 알 수 없었다. 라우팅과 제조법을 하나씩 열어\n * 단계 목록을 눈으로 훑는 수밖에 없었다. 지금 miratek 은 라우팅 다섯에 제조법 다섯이라 그것이\n * 되지만, 제품이 늘면 안 된다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `RouteDefinition.steps` 와 `RecipeDefinition.inputs[].operation` 은 `simple-json` 이다.\n * 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다. 표를 훑어 뒤집는 수밖에 없다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다**(`referrerScanCapped`). 조용히 자르면 「이 공정을\n * 아무도 안 쓴다」와 「너무 많아 다 못 봤다」가 같아 보인다. 앞엣것은 지워도 되는 공정이고\n * 뒤엣것은 지우면 라인이 선다. 품목의 제조법 훑기가 같은 자리에 같은 것을 둔다.\n */\nconst OPERATION_SCAN_LIMIT = 2000\n\ninterface OperationUse {\n /** 이 공정을 단계로 갖는 라우팅들. */\n routes: Map<string, string[]>\n /** 이 공정에서 자재를 넣는 제조법들. */\n recipes: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\nasync function scanOperationUse(domain: Domain): Promise<OperationUse> {\n const routeRepo = getRepository(RouteDefinition)\n const recipeRepo = getRepository(RecipeDefinition)\n\n const [routeTotal, recipeTotal] = await Promise.all([\n routeRepo.count({ where: { domain: { id: domain.id } } as any }),\n recipeRepo.count({ where: { domain: { id: domain.id } } as any })\n ])\n\n const [routeRows, recipeRows] = await Promise.all([\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 —\n * `take` 를 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n routeRepo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', 'steps'] as any,\n take: OPERATION_SCAN_LIMIT\n }),\n recipeRepo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', 'inputs'] as any,\n take: OPERATION_SCAN_LIMIT\n })\n ])\n\n const routes = indexMultiReferrers(\n routeRows as any[],\n one => one.steps ?? [],\n one => one.name\n )\n\n const recipes = indexMultiReferrers(\n recipeRows as any[],\n one => (one.inputs ?? []).map((part: any) => part?.operation),\n one => one.name\n )\n\n return { routes, recipes, capped: routeTotal > routeRows.length || recipeTotal > recipeRows.length }\n}\n\n/** 요청 안에서 한 번만 훑는다. **약속을 담는다** — 줄들이 거의 동시에 묻는다. */\nfunction operationUseOf(context: ResolverContext): Promise<OperationUse> {\n const state = context.state as { domain: Domain; operationUse?: Promise<OperationUse> }\n\n state.operationUse = state.operationUse || scanOperationUse(state.domain)\n\n return state.operationUse\n}\n\n@Resolver(OperationDefinition)\nexport class OperationDefinitionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => OperationDefinitionList, { description: 'To fetch operation definitions' })\n async operationDefinitions(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<OperationDefinitionList> {\n const { domain } = context.state\n const { items, total } = await listMaster('operationDefinition', params, domain)\n\n return { items: items as OperationDefinition[], total }\n }\n\n /**\n * 이 공정을 단계로 갖는 **라우팅들.**\n *\n * 공정을 고치기 전에 무엇이 딸려 오는지 여기서 본다. 여럿일 수 있다 — 같은 공정을 여러 라우팅이\n * 지난다(절단은 표준에도 빠른 것에도 있다).\n *\n * 아무도 안 쓰면 빈 목록이다. 다만 그것이 「안 쓴다」인지 「다 못 봤다」인지는\n * `referrerScanCapped` 가 말한다.\n */\n @FieldResolver(type => [String], { description: 'Routes that include this operation as a step' })\n async usedInRoutes(@Root() operation: OperationDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await operationUseOf(context)).routes.get(operation.name) ?? []\n }\n\n /**\n * 이 공정에서 자재를 넣는 **제조법들.**\n *\n * 라우팅은 「어디를 지나나」이고 이것은 「거기서 무엇이 빠지나」다. 공정을 없애면 그 자재가\n * 빠질 자리를 잃는다.\n */\n @FieldResolver(type => [String], { description: 'Recipes that consume materials at this operation' })\n async consumedAt(@Root() operation: OperationDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await operationUseOf(context)).recipes.get(operation.name) ?? []\n }\n\n /**\n * 라우팅·제조법을 다 보지 못했나.\n *\n * **조용히 자르지 않는다.** 상한에 걸리면 위의 두 칸이 덜 찬 답이고, 그것을 말하지 않으면\n * 「쓰는 데가 없다」와 「못 봤다」가 같아 보인다.\n */\n @FieldResolver(type => Boolean, { description: 'True when routes or recipes were too many to scan fully' })\n async referrerScanCapped(@Root() operation: OperationDefinition, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await operationUseOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [OperationDefinition], { description: 'To create, change or retire operation definitions' })\n async updateMultipleOperationDefinition(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<OperationDefinition[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('operationDefinition')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as OperationDefinition[]\n }\n}\n"]}
1
+ {"version":3,"file":"operation-master-resolver.js","sourceRoot":"","sources":["../../../server/service/operation/operation-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { UseMiddleware } from 'type-graphql'\nimport { SpeakRefusals } from '@things-factory/shell'\nimport { Args, Arg, Ctx, Directive, FieldResolver, Int, Mutation, ObjectType, Field, Query, Resolver, Root } from 'type-graphql'\n\nimport { Domain, ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { OperationDefinition } from './operation-definition'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { indexMultiReferrers } from '../resource/referrers'\n\n/**\n * 공정 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of operation definitions' })\nexport class OperationDefinitionList {\n @Field(type => [OperationDefinition])\n items: OperationDefinition[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * **이 공정을 누가 쓰나** — 들어오는 관계.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 공정 하나를 고치거나 없앨 때 무엇이 딸려 무너지는지 알 수 없었다. 라우팅과 제조법을 하나씩 열어\n * 단계 목록을 눈으로 훑는 수밖에 없었다. 지금 miratek 은 라우팅 다섯에 제조법 다섯이라 그것이\n * 되지만, 제품이 늘면 안 된다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `RouteDefinition.steps` 와 `RecipeDefinition.inputs[].operation` 은 `simple-json` 이다.\n * 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다. 표를 훑어 뒤집는 수밖에 없다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다**(`referrerScanCapped`). 조용히 자르면 「이 공정을\n * 아무도 안 쓴다」와 「너무 많아 다 못 봤다」가 같아 보인다. 앞엣것은 지워도 되는 공정이고\n * 뒤엣것은 지우면 라인이 선다. 품목의 제조법 훑기가 같은 자리에 같은 것을 둔다.\n */\nconst OPERATION_SCAN_LIMIT = 2000\n\ninterface OperationUse {\n /** 이 공정을 단계로 갖는 라우팅들. */\n routes: Map<string, string[]>\n /** 이 공정에서 자재를 넣는 제조법들. */\n recipes: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\nasync function scanOperationUse(domain: Domain): Promise<OperationUse> {\n const routeRepo = getRepository(RouteDefinition)\n const recipeRepo = getRepository(RecipeDefinition)\n\n const [routeTotal, recipeTotal] = await Promise.all([\n routeRepo.count({ where: { domain: { id: domain.id } } as any }),\n recipeRepo.count({ where: { domain: { id: domain.id } } as any })\n ])\n\n const [routeRows, recipeRows] = await Promise.all([\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 —\n * `take` 를 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n routeRepo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', 'steps'] as any,\n take: OPERATION_SCAN_LIMIT\n }),\n recipeRepo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', 'inputs'] as any,\n take: OPERATION_SCAN_LIMIT\n })\n ])\n\n const routes = indexMultiReferrers(\n routeRows as any[],\n one => one.steps ?? [],\n one => one.name\n )\n\n const recipes = indexMultiReferrers(\n recipeRows as any[],\n one => (one.inputs ?? []).map((part: any) => part?.operation),\n one => one.name\n )\n\n return { routes, recipes, capped: routeTotal > routeRows.length || recipeTotal > recipeRows.length }\n}\n\n/** 요청 안에서 한 번만 훑는다. **약속을 담는다** — 줄들이 거의 동시에 묻는다. */\nfunction operationUseOf(context: ResolverContext): Promise<OperationUse> {\n const state = context.state as { domain: Domain; operationUse?: Promise<OperationUse> }\n\n state.operationUse = state.operationUse || scanOperationUse(state.domain)\n\n return state.operationUse\n}\n\n@Resolver(OperationDefinition)\n@UseMiddleware(SpeakRefusals)\nexport class OperationDefinitionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => OperationDefinitionList, { description: 'To fetch operation definitions' })\n async operationDefinitions(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<OperationDefinitionList> {\n const { domain } = context.state\n const { items, total } = await listMaster('operationDefinition', params, domain)\n\n return { items: items as OperationDefinition[], total }\n }\n\n /**\n * 이 공정을 단계로 갖는 **라우팅들.**\n *\n * 공정을 고치기 전에 무엇이 딸려 오는지 여기서 본다. 여럿일 수 있다 — 같은 공정을 여러 라우팅이\n * 지난다(절단은 표준에도 빠른 것에도 있다).\n *\n * 아무도 안 쓰면 빈 목록이다. 다만 그것이 「안 쓴다」인지 「다 못 봤다」인지는\n * `referrerScanCapped` 가 말한다.\n */\n @FieldResolver(type => [String], { description: 'Routes that include this operation as a step' })\n async usedInRoutes(@Root() operation: OperationDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await operationUseOf(context)).routes.get(operation.name) ?? []\n }\n\n /**\n * 이 공정에서 자재를 넣는 **제조법들.**\n *\n * 라우팅은 「어디를 지나나」이고 이것은 「거기서 무엇이 빠지나」다. 공정을 없애면 그 자재가\n * 빠질 자리를 잃는다.\n */\n @FieldResolver(type => [String], { description: 'Recipes that consume materials at this operation' })\n async consumedAt(@Root() operation: OperationDefinition, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await operationUseOf(context)).recipes.get(operation.name) ?? []\n }\n\n /**\n * 라우팅·제조법을 다 보지 못했나.\n *\n * **조용히 자르지 않는다.** 상한에 걸리면 위의 두 칸이 덜 찬 답이고, 그것을 말하지 않으면\n * 「쓰는 데가 없다」와 「못 봤다」가 같아 보인다.\n */\n @FieldResolver(type => Boolean, { description: 'True when routes or recipes were too many to scan fully' })\n async referrerScanCapped(@Root() operation: OperationDefinition, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await operationUseOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [OperationDefinition], { description: 'To create, change or retire operation definitions' })\n async updateMultipleOperationDefinition(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<OperationDefinition[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('operationDefinition')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as OperationDefinition[]\n }\n}\n"]}