@things-factory/ops-master 10.1.33 → 10.1.35

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 (150) 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 +14 -0
  5. package/dist-client/master-change-approval.js +36 -0
  6. package/dist-client/master-change-approval.js.map +1 -0
  7. package/dist-client/pages/master/material-definition.d.ts +4 -11
  8. package/dist-client/pages/master/material-definition.js +5 -53
  9. package/dist-client/pages/master/material-definition.js.map +1 -1
  10. package/dist-client/pages/master/material-revision-fields.d.ts +2 -0
  11. package/dist-client/pages/master/material-revision-fields.js +43 -0
  12. package/dist-client/pages/master/material-revision-fields.js.map +1 -0
  13. package/dist-client/pages/master/recipe-definition.d.ts +4 -135
  14. package/dist-client/pages/master/recipe-definition.js +7 -397
  15. package/dist-client/pages/master/recipe-definition.js.map +1 -1
  16. package/dist-client/pages/master/revision-comparison.d.ts +48 -0
  17. package/dist-client/pages/master/revision-comparison.js +553 -0
  18. package/dist-client/pages/master/revision-comparison.js.map +1 -0
  19. package/dist-client/pages/master/revision-view.d.ts +19 -0
  20. package/dist-client/pages/master/revision-view.js +133 -0
  21. package/dist-client/pages/master/revision-view.js.map +1 -0
  22. package/dist-client/pages/master/revision-workspace.d.ts +50 -0
  23. package/dist-client/pages/master/revision-workspace.js +752 -0
  24. package/dist-client/pages/master/revision-workspace.js.map +1 -0
  25. package/dist-client/pages/master/test-specification.d.ts +4 -92
  26. package/dist-client/pages/master/test-specification.js +5 -440
  27. package/dist-client/pages/master/test-specification.js.map +1 -1
  28. package/dist-client/tsconfig.tsbuildinfo +1 -1
  29. package/dist-server/service/asset/asset-master-resolver.js +38 -34
  30. package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
  31. package/dist-server/service/asset/index.d.ts +2 -2
  32. package/dist-server/service/equipment/equipment-master-resolver.js +54 -49
  33. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  34. package/dist-server/service/equipment/index.d.ts +2 -2
  35. package/dist-server/service/index.d.ts +18 -2
  36. package/dist-server/service/index.js +18 -0
  37. package/dist-server/service/index.js.map +1 -1
  38. package/dist-server/service/location/index.d.ts +1 -1
  39. package/dist-server/service/location/location-master-resolver.js +34 -31
  40. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  41. package/dist-server/service/master-axis.js +1 -1
  42. package/dist-server/service/master-axis.js.map +1 -1
  43. package/dist-server/service/master-change-policy.d.ts +23 -0
  44. package/dist-server/service/master-change-policy.js +134 -0
  45. package/dist-server/service/master-change-policy.js.map +1 -0
  46. package/dist-server/service/master-conformance-resolver.js +11 -1
  47. package/dist-server/service/master-conformance-resolver.js.map +1 -1
  48. package/dist-server/service/master-crud.js +9 -0
  49. package/dist-server/service/master-crud.js.map +1 -1
  50. package/dist-server/service/master-list.js +6 -0
  51. package/dist-server/service/master-list.js.map +1 -1
  52. package/dist-server/service/material/index.d.ts +3 -2
  53. package/dist-server/service/material/index.js +4 -2
  54. package/dist-server/service/material/index.js.map +1 -1
  55. package/dist-server/service/material/material-axis-status.d.ts +8 -0
  56. package/dist-server/service/material/material-axis-status.js +19 -0
  57. package/dist-server/service/material/material-axis-status.js.map +1 -0
  58. package/dist-server/service/material/material-definition.d.ts +3 -0
  59. package/dist-server/service/material/material-definition.js +13 -0
  60. package/dist-server/service/material/material-definition.js.map +1 -1
  61. package/dist-server/service/material/material-effective-list.d.ts +39 -0
  62. package/dist-server/service/material/material-effective-list.js +54 -0
  63. package/dist-server/service/material/material-effective-list.js.map +1 -0
  64. package/dist-server/service/material/material-master-resolver.js +59 -61
  65. package/dist-server/service/material/material-master-resolver.js.map +1 -1
  66. package/dist-server/service/material/material-query.js +9 -2
  67. package/dist-server/service/material/material-query.js.map +1 -1
  68. package/dist-server/service/material/material-revision-content.d.ts +10 -0
  69. package/dist-server/service/material/material-revision-content.js +92 -0
  70. package/dist-server/service/material/material-revision-content.js.map +1 -0
  71. package/dist-server/service/material/material-revision-resolver.d.ts +54 -0
  72. package/dist-server/service/material/material-revision-resolver.js +76 -0
  73. package/dist-server/service/material/material-revision-resolver.js.map +1 -0
  74. package/dist-server/service/material/material-revision-service.d.ts +144 -0
  75. package/dist-server/service/material/material-revision-service.js +534 -0
  76. package/dist-server/service/material/material-revision-service.js.map +1 -0
  77. package/dist-server/service/material/material-revision.d.ts +84 -0
  78. package/dist-server/service/material/material-revision.js +339 -0
  79. package/dist-server/service/material/material-revision.js.map +1 -0
  80. package/dist-server/service/operation/index.d.ts +1 -1
  81. package/dist-server/service/operation/operation-definition.js +1 -0
  82. package/dist-server/service/operation/operation-definition.js.map +1 -1
  83. package/dist-server/service/operation/operation-master-resolver.js +30 -30
  84. package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
  85. package/dist-server/service/personnel/index.d.ts +2 -2
  86. package/dist-server/service/personnel/personnel-master-resolver.js +38 -34
  87. package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
  88. package/dist-server/service/quality/nonconformance-cause-resolver.js +17 -14
  89. package/dist-server/service/quality/nonconformance-cause-resolver.js.map +1 -1
  90. package/dist-server/service/recipe/index.d.ts +3 -2
  91. package/dist-server/service/recipe/index.js +4 -2
  92. package/dist-server/service/recipe/index.js.map +1 -1
  93. package/dist-server/service/recipe/recipe-definition.d.ts +4 -0
  94. package/dist-server/service/recipe/recipe-definition.js +14 -1
  95. package/dist-server/service/recipe/recipe-definition.js.map +1 -1
  96. package/dist-server/service/recipe/recipe-effective-list.d.ts +49 -0
  97. package/dist-server/service/recipe/recipe-effective-list.js +96 -0
  98. package/dist-server/service/recipe/recipe-effective-list.js.map +1 -0
  99. package/dist-server/service/recipe/recipe-master-resolver.js +32 -28
  100. package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -1
  101. package/dist-server/service/recipe/recipe-query.js +9 -2
  102. package/dist-server/service/recipe/recipe-query.js.map +1 -1
  103. package/dist-server/service/recipe/recipe-revision-content.d.ts +32 -0
  104. package/dist-server/service/recipe/recipe-revision-content.js +65 -0
  105. package/dist-server/service/recipe/recipe-revision-content.js.map +1 -0
  106. package/dist-server/service/recipe/recipe-revision-line.d.ts +30 -0
  107. package/dist-server/service/recipe/recipe-revision-line.js +95 -0
  108. package/dist-server/service/recipe/recipe-revision-line.js.map +1 -0
  109. package/dist-server/service/recipe/recipe-revision-resolver.d.ts +59 -0
  110. package/dist-server/service/recipe/recipe-revision-resolver.js +76 -0
  111. package/dist-server/service/recipe/recipe-revision-resolver.js.map +1 -0
  112. package/dist-server/service/recipe/recipe-revision-service.d.ts +147 -0
  113. package/dist-server/service/recipe/recipe-revision-service.js +591 -0
  114. package/dist-server/service/recipe/recipe-revision-service.js.map +1 -0
  115. package/dist-server/service/recipe/recipe-revision.d.ts +100 -0
  116. package/dist-server/service/recipe/recipe-revision.js +373 -0
  117. package/dist-server/service/recipe/recipe-revision.js.map +1 -0
  118. package/dist-server/service/recipe/route-definition.js +1 -0
  119. package/dist-server/service/recipe/route-definition.js.map +1 -1
  120. package/dist-server/service/resource/class-members.js +9 -0
  121. package/dist-server/service/resource/class-members.js.map +1 -1
  122. package/dist-server/service/status/master-status.js +2 -1
  123. package/dist-server/service/status/master-status.js.map +1 -1
  124. package/dist-server/service/test-spec/index.d.ts +3 -2
  125. package/dist-server/service/test-spec/index.js +4 -2
  126. package/dist-server/service/test-spec/index.js.map +1 -1
  127. package/dist-server/service/test-spec/test-spec-effective-list.d.ts +34 -0
  128. package/dist-server/service/test-spec/test-spec-effective-list.js +42 -0
  129. package/dist-server/service/test-spec/test-spec-effective-list.js.map +1 -0
  130. package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +4 -14
  131. package/dist-server/service/test-spec/test-spec-master-resolver.js +12 -83
  132. package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
  133. package/dist-server/service/test-spec/test-spec-query.js +7 -1
  134. package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
  135. package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +53 -0
  136. package/dist-server/service/test-spec/test-spec-revision-resolver.js +76 -0
  137. package/dist-server/service/test-spec/test-spec-revision-resolver.js.map +1 -0
  138. package/dist-server/service/test-spec/test-spec-revision-service.d.ts +138 -0
  139. package/dist-server/service/test-spec/test-spec-revision-service.js +538 -0
  140. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -0
  141. package/dist-server/service/test-spec/test-spec-revision.d.ts +84 -0
  142. package/dist-server/service/test-spec/test-spec-revision.js +334 -0
  143. package/dist-server/service/test-spec/test-spec-revision.js.map +1 -0
  144. package/dist-server/service/test-spec/test-specification.d.ts +6 -0
  145. package/dist-server/service/test-spec/test-specification.js +15 -2
  146. package/dist-server/service/test-spec/test-specification.js.map +1 -1
  147. package/dist-server/tsconfig.tsbuildinfo +1 -1
  148. package/package.json +10 -6
  149. package/translations/en.json +148 -0
  150. package/translations/ko.json +148 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-revision.js","sourceRoot":"","sources":["../../../server/service/recipe/recipe-revision.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, Entity, ForeignKey, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport type { RecipeDefinition } from './recipe-definition'\nimport { MasterChangePolicy } from '../master-change-policy'\nimport { RecipeRevisionInput, RecipeRevisionOutput } from './recipe-revision-line'\nimport { OneToMany } from 'typeorm'\nimport type { MaterialDefinition } from '../material/material-definition'\nimport type { RouteDefinition } from './route-definition'\n\n// Relations own the columns and RelationId exposes read-only identifiers.\n// Where a composite FK is declared, disable only the redundant single-column FK;\n// the database still enforces existence, domain and recipe together.\n\n/** Content and the proposed effective date freeze together. Worklist owns the decision. */\n@Entity('ops_recipe_revision')\n@Index('ix_recipe_revision_subject', ['domain', 'recipe', 'id'], { unique: true })\n@Index('ix_recipe_revision_list', ['domain', 'recipe', 'createdAt'])\n@ForeignKey('MaterialDefinition', ['domain', 'primaryMaterial'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RouteDefinition', ['domain', 'routeDefinition'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => MasterChangePolicy, ['domain', 'policy'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RecipeRevision', ['domain', 'recipe', 'baseRevision'], ['domain', 'recipe', 'id'], {\n onDelete: 'RESTRICT'\n})\n@Index('ix_recipe_revision_number', ['domain', 'recipe', 'revisionNumber'], { unique: true })\nexport class RecipeRevision {\n /** Human revision number within one identity; assigned once under the subject write lock. */\n @Column('int') revisionNumber: number\n @ManyToOne(() => MasterChangePolicy, { nullable: false, createForeignKeyConstraints: false })\n policy: MasterChangePolicy\n @RelationId((row: RecipeRevision) => row.policy)\n policyId: string\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipeRevision) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipeRevision) => row.recipe)\n recipeId: string\n @ManyToOne(() => RecipeRevision, { nullable: true, createForeignKeyConstraints: false })\n baseRevision: RecipeRevision | null\n @RelationId((row: RecipeRevision) => row.baseRevision)\n baseRevisionId: string | null\n @Column('int') baseGeneration: number\n @Column('int', { default: 0 }) editGeneration: number\n @Column('text', { nullable: true }) description: string | null\n @Column('varchar') materialDefinitionName: string\n @ManyToOne('MaterialDefinition', { nullable: false, createForeignKeyConstraints: false }) primaryMaterial: MaterialDefinition\n @RelationId((row: RecipeRevision) => row.primaryMaterial) primaryMaterialId: string\n @ManyToOne('RouteDefinition', { nullable: true, createForeignKeyConstraints: false }) routeDefinition: RouteDefinition | null\n @RelationId((row: RecipeRevision) => row.routeDefinition) routeDefinitionId: string | null\n @Column('varchar', { nullable: true }) route: string | null\n @OneToMany(() => RecipeRevisionInput, line => line.revision)\n inputs: RecipeRevisionInput[]\n @OneToMany(() => RecipeRevisionOutput, line => line.revision)\n outputs: RecipeRevisionOutput[]\n @Column({ type: Date }) effectiveFrom: Date\n @Column('text') reason: string\n @ManyToOne(() => User, { nullable: false })\n creator: User\n @RelationId((row: RecipeRevision) => row.creator)\n creatorId: string\n @Column({ type: Date }) createdAt: Date\n @Column({ type: Date, nullable: true }) frozenAt: Date | null\n @Column('varchar', { nullable: true }) contentHash: string | null\n /** Opaque product decision ID: the common master module does not depend on worklist. */\n @Column('varchar', { nullable: true }) activityInstanceId: string | null\n}\n\n/** An append-only generation of the complete schedule for one recipe. */\n@Entity('ops_recipe_plan')\n@Index('ix_recipe_plan_generation', ['domain', 'recipe', 'generation'], { unique: true })\n@Index('ix_recipe_plan_identity', ['domain', 'recipe', 'id'], { unique: true })\n@Index('ix_recipe_plan_decision', ['activityInstanceId'], { unique: true })\n@Index('ix_recipe_plan_publication', ['publishedRevision'], { unique: true })\n@ForeignKey(() => MasterChangePolicy, ['domain', 'policy'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipeRevision, ['domain', 'recipe', 'publishedRevision'], ['domain', 'recipe', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\nexport class RecipePlan {\n @ManyToOne(() => MasterChangePolicy, { nullable: false, createForeignKeyConstraints: false })\n policy: MasterChangePolicy\n @RelationId((row: RecipePlan) => row.policy)\n policyId: string\n @ManyToOne(() => RecipeRevision, { nullable: false, createForeignKeyConstraints: false })\n publishedRevision: RecipeRevision\n @RelationId((row: RecipePlan) => row.publishedRevision)\n publishedRevisionId: string\n @Column('varchar') releaseKind: 'approved' | 'policy'\n @ManyToOne(() => User, { nullable: false }) publisher: User\n @RelationId((row: RecipePlan) => row.publisher) publisherId: string\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipePlan) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipePlan) => row.recipe)\n recipeId: string\n @Column('int') generation: number\n @Column({ type: Date }) recordedAt: Date\n @Column('varchar', { nullable: true }) activityInstanceId: string | null\n @ManyToOne(() => User, { nullable: true })\n approver: User\n @RelationId((row: RecipePlan) => row.approver)\n approverId: string\n @Column('varchar') submissionHash: string\n}\n\n@Entity('ops_recipe_segment')\n@Index('ix_recipe_segment_lookup', ['domain', 'recipe', 'plan', 'validFrom'])\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipePlan, ['domain', 'recipe', 'plan'], ['domain', 'recipe', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipeRevision, ['domain', 'recipe', 'revision'], ['domain', 'recipe', 'id'], {\n onDelete: 'RESTRICT'\n})\nexport class RecipeSegment {\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipeSegment) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipeSegment) => row.recipe)\n recipeId: string\n @ManyToOne(() => RecipePlan, { nullable: false, createForeignKeyConstraints: false })\n plan: RecipePlan\n @RelationId((row: RecipeSegment) => row.plan)\n planId: string\n @ManyToOne(() => RecipeRevision, { nullable: false, createForeignKeyConstraints: false })\n revision: RecipeRevision\n @RelationId((row: RecipeSegment) => row.revision)\n revisionId: string\n @Column({ type: Date }) validFrom: Date\n @Column({ type: Date, nullable: true }) validTo: Date | null\n}\n\n/** Permanent evidence of the selected revision, also the retroactivity interlock. */\n@Entity('ops_recipe_basis')\n@Index('ix_recipe_basis_domain_id', ['domain', 'id'], { unique: true })\n@Index('ix_recipe_basis_identity', ['domain', 'id', 'revision'], { unique: true })\n@Index('ix_recipe_basis_record', ['domain', 'recordKey'], { unique: true })\n@Index('ix_recipe_basis_time', ['domain', 'recipe', 'businessTime'])\n@ForeignKey('RecipeDefinition', ['domain', 'recipe'], ['domain', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipePlan, ['domain', 'recipe', 'plan'], ['domain', 'recipe', 'id'], { onDelete: 'RESTRICT' })\n@ForeignKey(() => RecipeRevision, ['domain', 'recipe', 'revision'], ['domain', 'recipe', 'id'], {\n onDelete: 'RESTRICT'\n})\nexport class RecipeBasis {\n /** Execution snapshot of an independently owned route; not a route master revision. */\n @Column('simple-json', { nullable: true }) routeSnapshot: {\n id: string; name: string; steps: string[]; procedure: Record<string, unknown>[] | null\n } | null\n @Column('varchar', { nullable: true }) routeHash: string | null\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false })\n domain: Domain\n @RelationId((row: RecipeBasis) => row.domain)\n domainId: string\n @ManyToOne('RecipeDefinition', { nullable: false, createForeignKeyConstraints: false })\n recipe: RecipeDefinition\n @RelationId((row: RecipeBasis) => row.recipe)\n recipeId: string\n @Column('varchar') recordKey: string\n @ManyToOne(() => RecipeRevision, { nullable: false, createForeignKeyConstraints: false })\n revision: RecipeRevision\n @RelationId((row: RecipeBasis) => row.revision)\n revisionId: string\n @ManyToOne(() => RecipePlan, { nullable: false, createForeignKeyConstraints: false })\n plan: RecipePlan\n @RelationId((row: RecipeBasis) => row.plan)\n planId: string\n @Column('varchar') contentHash: string\n @Column({ type: Date }) businessTime: Date\n @Column({ type: Date }) resolvedAt: Date\n}\n\nexport const RECIPE_REVISION_ENTITIES = [\n RecipeRevision,\n RecipePlan,\n RecipeSegment,\n RecipeBasis,\n RecipeRevisionInput,\n RecipeRevisionOutput\n]\n"]}
@@ -107,6 +107,7 @@ tslib_1.__decorate([
107
107
  ], RouteDefinition.prototype, "updaterId", void 0);
108
108
  exports.RouteDefinition = RouteDefinition = tslib_1.__decorate([
109
109
  (0, typeorm_1.Entity)({ name: 'ops_route_definition' }),
110
+ (0, typeorm_1.Index)('ix_ops_route_domain_identity', ['domain', 'id'], { unique: true }),
110
111
  (0, typeorm_1.Index)('ix_ops_route_definition_0', (route) => [route.domain, route.name], { unique: true }),
111
112
  (0, type_graphql_1.ObjectType)({ description: 'Route — the ordered operations a material passes through (kernel RouteDef).' })
112
113
  ], RouteDefinition);
@@ -1 +1 @@
1
- {"version":3,"file":"route-definition.js","sourceRoot":"","sources":["../../../server/service/recipe/route-definition.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 라우팅 — **공정을 지나는 순서**. 커널 `RouteDef` 와 같은 모양이다.\n *\n * ── 왜 마스터에 있나 (2026-08-31) ────────────────────────────────────────\n * 커널에 `RouteDef` 가 있고 `JobOrder.routeKey` 가 그것을 가리키는데, **그 값이 가리킬 표가 없었다.**\n * 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n *\n * 트윈 모델 JSON 에 선언하는 길도 있었는데 그러지 않은 이유가 셋이다.\n *\n * · **유효기간을 담을 자리가 없다.** 라우팅은 개정된다. 모델에 두면 「언제부터 이 라우팅였나」가 없고,\n * 그러면 지난 실적이 **어느 라우팅으로 만든 것인지** 잃는다. 정산과 추적이 그 위에 선다.\n * · **MES 가 트윈 없이 배포된다.** 라우팅이 모델에 살면 MES 가 트윈을 세워야 자기 지시서를 만든다.\n * · **표준이 그 자리에 둔다.** `OperationsDefinition` 은 Level 4·3 의 마스터이고 실행 모델이 아니다.\n *\n * ── 순서의 정본은 `steps` 다 ──────────────────────────────────────────────\n * `procedure`(ISA-88 절차 계층)는 그 위에 이름을 붙이는 것이다. 트리를 따로 두면 두 곳이 어긋날 수\n * 있고, 어긋났을 때 어느 쪽이 맞는지 말할 근거가 없다(§`RouteDef.procedure`).\n */\n@Entity({ name: 'ops_route_definition' })\n@Index('ix_ops_route_definition_0', (route: RouteDefinition) => [route.domain, route.name], { unique: true })\n@ObjectType({ description: 'Route — the ordered operations a material passes through (kernel RouteDef).' })\nexport class RouteDefinition {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((route: RouteDefinition) => route.domain)\n domainId?: string\n\n /** 라우팅 키 — 레시피와 오더가 이 값을 가리킨다(커널 `RouteDef.key`). */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /**\n * 지나는 공정의 **순서** — `OperationDefinition.name` 의 목록(커널 `RouteDef.steps`).\n *\n * 이름으로 가리킨다. 외래키로 묶지 않는 이유는, 공정이 유효기간으로 끝나도 그것을 지나간 지난 기록이\n * 가리킬 것을 잃지 않아야 하기 때문이다 — 마스터의 다른 축도 같은 규율이다.\n */\n @Column({ type: 'simple-json' })\n @Field(type => [String])\n steps: string[]\n\n /** ISA-88 절차 계층 — 선언하지 않으면 `steps` 만으로 돈다(커널 `RouteDef.procedure`). */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'ProcedureElementDef[]' })\n procedure?: Record<string, unknown>[]\n\n /** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /**\n * 언제까지 유효한가 — 표준 `EffectiveEndDate`.\n *\n * **없앨 때 행을 지우지 않고 이 칸을 채운다.** 지우면 그것으로 만든 지난 기록이 가리킬 것을 잃는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((route: RouteDefinition) => route.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((route: RouteDefinition) => route.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"route-definition.js","sourceRoot":"","sources":["../../../server/service/recipe/route-definition.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 라우팅 — **공정을 지나는 순서**. 커널 `RouteDef` 와 같은 모양이다.\n *\n * ── 왜 마스터에 있나 (2026-08-31) ────────────────────────────────────────\n * 커널에 `RouteDef` 가 있고 `JobOrder.routeKey` 가 그것을 가리키는데, **그 값이 가리킬 표가 없었다.**\n * 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n *\n * 트윈 모델 JSON 에 선언하는 길도 있었는데 그러지 않은 이유가 셋이다.\n *\n * · **유효기간을 담을 자리가 없다.** 라우팅은 개정된다. 모델에 두면 「언제부터 이 라우팅였나」가 없고,\n * 그러면 지난 실적이 **어느 라우팅으로 만든 것인지** 잃는다. 정산과 추적이 그 위에 선다.\n * · **MES 가 트윈 없이 배포된다.** 라우팅이 모델에 살면 MES 가 트윈을 세워야 자기 지시서를 만든다.\n * · **표준이 그 자리에 둔다.** `OperationsDefinition` 은 Level 4·3 의 마스터이고 실행 모델이 아니다.\n *\n * ── 순서의 정본은 `steps` 다 ──────────────────────────────────────────────\n * `procedure`(ISA-88 절차 계층)는 그 위에 이름을 붙이는 것이다. 트리를 따로 두면 두 곳이 어긋날 수\n * 있고, 어긋났을 때 어느 쪽이 맞는지 말할 근거가 없다(§`RouteDef.procedure`).\n */\n@Entity({ name: 'ops_route_definition' })\n@Index('ix_ops_route_domain_identity', ['domain', 'id'], { unique: true })\n@Index('ix_ops_route_definition_0', (route: RouteDefinition) => [route.domain, route.name], { unique: true })\n@ObjectType({ description: 'Route — the ordered operations a material passes through (kernel RouteDef).' })\nexport class RouteDefinition {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((route: RouteDefinition) => route.domain)\n domainId?: string\n\n /** 라우팅 키 — 레시피와 오더가 이 값을 가리킨다(커널 `RouteDef.key`). */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /**\n * 지나는 공정의 **순서** — `OperationDefinition.name` 의 목록(커널 `RouteDef.steps`).\n *\n * 이름으로 가리킨다. 외래키로 묶지 않는 이유는, 공정이 유효기간으로 끝나도 그것을 지나간 지난 기록이\n * 가리킬 것을 잃지 않아야 하기 때문이다 — 마스터의 다른 축도 같은 규율이다.\n */\n @Column({ type: 'simple-json' })\n @Field(type => [String])\n steps: string[]\n\n /** ISA-88 절차 계층 — 선언하지 않으면 `steps` 만으로 돈다(커널 `RouteDef.procedure`). */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'ProcedureElementDef[]' })\n procedure?: Record<string, unknown>[]\n\n /** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /**\n * 언제까지 유효한가 — 표준 `EffectiveEndDate`.\n *\n * **없앨 때 행을 지우지 않고 이 칸을 채운다.** 지우면 그것으로 만든 지난 기록이 가리킬 것을 잃는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((route: RouteDefinition) => route.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((route: RouteDefinition) => route.updater)\n updaterId?: string\n}\n"]}
@@ -4,6 +4,8 @@ exports.MEMBER_SCAN_LIMIT = void 0;
4
4
  exports.scanClassMembers = scanClassMembers;
5
5
  const shell_1 = require("@things-factory/shell");
6
6
  const referrers_1 = require("./referrers");
7
+ const material_definition_1 = require("../material/material-definition");
8
+ const material_effective_list_1 = require("../material/material-effective-list");
7
9
  /**
8
10
  * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.
9
11
  *
@@ -34,6 +36,13 @@ exports.MEMBER_SCAN_LIMIT = 2000;
34
36
  */
35
37
  async function scanClassMembers(Entity, field, domain) {
36
38
  const repo = (0, shell_1.getRepository)(Entity);
39
+ if (Entity === material_definition_1.MaterialDefinition) {
40
+ const { items, total } = await (0, material_effective_list_1.listEffectiveMaterialDefinitions)(repo.manager, domain, {
41
+ filters: [{ name: 'revisionNumber', operator: 'is_not_null', value: true }],
42
+ pagination: { page: 1, limit: exports.MEMBER_SCAN_LIMIT }
43
+ });
44
+ return { members: (0, referrers_1.indexMultiReferrers)(items, one => one.materialClassIds ?? [], one => one.name), capped: total > items.length };
45
+ }
37
46
  const total = await repo.count({ where: { domain: { id: domain.id } } });
38
47
  /*
39
48
  * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를
@@ -1 +1 @@
1
- {"version":3,"file":"class-members.js","sourceRoot":"","sources":["../../../server/service/resource/class-members.ts"],"names":[],"mappings":";;;AAyCA,4CAqBC;AA9DD,iDAA6D;AAE7D,2CAAiD;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4CAA4C;AAC/B,QAAA,iBAAiB,GAAG,IAAI,CAAA;AASrC;;;;GAIG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAW,EAAE,KAAa,EAAE,MAAc;IAC/E,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,MAAM,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAE/E;;;OAGG;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAQ;QACpC,IAAI,EAAE,yBAAiB;KACxB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAA,+BAAmB,EACjC,IAAa,EACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EACvB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAChB,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAI,IAAc,CAAC,MAAM,EAAE,CAAA;AAC5D,CAAC","sourcesContent":["import { Domain, getRepository } from '@things-factory/shell'\n\nimport { indexMultiReferrers } from './referrers'\n\n/**\n * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그\n * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다.\n *\n * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.\n *\n * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────\n * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로\n * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면\n * 한쪽만 고쳐지는 날이 온다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무\n * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.\n */\n\n/** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */\nexport const MEMBER_SCAN_LIMIT = 2000\n\nexport interface ClassMembers {\n /** 등급 이름 → 그 등급에 든 것들의 이름. */\n members: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\n/**\n * 소속 표를 훑어 등급별로 뒤집는다.\n *\n * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.\n */\nexport async function scanClassMembers(Entity: any, field: string, domain: Domain): Promise<ClassMembers> {\n const repo = getRepository(Entity)\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를\n * 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', field] as any,\n take: MEMBER_SCAN_LIMIT\n })\n\n const members = indexMultiReferrers(\n rows as any[],\n one => one[field] ?? [],\n one => one.name\n )\n\n return { members, capped: total > (rows as any[]).length }\n}\n"]}
1
+ {"version":3,"file":"class-members.js","sourceRoot":"","sources":["../../../server/service/resource/class-members.ts"],"names":[],"mappings":";;;AA2CA,4CA4BC;AAvED,iDAA6D;AAE7D,2CAAiD;AACjD,yEAAoE;AACpE,iFAAsF;AAEtF;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4CAA4C;AAC/B,QAAA,iBAAiB,GAAG,IAAI,CAAA;AASrC;;;;GAIG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAW,EAAE,KAAa,EAAE,MAAc;IAC/E,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,MAAM,CAAC,CAAA;IAClC,IAAI,MAAM,KAAK,wCAAkB,EAAE,CAAC;QAClC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAA,0DAAgC,EAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE;YACpF,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAC3E,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,yBAAiB,EAAE;SAC3C,CAAC,CAAA;QACT,OAAO,EAAE,OAAO,EAAE,IAAA,+BAAmB,EAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAA;IAClI,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAE/E;;;OAGG;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAQ;QACpC,IAAI,EAAE,yBAAiB;KACxB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAA,+BAAmB,EACjC,IAAa,EACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EACvB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAChB,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAI,IAAc,CAAC,MAAM,EAAE,CAAA;AAC5D,CAAC","sourcesContent":["import { Domain, getRepository } from '@things-factory/shell'\n\nimport { indexMultiReferrers } from './referrers'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { listEffectiveMaterialDefinitions } from '../material/material-effective-list'\n\n/**\n * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그\n * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다.\n *\n * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.\n *\n * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────\n * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로\n * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면\n * 한쪽만 고쳐지는 날이 온다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무\n * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.\n */\n\n/** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */\nexport const MEMBER_SCAN_LIMIT = 2000\n\nexport interface ClassMembers {\n /** 등급 이름 → 그 등급에 든 것들의 이름. */\n members: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\n/**\n * 소속 표를 훑어 등급별로 뒤집는다.\n *\n * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.\n */\nexport async function scanClassMembers(Entity: any, field: string, domain: Domain): Promise<ClassMembers> {\n const repo = getRepository(Entity)\n if (Entity === MaterialDefinition) {\n const { items, total } = await listEffectiveMaterialDefinitions(repo.manager, domain, {\n filters: [{ name: 'revisionNumber', operator: 'is_not_null', value: true }],\n pagination: { page: 1, limit: MEMBER_SCAN_LIMIT }\n } as any)\n return { members: indexMultiReferrers(items, one => one.materialClassIds ?? [], one => one.name), capped: total > items.length }\n }\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를\n * 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', field] as any,\n take: MEMBER_SCAN_LIMIT\n })\n\n const members = indexMultiReferrers(\n rows as any[],\n one => one[field] ?? [],\n one => one.name\n )\n\n return { members, capped: total > (rows as any[]).length }\n}\n"]}
@@ -11,6 +11,7 @@ const equipment_class_1 = require("../equipment/equipment-class");
11
11
  const operational_location_1 = require("../location/operational-location");
12
12
  const material_class_1 = require("../material/material-class");
13
13
  const material_definition_1 = require("../material/material-definition");
14
+ const material_axis_status_1 = require("../material/material-axis-status");
14
15
  const operation_definition_1 = require("../operation/operation-definition");
15
16
  const person_1 = require("../personnel/person");
16
17
  const personnel_class_1 = require("../personnel/personnel-class");
@@ -105,7 +106,7 @@ let OpsMasterStatusQuery = class OpsMasterStatusQuery {
105
106
  axisStatus('assets', (0, shell_1.getRepository)(physical_asset_1.PhysicalAsset), domain.id),
106
107
  axisStatus('locations', (0, shell_1.getRepository)(operational_location_1.OperationalLocation), domain.id),
107
108
  axisStatus('materialClasses', (0, shell_1.getRepository)(material_class_1.MaterialClass), domain.id),
108
- axisStatus('materialDefinitions', (0, shell_1.getRepository)(material_definition_1.MaterialDefinition), domain.id),
109
+ (0, material_axis_status_1.materialAxisStatus)((0, shell_1.getRepository)(material_definition_1.MaterialDefinition).manager, domain.id),
109
110
  axisStatus('operations', (0, shell_1.getRepository)(operation_definition_1.OperationDefinition), domain.id),
110
111
  axisStatus('recipes', (0, shell_1.getRepository)(recipe_definition_1.RecipeDefinition), domain.id),
111
112
  axisStatus('routes', (0, shell_1.getRepository)(route_definition_1.RouteDefinition), domain.id),
@@ -1 +1 @@
1
- {"version":3,"file":"master-status.js","sourceRoot":"","sources":["../../../server/service/status/master-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\nimport type { ObjectLiteral, Repository } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { PhysicalAssetClass } from '../asset/physical-asset-class'\nimport { Equipment } from '../equipment/equipment'\nimport { EquipmentClass } from '../equipment/equipment-class'\nimport { OperationalLocation } from '../location/operational-location'\nimport { MaterialClass } from '../material/material-class'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { OperationDefinition } from '../operation/operation-definition'\nimport { Person } from '../personnel/person'\nimport { PersonnelClass } from '../personnel/personnel-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { TestSpecification } from '../test-spec/test-specification'\n\nimport { axisStamp } from './axis-stamp'\n\n/**\n * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 트윈은 마스터를 주기적으로 당긴다. 그런데 인원 마스터가 수천 건이면 바뀐 것이 없어도 매번 그만큼을\n * 옮기게 된다. 이 질의는 **축마다 마지막으로 바뀐 시각과 건수만** 내므로, 트윈이 자기가 마지막으로 본\n * 값과 비교해 바뀐 축만 당길 수 있다.\n *\n * ── 왜 지우지 않는가 ────────────────────────────────────────────────────────\n * 건수가 줄지 않는 것을 전제로 한다. 마스터에서 무엇을 「없앨」 때 행을 지우지 않고 유효 기간으로\n * 끝내기 때문이다(§`effectiveEnd`). 지우면 그 개체를 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n@ObjectType({ description: 'When one master axis last changed' })\nexport class MasterAxisStatus {\n @Field({ description: 'axis name, matching the pull query' })\n axis: string\n\n @Field(type => Int, { description: 'number of rows in this axis' })\n count: number\n\n /** 마지막으로 바뀐 시각. 축이 비어 있으면 없다 — 「비어 있다」와 「바뀐 적 없다」를 섞지 않는다. */\n @Field({ nullable: true, description: 'last change time, absent when the axis is empty' })\n changedAt?: Date\n\n /**\n * 이 축의 상태를 대표하는 값 하나 — **크기로 비교하지 말고 같은지만 본다.**\n *\n * 시각을 크기로 비교하면 시계가 되감길 때(백업 복원 · 시각 보정) 바뀐 것을 놓친다. 개수를 함께 담아\n * 두므로 같은 초에 두 번 바뀌어도 표시가 달라진다. 읽는 쪽은 자기가 마지막으로 본 표시와 **다르면**\n * 당기면 된다.\n */\n @Field({ description: 'opaque value — compare for equality, never for order' })\n stamp: string\n}\n\n@ObjectType({ description: 'Change status of every master axis' })\nexport class OpsMasterStatus {\n @Field(type => [MasterAxisStatus])\n axes: MasterAxisStatus[]\n\n}\n\n/** 축 하나의 건수와 마지막 변경 시각. 두 값을 한 질의로 읽는다. */\nasync function axisStatus<T extends ObjectLiteral>(\n axis: string,\n repository: Repository<T>,\n domainId: string\n): Promise<MasterAxisStatus> {\n const row = await repository\n .createQueryBuilder('row')\n .select('COUNT(1)', 'count')\n .addSelect('MAX(row.updatedAt)', 'changedAt')\n .where('row.domain = :domainId', { domainId })\n .getRawOne<{ count: string | number; changedAt: Date | string | null }>()\n\n const count = Number(row?.count ?? 0)\n const changedAt = row?.changedAt ? new Date(row.changedAt) : undefined\n\n return {\n axis,\n count,\n stamp: axisStamp(count, changedAt),\n ...(changedAt ? { changedAt } : {})\n }\n}\n\n\n@Resolver(OpsMasterStatus)\nexport class OpsMasterStatusQuery {\n /**\n * 모든 축의 변경 상태 — 트윈이 주기적으로 부르는 자리.\n *\n * 한 번에 낸다. 축마다 따로 물으면 일곱 번 오가고, 그 사이에 마스터가 바뀌면 축들이 서로 다른 시점을\n * 보게 된다.\n */\n @Query(returns => OpsMasterStatus, { description: 'Change status of every master axis' })\n async opsMasterStatus(@Ctx() context: ResolverContext): Promise<OpsMasterStatus> {\n const { domain } = context.state\n\n const axes = await Promise.all([\n axisStatus('personnelClasses', getRepository(PersonnelClass), domain.id),\n axisStatus('persons', getRepository(Person), domain.id),\n axisStatus('equipmentClasses', getRepository(EquipmentClass), domain.id),\n axisStatus('equipment', getRepository(Equipment), domain.id),\n axisStatus('assetClasses', getRepository(PhysicalAssetClass), domain.id),\n axisStatus('assets', getRepository(PhysicalAsset), domain.id),\n axisStatus('locations', getRepository(OperationalLocation), domain.id),\n axisStatus('materialClasses', getRepository(MaterialClass), domain.id),\n axisStatus('materialDefinitions', getRepository(MaterialDefinition), domain.id),\n axisStatus('operations', getRepository(OperationDefinition), domain.id),\n axisStatus('recipes', getRepository(RecipeDefinition), domain.id),\n axisStatus('routes', getRepository(RouteDefinition), domain.id),\n axisStatus('testSpecifications', getRepository(TestSpecification), domain.id)\n ])\n\n return { axes }\n }\n}\n"]}
1
+ {"version":3,"file":"master-status.js","sourceRoot":"","sources":["../../../server/service/status/master-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\nimport type { ObjectLiteral, Repository } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { PhysicalAssetClass } from '../asset/physical-asset-class'\nimport { Equipment } from '../equipment/equipment'\nimport { EquipmentClass } from '../equipment/equipment-class'\nimport { OperationalLocation } from '../location/operational-location'\nimport { MaterialClass } from '../material/material-class'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { materialAxisStatus } from '../material/material-axis-status'\nimport { OperationDefinition } from '../operation/operation-definition'\nimport { Person } from '../personnel/person'\nimport { PersonnelClass } from '../personnel/personnel-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { TestSpecification } from '../test-spec/test-specification'\n\nimport { axisStamp } from './axis-stamp'\n\n/**\n * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 트윈은 마스터를 주기적으로 당긴다. 그런데 인원 마스터가 수천 건이면 바뀐 것이 없어도 매번 그만큼을\n * 옮기게 된다. 이 질의는 **축마다 마지막으로 바뀐 시각과 건수만** 내므로, 트윈이 자기가 마지막으로 본\n * 값과 비교해 바뀐 축만 당길 수 있다.\n *\n * ── 왜 지우지 않는가 ────────────────────────────────────────────────────────\n * 건수가 줄지 않는 것을 전제로 한다. 마스터에서 무엇을 「없앨」 때 행을 지우지 않고 유효 기간으로\n * 끝내기 때문이다(§`effectiveEnd`). 지우면 그 개체를 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n@ObjectType({ description: 'When one master axis last changed' })\nexport class MasterAxisStatus {\n @Field({ description: 'axis name, matching the pull query' })\n axis: string\n\n @Field(type => Int, { description: 'number of rows in this axis' })\n count: number\n\n /** 마지막으로 바뀐 시각. 축이 비어 있으면 없다 — 「비어 있다」와 「바뀐 적 없다」를 섞지 않는다. */\n @Field({ nullable: true, description: 'last change time, absent when the axis is empty' })\n changedAt?: Date\n\n /**\n * 이 축의 상태를 대표하는 값 하나 — **크기로 비교하지 말고 같은지만 본다.**\n *\n * 시각을 크기로 비교하면 시계가 되감길 때(백업 복원 · 시각 보정) 바뀐 것을 놓친다. 개수를 함께 담아\n * 두므로 같은 초에 두 번 바뀌어도 표시가 달라진다. 읽는 쪽은 자기가 마지막으로 본 표시와 **다르면**\n * 당기면 된다.\n */\n @Field({ description: 'opaque value — compare for equality, never for order' })\n stamp: string\n}\n\n@ObjectType({ description: 'Change status of every master axis' })\nexport class OpsMasterStatus {\n @Field(type => [MasterAxisStatus])\n axes: MasterAxisStatus[]\n\n}\n\n/** 축 하나의 건수와 마지막 변경 시각. 두 값을 한 질의로 읽는다. */\nasync function axisStatus<T extends ObjectLiteral>(\n axis: string,\n repository: Repository<T>,\n domainId: string\n): Promise<MasterAxisStatus> {\n const row = await repository\n .createQueryBuilder('row')\n .select('COUNT(1)', 'count')\n .addSelect('MAX(row.updatedAt)', 'changedAt')\n .where('row.domain = :domainId', { domainId })\n .getRawOne<{ count: string | number; changedAt: Date | string | null }>()\n\n const count = Number(row?.count ?? 0)\n const changedAt = row?.changedAt ? new Date(row.changedAt) : undefined\n\n return {\n axis,\n count,\n stamp: axisStamp(count, changedAt),\n ...(changedAt ? { changedAt } : {})\n }\n}\n\n\n@Resolver(OpsMasterStatus)\nexport class OpsMasterStatusQuery {\n /**\n * 모든 축의 변경 상태 — 트윈이 주기적으로 부르는 자리.\n *\n * 한 번에 낸다. 축마다 따로 물으면 일곱 번 오가고, 그 사이에 마스터가 바뀌면 축들이 서로 다른 시점을\n * 보게 된다.\n */\n @Query(returns => OpsMasterStatus, { description: 'Change status of every master axis' })\n async opsMasterStatus(@Ctx() context: ResolverContext): Promise<OpsMasterStatus> {\n const { domain } = context.state\n\n const axes = await Promise.all([\n axisStatus('personnelClasses', getRepository(PersonnelClass), domain.id),\n axisStatus('persons', getRepository(Person), domain.id),\n axisStatus('equipmentClasses', getRepository(EquipmentClass), domain.id),\n axisStatus('equipment', getRepository(Equipment), domain.id),\n axisStatus('assetClasses', getRepository(PhysicalAssetClass), domain.id),\n axisStatus('assets', getRepository(PhysicalAsset), domain.id),\n axisStatus('locations', getRepository(OperationalLocation), domain.id),\n axisStatus('materialClasses', getRepository(MaterialClass), domain.id),\n materialAxisStatus(getRepository(MaterialDefinition).manager, domain.id),\n axisStatus('operations', getRepository(OperationDefinition), domain.id),\n axisStatus('recipes', getRepository(RecipeDefinition), domain.id),\n axisStatus('routes', getRepository(RouteDefinition), domain.id),\n axisStatus('testSpecifications', getRepository(TestSpecification), domain.id)\n ])\n\n return { axes }\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { TestSpecification } from './test-specification';
2
2
  import { TestSpecificationQuery } from './test-spec-query';
3
3
  import { TestSpecificationResolver } from './test-spec-master-resolver';
4
- export declare const entities: (typeof TestSpecification)[];
5
- export declare const resolvers: (typeof TestSpecificationQuery | typeof TestSpecificationResolver)[];
4
+ import { TestSpecificationRevisionResolver } from './test-spec-revision-resolver';
5
+ export declare const entities: (typeof TestSpecification | typeof import("./test-spec-revision").TestSpecificationRevision | typeof import("./test-spec-revision").TestSpecificationPlan | typeof import("./test-spec-revision").TestSpecificationSegment | typeof import("./test-spec-revision").TestSpecificationBasis)[];
6
+ export declare const resolvers: (typeof TestSpecificationResolver | typeof TestSpecificationRevisionResolver | typeof TestSpecificationQuery)[];
@@ -4,6 +4,8 @@ exports.resolvers = exports.entities = void 0;
4
4
  const test_specification_1 = require("./test-specification");
5
5
  const test_spec_query_1 = require("./test-spec-query");
6
6
  const test_spec_master_resolver_1 = require("./test-spec-master-resolver");
7
- exports.entities = [test_specification_1.TestSpecification];
8
- exports.resolvers = [test_spec_query_1.TestSpecificationQuery, test_spec_master_resolver_1.TestSpecificationResolver];
7
+ const test_spec_revision_1 = require("./test-spec-revision");
8
+ const test_spec_revision_resolver_1 = require("./test-spec-revision-resolver");
9
+ exports.entities = [test_specification_1.TestSpecification, ...test_spec_revision_1.TEST_SPEC_REVISION_ENTITIES];
10
+ exports.resolvers = [test_spec_query_1.TestSpecificationQuery, test_spec_master_resolver_1.TestSpecificationResolver, test_spec_revision_resolver_1.TestSpecificationRevisionResolver];
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/test-spec/index.ts"],"names":[],"mappings":";;;AAAA,6DAAwD;AACxD,uDAA0D;AAC1D,2EAAuE;AAE1D,QAAA,QAAQ,GAAG,CAAC,sCAAiB,CAAC,CAAA;AAC9B,QAAA,SAAS,GAAG,CAAC,wCAAsB,EAAE,qDAAyB,CAAC,CAAA","sourcesContent":["import { TestSpecification } from './test-specification'\nimport { TestSpecificationQuery } from './test-spec-query'\nimport { TestSpecificationResolver } from './test-spec-master-resolver'\n\nexport const entities = [TestSpecification]\nexport const resolvers = [TestSpecificationQuery, TestSpecificationResolver]\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/test-spec/index.ts"],"names":[],"mappings":";;;AAAA,6DAAwD;AACxD,uDAA0D;AAC1D,2EAAuE;AACvE,6DAAkE;AAClE,+EAAiF;AAEpE,QAAA,QAAQ,GAAG,CAAC,sCAAiB,EAAE,GAAG,gDAA2B,CAAC,CAAA;AAC9D,QAAA,SAAS,GAAG,CAAC,wCAAsB,EAAE,qDAAyB,EAAE,+DAAiC,CAAC,CAAA","sourcesContent":["import { TestSpecification } from './test-specification'\nimport { TestSpecificationQuery } from './test-spec-query'\nimport { TestSpecificationResolver } from './test-spec-master-resolver'\nimport { TEST_SPEC_REVISION_ENTITIES } from './test-spec-revision'\nimport { TestSpecificationRevisionResolver } from './test-spec-revision-resolver'\n\nexport const entities = [TestSpecification, ...TEST_SPEC_REVISION_ENTITIES]\nexport const resolvers = [TestSpecificationQuery, TestSpecificationResolver, TestSpecificationRevisionResolver]\n"]}
@@ -0,0 +1,34 @@
1
+ import { EntityManager } from 'typeorm';
2
+ import { Domain, ListParam } from '@things-factory/shell';
3
+ import { TestSpecification } from './test-specification';
4
+ /** Filter, sort, paginate and display the same time projection. No per-row resolution queries. */
5
+ export declare function listEffectiveTestSpecifications(manager: EntityManager, domain: Domain, params: ListParam, at?: Date): Promise<{
6
+ items: (TestSpecification & {
7
+ revisionNumber?: number;
8
+ id: string;
9
+ domain?: Domain;
10
+ domainId?: string;
11
+ planGeneration: number;
12
+ revisionLock: number;
13
+ name: string;
14
+ description?: string;
15
+ version?: string;
16
+ criteria?: Record<string, unknown>[];
17
+ effectiveStart?: Date;
18
+ effectiveEnd?: Date;
19
+ createdAt?: Date;
20
+ updatedAt?: Date;
21
+ creator?: import("@things-factory/auth-base").User;
22
+ creatorId?: string;
23
+ updater?: import("@things-factory/auth-base").User;
24
+ updaterId?: string;
25
+ } & {
26
+ description: string;
27
+ revisionNumber: number;
28
+ version: string;
29
+ criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
30
+ effectiveStart: Date;
31
+ effectiveEnd: Date;
32
+ })[];
33
+ total: number;
34
+ }>;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listEffectiveTestSpecifications = listEffectiveTestSpecifications;
4
+ const shell_1 = require("@things-factory/shell");
5
+ const test_specification_1 = require("./test-specification");
6
+ const test_spec_revision_1 = require("./test-spec-revision");
7
+ /** Filter, sort, paginate and display the same time projection. No per-row resolution queries. */
8
+ async function listEffectiveTestSpecifications(manager, domain, params, at = new Date()) {
9
+ const query = (0, shell_1.getQueryBuilderFromListParams)({
10
+ repository: manager.getRepository(test_specification_1.TestSpecification),
11
+ domain,
12
+ params,
13
+ alias: 'spec',
14
+ searchables: ['name', 'description'],
15
+ sortables: ['id', 'name', 'revisionNumber', 'description', 'version', 'effectiveStart', 'effectiveEnd', 'createdAt', 'updatedAt'],
16
+ columnExpressions: {
17
+ revisionNumber: 'revision.revisionNumber',
18
+ description: 'revision.description',
19
+ version: 'revision.version',
20
+ effectiveStart: 'segment.validFrom',
21
+ effectiveEnd: 'segment.validTo'
22
+ }
23
+ });
24
+ query
25
+ .leftJoin(test_spec_revision_1.TestSpecificationPlan, 'plan', 'plan.spec_id = spec.id AND plan.domain_id = spec.domain_id AND plan.generation = spec.plan_generation')
26
+ .leftJoinAndMapOne('spec.selectedSegment', test_spec_revision_1.TestSpecificationSegment, 'segment', 'segment.plan_id = plan.id AND segment.validFrom <= :at AND (segment.validTo IS NULL OR segment.validTo > :at)', { at })
27
+ .leftJoinAndMapOne('spec.selectedRevision', test_spec_revision_1.TestSpecificationRevision, 'revision', 'revision.id = segment.revision_id');
28
+ const [rows, total] = await query.getManyAndCount();
29
+ const items = rows.map(row => {
30
+ const { selectedRevision, selectedSegment, ...identity } = row;
31
+ return Object.assign(new test_specification_1.TestSpecification(), identity, {
32
+ description: selectedRevision?.description ?? null,
33
+ revisionNumber: selectedRevision?.revisionNumber ?? null,
34
+ version: selectedRevision?.version ?? null,
35
+ criteria: selectedRevision?.criteria ?? [],
36
+ effectiveStart: selectedSegment?.validFrom ?? null,
37
+ effectiveEnd: selectedSegment?.validTo ?? null
38
+ });
39
+ });
40
+ return { items, total };
41
+ }
42
+ //# sourceMappingURL=test-spec-effective-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-spec-effective-list.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-effective-list.ts"],"names":[],"mappings":";;AAWA,0EAqDC;AA/DD,iDAAwF;AACxF,6DAAwD;AACxD,6DAAiH;AAOjH,kGAAkG;AAC3F,KAAK,UAAU,+BAA+B,CACnD,OAAsB,EACtB,MAAc,EACd,MAAiB,EACjB,EAAE,GAAG,IAAI,IAAI,EAAE;IAEf,MAAM,KAAK,GAAG,IAAA,qCAA6B,EAAC;QAC1C,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,sCAAiB,CAAC;QACpD,MAAM;QACN,MAAM;QACN,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC;QACpC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC;QACjI,iBAAiB,EAAE;YACjB,cAAc,EAAE,yBAAyB;YACzC,WAAW,EAAE,sBAAsB;YACnC,OAAO,EAAE,kBAAkB;YAC3B,cAAc,EAAE,mBAAmB;YACnC,YAAY,EAAE,iBAAiB;SAChC;KACF,CAAC,CAAA;IACF,KAAK;SACF,QAAQ,CACP,0CAAqB,EACrB,MAAM,EACN,uGAAuG,CACxG;SACA,iBAAiB,CAChB,sBAAsB,EACtB,6CAAwB,EACxB,SAAS,EACT,+GAA+G,EAC/G,EAAE,EAAE,EAAE,CACP;SACA,iBAAiB,CAChB,uBAAuB,EACvB,8CAAyB,EACzB,UAAU,EACV,mCAAmC,CACpC,CAAA;IACH,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAA;IACnD,MAAM,KAAK,GAAI,IAAuB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QAC/C,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAA;QAC9D,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,sCAAiB,EAAE,EAAE,QAAQ,EAAE;YACtD,WAAW,EAAE,gBAAgB,EAAE,WAAW,IAAI,IAAI;YAClD,cAAc,EAAE,gBAAgB,EAAE,cAAc,IAAI,IAAI;YACxD,OAAO,EAAE,gBAAgB,EAAE,OAAO,IAAI,IAAI;YAC1C,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,IAAI,EAAE;YAC1C,cAAc,EAAE,eAAe,EAAE,SAAS,IAAI,IAAI;YAClD,YAAY,EAAE,eAAe,EAAE,OAAO,IAAI,IAAI;SAC/C,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;AACzB,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\nimport { Domain, getQueryBuilderFromListParams, ListParam } from '@things-factory/shell'\nimport { TestSpecification } from './test-specification'\nimport { TestSpecificationPlan, TestSpecificationRevision, TestSpecificationSegment } from './test-spec-revision'\n\ntype EffectiveRow = TestSpecification & {\n selectedRevision?: TestSpecificationRevision\n selectedSegment?: TestSpecificationSegment\n}\n\n/** Filter, sort, paginate and display the same time projection. No per-row resolution queries. */\nexport async function listEffectiveTestSpecifications(\n manager: EntityManager,\n domain: Domain,\n params: ListParam,\n at = new Date()\n) {\n const query = getQueryBuilderFromListParams({\n repository: manager.getRepository(TestSpecification),\n domain,\n params,\n alias: 'spec',\n searchables: ['name', 'description'],\n sortables: ['id', 'name', 'revisionNumber', 'description', 'version', 'effectiveStart', 'effectiveEnd', 'createdAt', 'updatedAt'],\n columnExpressions: {\n revisionNumber: 'revision.revisionNumber',\n description: 'revision.description',\n version: 'revision.version',\n effectiveStart: 'segment.validFrom',\n effectiveEnd: 'segment.validTo'\n }\n })\n query\n .leftJoin(\n TestSpecificationPlan,\n 'plan',\n 'plan.spec_id = spec.id AND plan.domain_id = spec.domain_id AND plan.generation = spec.plan_generation'\n )\n .leftJoinAndMapOne(\n 'spec.selectedSegment',\n TestSpecificationSegment,\n 'segment',\n 'segment.plan_id = plan.id AND segment.validFrom <= :at AND (segment.validTo IS NULL OR segment.validTo > :at)',\n { at }\n )\n .leftJoinAndMapOne(\n 'spec.selectedRevision',\n TestSpecificationRevision,\n 'revision',\n 'revision.id = segment.revision_id'\n )\n const [rows, total] = await query.getManyAndCount()\n const items = (rows as EffectiveRow[]).map(row => {\n const { selectedRevision, selectedSegment, ...identity } = row\n return Object.assign(new TestSpecification(), identity, {\n description: selectedRevision?.description ?? null,\n revisionNumber: selectedRevision?.revisionNumber ?? null,\n version: selectedRevision?.version ?? null,\n criteria: selectedRevision?.criteria ?? [],\n effectiveStart: selectedSegment?.validFrom ?? null,\n effectiveEnd: selectedSegment?.validTo ?? null\n })\n })\n return { items, total }\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  import { ListParam } from '@things-factory/shell';
2
- import { MasterPatch } from '../master-crud';
2
+ import type { MasterPatch } from '../master-crud';
3
3
  import { CriterionPatch } from './criteria-rows';
4
4
  import { TestSpecification } from './test-specification';
5
+ /** Reads project the effective revision. Changes go through the revision draft resolver. */
5
6
  /**
6
7
  * 기준 한 줄 — **화면이 받는 모양.**
7
8
  *
@@ -29,13 +30,7 @@ export declare class TestSpecificationList {
29
30
  }
30
31
  export declare class TestSpecificationResolver {
31
32
  testSpecifications(params: ListParam, context: ResolverContext): Promise<TestSpecificationList>;
32
- /**
33
- * 그리드가 보낸 변경을 한 번에 적용한다.
34
- *
35
- * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을
36
- * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
37
- * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
38
- */
33
+ /** Explicitly closed old write door: even non-regulated publication must retain a revision. */
39
34
  updateMultipleTestSpecification(patches: MasterPatch[], context: ResolverContext): Promise<TestSpecification[]>;
40
35
  /**
41
36
  * 명세 하나의 기준을 **행으로** 낸다.
@@ -44,11 +39,6 @@ export declare class TestSpecificationResolver {
44
39
  * 「기준이 없는 명세」와 같아 보이고, 그러면 사람이 기준을 다시 적는다.
45
40
  */
46
41
  testSpecificationCriteria(name: string, context: ResolverContext): Promise<TestSpecificationCriterionRow[]>;
47
- /**
48
- * 기준을 **바뀐 줄만** 받아 얹는다.
49
- *
50
- * 목록 전체를 받으면 두 사람이 같은 명세를 열어 각자 한 줄씩 고쳤을 때 나중에 저장한 쪽이 앞사람의
51
- * 줄을 지운다. 판단은 `criteria-rows.ts` 와 `criteria-check.ts` 가 든다.
52
- */
42
+ /** Criteria belong to a draft, not to the stable identity row. */
53
43
  updateTestSpecificationCriteria(name: string, patches: CriterionPatch[], context: ResolverContext): Promise<TestSpecificationCriterionRow[]>;
54
44
  }
@@ -4,54 +4,11 @@ exports.TestSpecificationResolver = exports.TestSpecificationList = exports.Test
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 master_crud_1 = require("../master-crud");
8
- const master_axis_1 = require("../master-axis");
9
- const master_list_1 = require("../master-list");
10
- const criteria_check_1 = require("./criteria-check");
11
7
  const criteria_rows_1 = require("./criteria-rows");
12
8
  const test_specification_1 = require("./test-specification");
13
- /**
14
- * 검사 명세 축을 채우고 고친다.
15
- *
16
- * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────
17
- * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의
18
- * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —
19
- * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.
20
- *
21
- * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.
22
- *
23
- * ── 기준의 내용까지는 공통 규칙이 못 본다 ──────────────────────────────────
24
- * 모양을 옮기는 일은 스키마가 한다 — `criteria` 는 `ScalarObject` 라 구조가 구조로 오간다.
25
- * 그러나 **판정에 쓸 수 있는 내용인가**는 이 축만 안다(단위가 있나, 하한이 상한보다 크지 않나).
26
- * 그래서 저장 전 검증만 여기서 든다.
27
- */
28
- /**
29
- * 패치 하나의 기준 칸을 저장할 모양으로 바꾼다.
30
- *
31
- * ── 왜 저장 전에 막나 ──────────────────────────────────────────────────────
32
- * 판정하는 쪽은 **모르는 것을 통과로 읽지 않도록** 만들어져 있다. 그래서 망가진 기준이 들어와도 터지지
33
- * 않고 조용히 「판정하지 않음」이나 「늘 한계 안」이 된다. 기준을 적은 사람은 적었다고 믿고 검사원은
34
- * 통과했다고 읽는다. 그 어긋남을 막을 수 있는 자리는 여기뿐이다.
35
- *
36
- * 규칙은 `criteria-check.ts` 가 든다. 여기는 칸을 꺼내 맡기고 답을 그대로 올린다.
37
- */
38
- function prepareCriteria(patch) {
39
- if (!('criteria' in patch)) {
40
- /* 기준을 안 건드린 패치다. 이름만 고치는 것도 정상이다. */
41
- return patch;
42
- }
43
- const read = (0, criteria_check_1.readCriteria)(patch.criteria);
44
- if (read.read === 'failed') {
45
- throw new Error(`검사 명세 "${patch.name ?? patch.id ?? ''}" — ${read.detail}`);
46
- }
47
- const verdict = (0, criteria_check_1.judgeCriteria)(read.criteria);
48
- if ((0, criteria_check_1.criteriaRejected)(verdict)) {
49
- /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */
50
- throw new Error(`검사 명세 "${patch.name ?? patch.id ?? ''}" 의 기준을 저장하지 않았습니다.\n` +
51
- verdict.problems.map(one => `· ${one}`).join('\n'));
52
- }
53
- return { ...patch, criteria: read.criteria };
54
- }
9
+ const test_spec_revision_service_1 = require("./test-spec-revision-service");
10
+ const test_spec_effective_list_1 = require("./test-spec-effective-list");
11
+ /** Reads project the effective revision. Changes go through the revision draft resolver. */
55
12
  /**
56
13
  * 기준 한 줄 — **화면이 받는 모양.**
57
14
  *
@@ -113,24 +70,11 @@ exports.TestSpecificationList = TestSpecificationList = tslib_1.__decorate([
113
70
  let TestSpecificationResolver = class TestSpecificationResolver {
114
71
  async testSpecifications(params, context) {
115
72
  const { domain } = context.state;
116
- const { items, total } = await (0, master_list_1.listMaster)('testSpecification', params, domain);
117
- return { items: items, total };
73
+ return (0, test_spec_effective_list_1.listEffectiveTestSpecifications)((0, shell_1.getRepository)(test_specification_1.TestSpecification).manager, domain, params);
118
74
  }
119
- /**
120
- * 그리드가 보낸 변경을 한 번에 적용한다.
121
- *
122
- * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을
123
- * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
124
- * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
125
- */
75
+ /** Explicitly closed old write door: even non-regulated publication must retain a revision. */
126
76
  async updateMultipleTestSpecification(patches, context) {
127
- const { domain, user, tx } = context.state;
128
- const axis = (0, master_axis_1.masterAxisOf)('testSpecification');
129
- const prepared = patches.map(patch => prepareCriteria(patch));
130
- const saved = await (0, master_crud_1.saveMasterPatches)(tx, domain, axis.entity, prepared, user, {
131
- hasEffectivePeriod: axis.hasEffectivePeriod
132
- });
133
- return saved;
77
+ throw new test_spec_revision_service_1.TestSpecificationRefusal('REVISION_REQUIRED', 'Use a test specification draft; direct identity-row changes are not supported.');
134
78
  }
135
79
  /**
136
80
  * 명세 하나의 기준을 **행으로** 낸다.
@@ -145,28 +89,12 @@ let TestSpecificationResolver = class TestSpecificationResolver {
145
89
  if (!spec) {
146
90
  throw new Error(`검사 명세 "${name}" 를 찾지 못했습니다`);
147
91
  }
148
- return (0, criteria_rows_1.toCriterionRows)(spec.criteria ?? []);
92
+ const effective = await (0, test_spec_revision_service_1.effectiveTestSpecification)((0, shell_1.getRepository)(test_specification_1.TestSpecification).manager, domain.id, spec);
93
+ return (0, criteria_rows_1.toCriterionRows)(effective.criteria ?? []);
149
94
  }
150
- /**
151
- * 기준을 **바뀐 줄만** 받아 얹는다.
152
- *
153
- * 목록 전체를 받으면 두 사람이 같은 명세를 열어 각자 한 줄씩 고쳤을 때 나중에 저장한 쪽이 앞사람의
154
- * 줄을 지운다. 판단은 `criteria-rows.ts` 와 `criteria-check.ts` 가 든다.
155
- */
95
+ /** Criteria belong to a draft, not to the stable identity row. */
156
96
  async updateTestSpecificationCriteria(name, patches, context) {
157
- const { domain, user, tx } = context.state;
158
- const repository = tx.getRepository(test_specification_1.TestSpecification);
159
- const spec = await repository.findOne({ where: { domain: { id: domain.id }, name } });
160
- if (!spec) {
161
- throw new Error(`검사 명세 "${name}" 를 찾지 못했습니다`);
162
- }
163
- const applied = (0, criteria_rows_1.applyCriterionPatches)(spec.criteria ?? [], patches);
164
- if (applied.applied === 'rejected') {
165
- /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */
166
- throw new Error(`검사 명세 "${name}" 의 기준을 저장하지 않았습니다.\n` + applied.problems.map(one => `· ${one}`).join('\n'));
167
- }
168
- await repository.save({ ...spec, criteria: applied.criteria, updater: user });
169
- return (0, criteria_rows_1.toCriterionRows)(applied.criteria);
97
+ throw new test_spec_revision_service_1.TestSpecificationRefusal('REVISION_REQUIRED', 'Edit the draft criteria, then submit the frozen revision for approval.');
170
98
  }
171
99
  };
172
100
  exports.TestSpecificationResolver = TestSpecificationResolver;
@@ -210,6 +138,7 @@ tslib_1.__decorate([
210
138
  tslib_1.__metadata("design:returntype", Promise)
211
139
  ], TestSpecificationResolver.prototype, "updateTestSpecificationCriteria", null);
212
140
  exports.TestSpecificationResolver = TestSpecificationResolver = tslib_1.__decorate([
213
- (0, type_graphql_1.Resolver)(test_specification_1.TestSpecification)
141
+ (0, type_graphql_1.Resolver)(test_specification_1.TestSpecification),
142
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
214
143
  ], TestSpecificationResolver);
215
144
  //# sourceMappingURL=test-spec-master-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-spec-master-resolver.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Args, Arg, Ctx, Directive, Float, Int, Mutation, ObjectType, Field, Query, Resolver } from 'type-graphql'\n\nimport { 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 { criteriaRejected, judgeCriteria, readCriteria } from './criteria-check'\nimport { CriterionPatch, CriterionRow, applyCriterionPatches, toCriterionRows } from './criteria-rows'\nimport { TestSpecification } from './test-specification'\n\n/**\n * 검사 명세 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n *\n * ── 기준의 내용까지는 공통 규칙이 못 본다 ──────────────────────────────────\n * 모양을 옮기는 일은 스키마가 한다 — `criteria` 는 `ScalarObject` 라 구조가 구조로 오간다.\n * 그러나 **판정에 쓸 수 있는 내용인가**는 이 축만 안다(단위가 있나, 하한이 상한보다 크지 않나).\n * 그래서 저장 전 검증만 여기서 든다.\n */\n\n/**\n * 패치 하나의 기준 칸을 저장할 모양으로 바꾼다.\n *\n * ── 왜 저장 전에 막나 ──────────────────────────────────────────────────────\n * 판정하는 쪽은 **모르는 것을 통과로 읽지 않도록** 만들어져 있다. 그래서 망가진 기준이 들어와도 터지지\n * 않고 조용히 「판정하지 않음」이나 「늘 한계 안」이 된다. 기준을 적은 사람은 적었다고 믿고 검사원은\n * 통과했다고 읽는다. 그 어긋남을 막을 수 있는 자리는 여기뿐이다.\n *\n * 규칙은 `criteria-check.ts` 가 든다. 여기는 칸을 꺼내 맡기고 답을 그대로 올린다.\n */\nfunction prepareCriteria(patch: MasterPatch): MasterPatch {\n if (!('criteria' in patch)) {\n /* 기준을 안 건드린 패치다. 이름만 고치는 것도 정상이다. */\n return patch\n }\n\n const read = readCriteria(patch.criteria)\n\n if (read.read === 'failed') {\n throw new Error(`검사 명세 \"${patch.name ?? patch.id ?? ''}\" — ${read.detail}`)\n }\n\n const verdict = judgeCriteria(read.criteria)\n\n if (criteriaRejected(verdict)) {\n /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */\n throw new Error(\n `검사 명세 \"${patch.name ?? patch.id ?? ''}\" 의 기준을 저장하지 않았습니다.\\n` +\n verdict.problems.map(one => `· ${one}`).join('\\n')\n )\n }\n\n return { ...patch, criteria: read.criteria }\n}\n\n/**\n * 기준 한 줄 — **화면이 받는 모양.**\n *\n * 한계를 펴서 낸다. 화면은 이름 · 설명 · 하한 · 상한 · 단위 · 표현식이 각자 칸인 표를 그리고,\n * JSON 을 아예 모른다. 옮김은 `criteria-rows.ts` 한 곳이 든다 — 저장이 표로 내려가도 그 파일만 바뀐다.\n */\n@ObjectType({ description: 'One test specification criterion, flattened for editing' })\nexport class TestSpecificationCriterionRow {\n @Field({ description: 'criterion id — measurements find their criterion by this' })\n id: string\n\n @Field({ nullable: true, description: 'what is being checked' })\n description?: string\n\n @Field({ nullable: true, description: 'ISA-95 EvaluatedPropertyID — which property is measured' })\n evaluatedPropertyId?: string\n\n @Field(type => Float, { nullable: true, description: 'lower limit' })\n minimum?: number\n\n @Field(type => Float, { nullable: true, description: 'upper limit' })\n maximum?: number\n\n @Field({ nullable: true, description: 'unit of measure — required whenever a limit is given' })\n uom?: string\n\n @Field({ nullable: true, description: \"the site's own notation — never parsed\" })\n expression?: string\n\n /**\n * 화면이 아직 안 그리는 표준 칸들.\n *\n * **버리지 않는다.** 편 것을 다시 담을 때 이 칸들이 사라지면 사람이 적은 것이 조용히 없어진다.\n */\n @Field(type => ScalarObject, { nullable: true, description: 'standard fields this screen does not draw yet' })\n keptFields?: Record<string, unknown>\n}\n\n@ObjectType({ description: 'A page of test specifications' })\nexport class TestSpecificationList {\n @Field(type => [TestSpecification])\n items: TestSpecification[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(TestSpecification)\nexport class TestSpecificationResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => TestSpecificationList, { description: 'To fetch test specifications' })\n async testSpecifications(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<TestSpecificationList> {\n const { domain } = context.state\n const { items, total } = await listMaster('testSpecification', params, domain)\n\n return { items: items as TestSpecification[], total }\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 => [TestSpecification], { description: 'To create, change or retire test specifications' })\n async updateMultipleTestSpecification(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecification[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('testSpecification')\n\n const prepared = patches.map(patch => prepareCriteria(patch))\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, prepared, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as TestSpecification[]\n }\n /**\n * 명세 하나의 기준을 **행으로** 낸다.\n *\n * 화면이 JSON 을 풀지 않게 하는 자리다. 명세를 못 찾으면 빈 목록을 주지 않고 말한다 — 빈 목록은\n * 「기준이 없는 명세」와 같아 보이고, 그러면 사람이 기준을 다시 적는다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [TestSpecificationCriterionRow], { description: 'Criteria of one test specification, as editable rows' })\n async testSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n /* 조회에는 `@transaction` 이 없다 — `context.state.tx` 는 그 지시자가 있을 때만 있다. */\n const { domain } = context.state\n\n const spec = await getRepository(TestSpecification).findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!spec) {\n throw new Error(`검사 명세 \"${name}\" 를 찾지 못했습니다`)\n }\n\n return toCriterionRows(spec.criteria ?? []) as TestSpecificationCriterionRow[]\n }\n\n /**\n * 기준을 **바뀐 줄만** 받아 얹는다.\n *\n * 목록 전체를 받으면 두 사람이 같은 명세를 열어 각자 한 줄씩 고쳤을 때 나중에 저장한 쪽이 앞사람의\n * 줄을 지운다. 판단은 `criteria-rows.ts` 와 `criteria-check.ts` 가 든다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecificationCriterionRow], { description: 'To add, change or remove criteria of one test specification' })\n async updateTestSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Arg('patches', type => [ScalarObject], { description: 'changed rows only, each with cuFlag and rowIndex' })\n patches: CriterionPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(TestSpecification)\n\n const spec = await repository.findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!spec) {\n throw new Error(`검사 명세 \"${name}\" 를 찾지 못했습니다`)\n }\n\n const applied = applyCriterionPatches(spec.criteria ?? [], patches)\n\n if (applied.applied === 'rejected') {\n /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */\n throw new Error(\n `검사 명세 \"${name}\" 의 기준을 저장하지 않았습니다.\\n` + applied.problems.map(one => `· ${one}`).join('\\n')\n )\n }\n\n await repository.save({ ...spec, criteria: applied.criteria, updater: user } as any)\n\n return toCriterionRows(applied.criteria) as TestSpecificationCriterionRow[]\n }\n}\n"]}
1
+ {"version":3,"file":"test-spec-master-resolver.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Args, Arg, Ctx, Directive, Float, Int, Mutation, ObjectType, Field, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { ListParam, ScalarObject, getRepository, SpeakRefusals } from '@things-factory/shell'\n\nimport type { MasterPatch } from '../master-crud'\nimport { CriterionPatch, toCriterionRows } from './criteria-rows'\nimport { TestSpecification } from './test-specification'\nimport { effectiveTestSpecification, TestSpecificationRefusal } from './test-spec-revision-service'\nimport { listEffectiveTestSpecifications } from './test-spec-effective-list'\n\n/** Reads project the effective revision. Changes go through the revision draft resolver. */\n\n/**\n * 기준 한 줄 — **화면이 받는 모양.**\n *\n * 한계를 펴서 낸다. 화면은 이름 · 설명 · 하한 · 상한 · 단위 · 표현식이 각자 칸인 표를 그리고,\n * JSON 을 아예 모른다. 옮김은 `criteria-rows.ts` 한 곳이 든다 — 저장이 표로 내려가도 그 파일만 바뀐다.\n */\n@ObjectType({ description: 'One test specification criterion, flattened for editing' })\nexport class TestSpecificationCriterionRow {\n @Field({ description: 'criterion id — measurements find their criterion by this' })\n id: string\n\n @Field({ nullable: true, description: 'what is being checked' })\n description?: string\n\n @Field({ nullable: true, description: 'ISA-95 EvaluatedPropertyID — which property is measured' })\n evaluatedPropertyId?: string\n\n @Field(type => Float, { nullable: true, description: 'lower limit' })\n minimum?: number\n\n @Field(type => Float, { nullable: true, description: 'upper limit' })\n maximum?: number\n\n @Field({ nullable: true, description: 'unit of measure — required whenever a limit is given' })\n uom?: string\n\n @Field({ nullable: true, description: \"the site's own notation — never parsed\" })\n expression?: string\n\n /**\n * 화면이 아직 안 그리는 표준 칸들.\n *\n * **버리지 않는다.** 편 것을 다시 담을 때 이 칸들이 사라지면 사람이 적은 것이 조용히 없어진다.\n */\n @Field(type => ScalarObject, { nullable: true, description: 'standard fields this screen does not draw yet' })\n keptFields?: Record<string, unknown>\n}\n\n@ObjectType({ description: 'A page of test specifications' })\nexport class TestSpecificationList {\n @Field(type => [TestSpecification])\n items: TestSpecification[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(TestSpecification)\n@UseMiddleware(SpeakRefusals)\nexport class TestSpecificationResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => TestSpecificationList, { description: 'To fetch test specifications' })\n async testSpecifications(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<TestSpecificationList> {\n const { domain } = context.state\n return listEffectiveTestSpecifications(getRepository(TestSpecification).manager, domain, params)\n }\n\n /** Explicitly closed old write door: even non-regulated publication must retain a revision. */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecification], { description: 'To create, change or retire test specifications' })\n async updateMultipleTestSpecification(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecification[]> {\n throw new TestSpecificationRefusal('REVISION_REQUIRED', 'Use a test specification draft; direct identity-row changes are not supported.')\n }\n /**\n * 명세 하나의 기준을 **행으로** 낸다.\n *\n * 화면이 JSON 을 풀지 않게 하는 자리다. 명세를 못 찾으면 빈 목록을 주지 않고 말한다 — 빈 목록은\n * 「기준이 없는 명세」와 같아 보이고, 그러면 사람이 기준을 다시 적는다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [TestSpecificationCriterionRow], { description: 'Criteria of one test specification, as editable rows' })\n async testSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n /* 조회에는 `@transaction` 이 없다 — `context.state.tx` 는 그 지시자가 있을 때만 있다. */\n const { domain } = context.state\n\n const spec = await getRepository(TestSpecification).findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!spec) {\n throw new Error(`검사 명세 \"${name}\" 를 찾지 못했습니다`)\n }\n\n const effective = await effectiveTestSpecification(getRepository(TestSpecification).manager, domain.id, spec)\n return toCriterionRows(effective.criteria ?? []) as TestSpecificationCriterionRow[]\n }\n\n /** Criteria belong to a draft, not to the stable identity row. */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecificationCriterionRow], { description: 'To add, change or remove criteria of one test specification' })\n async updateTestSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Arg('patches', type => [ScalarObject], { description: 'changed rows only, each with cuFlag and rowIndex' })\n patches: CriterionPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n throw new TestSpecificationRefusal('REVISION_REQUIRED', 'Edit the draft criteria, then submit the frozen revision for approval.')\n }\n}\n"]}
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TestSpecificationQuery = exports.TestSpecificationMaster = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
6
7
  const master_page_query_1 = require("../master-page-query");
7
8
  const test_spec_master_1 = require("./test-spec-master");
8
9
  const test_specification_1 = require("./test-specification");
10
+ const test_spec_effective_list_1 = require("./test-spec-effective-list");
9
11
  let TestSpecificationMaster = class TestSpecificationMaster {
10
12
  };
11
13
  exports.TestSpecificationMaster = TestSpecificationMaster;
@@ -32,8 +34,12 @@ let TestSpecificationQuery = class TestSpecificationQuery {
32
34
  const page = await (0, master_page_query_1.pullPage)(test_specification_1.TestSpecification, domain, request);
33
35
  /* 통째로 달라고 했는데 한도를 넘으면 **자르지 않고 거절한다**(§`master-page`). */
34
36
  (0, master_page_query_1.refuseIfTooLarge)('검사 명세', request, page);
37
+ const manager = (0, shell_1.getRepository)(test_specification_1.TestSpecification).manager;
38
+ const effective = page.rows.length ? (await (0, test_spec_effective_list_1.listEffectiveTestSpecifications)(manager, domain, {
39
+ filters: [{ name: 'id', operator: 'in', value: page.rows.map(row => row.id) }], sortings: [{ name: 'name', desc: false }]
40
+ })).items.filter(row => row.effectiveStart != null) : [];
35
41
  return {
36
- testSpecifications: JSON.stringify((0, test_spec_master_1.testSpecificationMaster)(page.rows).testSpecifications),
42
+ testSpecifications: JSON.stringify((0, test_spec_master_1.testSpecificationMaster)(effective).testSpecifications),
37
43
  hasMore: page.hasMore,
38
44
  ...(page.next ? { next: page.next } : {})
39
45
  };