@things-factory/ops-master 10.0.19 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/bootstrap.d.ts +18 -0
- package/dist-client/bootstrap.js +36 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/index.d.ts +15 -1
- package/dist-client/index.js +18 -1
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/master/equipment-class.d.ts +12 -0
- package/dist-client/pages/master/equipment-class.js +42 -0
- package/dist-client/pages/master/equipment-class.js.map +1 -0
- package/dist-client/pages/master/equipment.d.ts +12 -0
- package/dist-client/pages/master/equipment.js +57 -0
- package/dist-client/pages/master/equipment.js.map +1 -0
- package/dist-client/pages/master/grist-iso-duration.d.ts +20 -0
- package/dist-client/pages/master/grist-iso-duration.js +70 -0
- package/dist-client/pages/master/grist-iso-duration.js.map +1 -0
- package/dist-client/pages/master/grist-reference.d.ts +91 -0
- package/dist-client/pages/master/grist-reference.js +419 -0
- package/dist-client/pages/master/grist-reference.js.map +1 -0
- package/dist-client/pages/master/import-rows.d.ts +35 -0
- package/dist-client/pages/master/import-rows.js +66 -0
- package/dist-client/pages/master/import-rows.js.map +1 -0
- package/dist-client/pages/master/iso-duration.d.ts +34 -0
- package/dist-client/pages/master/iso-duration.js +76 -0
- package/dist-client/pages/master/iso-duration.js.map +1 -0
- package/dist-client/pages/master/master-options.d.ts +33 -0
- package/dist-client/pages/master/master-options.js +47 -0
- package/dist-client/pages/master/master-options.js.map +1 -0
- package/dist-client/pages/master/master-page-base.d.ts +357 -0
- package/dist-client/pages/master/master-page-base.js +626 -0
- package/dist-client/pages/master/master-page-base.js.map +1 -0
- package/dist-client/pages/master/material-class.d.ts +12 -0
- package/dist-client/pages/master/material-class.js +42 -0
- package/dist-client/pages/master/material-class.js.map +1 -0
- package/dist-client/pages/master/material-definition.d.ts +12 -0
- package/dist-client/pages/master/material-definition.js +62 -0
- package/dist-client/pages/master/material-definition.js.map +1 -0
- package/dist-client/pages/master/operation-definition.d.ts +12 -0
- package/dist-client/pages/master/operation-definition.js +49 -0
- package/dist-client/pages/master/operation-definition.js.map +1 -0
- package/dist-client/pages/master/operational-location.d.ts +84 -0
- package/dist-client/pages/master/operational-location.js +426 -0
- package/dist-client/pages/master/operational-location.js.map +1 -0
- package/dist-client/pages/master/person.d.ts +12 -0
- package/dist-client/pages/master/person.js +41 -0
- package/dist-client/pages/master/person.js.map +1 -0
- package/dist-client/pages/master/personnel-class.d.ts +12 -0
- package/dist-client/pages/master/personnel-class.js +43 -0
- package/dist-client/pages/master/personnel-class.js.map +1 -0
- package/dist-client/pages/master/physical-asset-class.d.ts +12 -0
- package/dist-client/pages/master/physical-asset-class.js +40 -0
- package/dist-client/pages/master/physical-asset-class.js.map +1 -0
- package/dist-client/pages/master/physical-asset.d.ts +12 -0
- package/dist-client/pages/master/physical-asset.js +41 -0
- package/dist-client/pages/master/physical-asset.js.map +1 -0
- package/dist-client/pages/master/recipe-definition.d.ts +136 -0
- package/dist-client/pages/master/recipe-definition.js +404 -0
- package/dist-client/pages/master/recipe-definition.js.map +1 -0
- package/dist-client/pages/master/reference-targets.d.ts +42 -0
- package/dist-client/pages/master/reference-targets.js +82 -0
- package/dist-client/pages/master/reference-targets.js.map +1 -0
- package/dist-client/pages/master/route-definition.d.ts +19 -0
- package/dist-client/pages/master/route-definition.js +40 -0
- package/dist-client/pages/master/route-definition.js.map +1 -0
- package/dist-client/pages/master/stop-reason.d.ts +12 -0
- package/dist-client/pages/master/stop-reason.js +48 -0
- package/dist-client/pages/master/stop-reason.js.map +1 -0
- package/dist-client/pages/master/test-specification.d.ts +93 -0
- package/dist-client/pages/master/test-specification.js +449 -0
- package/dist-client/pages/master/test-specification.js.map +1 -0
- package/dist-client/pages/master-axes.d.ts +20 -29
- package/dist-client/pages/master-axes.js +32 -182
- package/dist-client/pages/master-axes.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +76 -3
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/asset/asset-lifecycle.d.ts +30 -7
- package/dist-server/service/asset/asset-lifecycle.js +33 -6
- package/dist-server/service/asset/asset-lifecycle.js.map +1 -1
- package/dist-server/service/asset/asset-master-resolver.d.ts +9 -0
- package/dist-server/service/asset/asset-master-resolver.js +41 -0
- package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
- package/dist-server/service/asset/asset-master.js +1 -1
- package/dist-server/service/asset/asset-master.js.map +1 -1
- package/dist-server/service/asset/asset-query.d.ts +9 -1
- package/dist-server/service/asset/asset-query.js +28 -6
- package/dist-server/service/asset/asset-query.js.map +1 -1
- package/dist-server/service/asset/physical-asset-class.js +1 -1
- package/dist-server/service/asset/physical-asset-class.js.map +1 -1
- package/dist-server/service/asset/physical-asset.d.ts +17 -4
- package/dist-server/service/asset/physical-asset.js +6 -6
- package/dist-server/service/asset/physical-asset.js.map +1 -1
- package/dist-server/service/equipment/equipment-class.js +1 -1
- package/dist-server/service/equipment/equipment-class.js.map +1 -1
- package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -0
- package/dist-server/service/equipment/equipment-master-resolver.js +58 -0
- package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
- package/dist-server/service/equipment/equipment-master.d.ts +3 -2
- package/dist-server/service/equipment/equipment-master.js +21 -4
- package/dist-server/service/equipment/equipment-master.js.map +1 -1
- package/dist-server/service/equipment/equipment-query.d.ts +9 -1
- package/dist-server/service/equipment/equipment-query.js +28 -6
- package/dist-server/service/equipment/equipment-query.js.map +1 -1
- package/dist-server/service/equipment/equipment.d.ts +27 -1
- package/dist-server/service/equipment/equipment.js +8 -3
- package/dist-server/service/equipment/equipment.js.map +1 -1
- package/dist-server/service/equipment/state-period-rules.d.ts +19 -0
- package/dist-server/service/equipment/state-period-rules.js +35 -1
- package/dist-server/service/equipment/state-period-rules.js.map +1 -1
- package/dist-server/service/index.d.ts +4 -1
- package/dist-server/service/index.js +11 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/location/location-master-resolver.d.ts +29 -0
- package/dist-server/service/location/location-master-resolver.js +228 -1
- package/dist-server/service/location/location-master-resolver.js.map +1 -1
- package/dist-server/service/location/location-query.d.ts +9 -1
- package/dist-server/service/location/location-query.js +24 -9
- package/dist-server/service/location/location-query.js.map +1 -1
- package/dist-server/service/location/operational-location.d.ts +37 -0
- package/dist-server/service/location/operational-location.js +22 -2
- package/dist-server/service/location/operational-location.js.map +1 -1
- package/dist-server/service/master-axis.js +2 -2
- package/dist-server/service/master-axis.js.map +1 -1
- package/dist-server/service/master-conformance-resolver.d.ts +34 -0
- package/dist-server/service/master-conformance-resolver.js +143 -0
- package/dist-server/service/master-conformance-resolver.js.map +1 -0
- package/dist-server/service/master-conformance.d.ts +80 -0
- package/dist-server/service/master-conformance.js +266 -0
- package/dist-server/service/master-conformance.js.map +1 -0
- package/dist-server/service/master-page-query.d.ts +33 -0
- package/dist-server/service/master-page-query.js +55 -0
- package/dist-server/service/master-page-query.js.map +1 -0
- package/dist-server/service/master-page.d.ts +85 -0
- package/dist-server/service/master-page.js +104 -0
- package/dist-server/service/master-page.js.map +1 -0
- package/dist-server/service/material/material-class.js +1 -1
- package/dist-server/service/material/material-class.js.map +1 -1
- package/dist-server/service/material/material-definition.js +2 -2
- package/dist-server/service/material/material-definition.js.map +1 -1
- package/dist-server/service/material/material-master-resolver.d.ts +62 -0
- package/dist-server/service/material/material-master-resolver.js +213 -0
- package/dist-server/service/material/material-master-resolver.js.map +1 -1
- package/dist-server/service/material/material-query.d.ts +9 -1
- package/dist-server/service/material/material-query.js +28 -6
- package/dist-server/service/material/material-query.js.map +1 -1
- package/dist-server/service/material/recipe-index.d.ts +23 -0
- package/dist-server/service/material/recipe-index.js +44 -0
- package/dist-server/service/material/recipe-index.js.map +1 -0
- package/dist-server/service/operation/operation-definition.d.ts +1 -1
- package/dist-server/service/operation/operation-definition.js +4 -4
- package/dist-server/service/operation/operation-definition.js.map +1 -1
- package/dist-server/service/operation/operation-master-resolver.d.ts +24 -0
- package/dist-server/service/operation/operation-master-resolver.js +107 -0
- package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
- package/dist-server/service/operation/operation-query.d.ts +10 -2
- package/dist-server/service/operation/operation-query.js +25 -10
- package/dist-server/service/operation/operation-query.js.map +1 -1
- package/dist-server/service/personnel/person.js +3 -3
- package/dist-server/service/personnel/person.js.map +1 -1
- package/dist-server/service/personnel/personnel-class.d.ts +1 -1
- package/dist-server/service/personnel/personnel-class.js +2 -2
- package/dist-server/service/personnel/personnel-class.js.map +1 -1
- package/dist-server/service/personnel/personnel-master-resolver.d.ts +9 -0
- package/dist-server/service/personnel/personnel-master-resolver.js +43 -0
- package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
- package/dist-server/service/personnel/personnel-master.js +1 -1
- package/dist-server/service/personnel/personnel-master.js.map +1 -1
- package/dist-server/service/personnel/personnel-query.d.ts +9 -1
- package/dist-server/service/personnel/personnel-query.js +28 -6
- package/dist-server/service/personnel/personnel-query.js.map +1 -1
- package/dist-server/service/recipe/recipe-definition.d.ts +3 -3
- package/dist-server/service/recipe/recipe-definition.js +3 -3
- package/dist-server/service/recipe/recipe-definition.js.map +1 -1
- package/dist-server/service/recipe/recipe-master-resolver.d.ts +1 -1
- package/dist-server/service/recipe/recipe-master-resolver.js +1 -1
- package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -1
- package/dist-server/service/recipe/recipe-master.d.ts +1 -1
- package/dist-server/service/recipe/recipe-master.js +2 -2
- package/dist-server/service/recipe/recipe-master.js.map +1 -1
- package/dist-server/service/recipe/recipe-query.d.ts +11 -3
- package/dist-server/service/recipe/recipe-query.js +30 -9
- package/dist-server/service/recipe/recipe-query.js.map +1 -1
- package/dist-server/service/recipe/route-definition.d.ts +6 -6
- package/dist-server/service/recipe/route-definition.js +6 -6
- package/dist-server/service/recipe/route-definition.js.map +1 -1
- package/dist-server/service/resource/class-lineage.d.ts +46 -0
- package/dist-server/service/resource/class-lineage.js +80 -0
- package/dist-server/service/resource/class-lineage.js.map +1 -0
- package/dist-server/service/resource/class-members.d.ts +36 -0
- package/dist-server/service/resource/class-members.js +50 -0
- package/dist-server/service/resource/class-members.js.map +1 -0
- package/dist-server/service/resource/referrers.d.ts +54 -0
- package/dist-server/service/resource/referrers.js +99 -0
- package/dist-server/service/resource/referrers.js.map +1 -0
- package/dist-server/service/test-spec/criteria-check.d.ts +53 -0
- package/dist-server/service/test-spec/criteria-check.js +142 -0
- package/dist-server/service/test-spec/criteria-check.js.map +1 -0
- package/dist-server/service/test-spec/criteria-rows.d.ts +89 -0
- package/dist-server/service/test-spec/criteria-rows.js +139 -0
- package/dist-server/service/test-spec/criteria-rows.js.map +1 -0
- package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +33 -7
- package/dist-server/service/test-spec/test-spec-master-resolver.js +137 -2
- package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-query.d.ts +9 -1
- package/dist-server/service/test-spec/test-spec-query.js +23 -9
- package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
- package/dist-server/service/test-spec/test-specification.js +1 -1
- package/dist-server/service/test-spec/test-specification.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/helps/ops-master/equipment-class.ko.md +11 -0
- package/helps/ops-master/equipment-class.md +11 -0
- package/helps/ops-master/equipment.ko.md +11 -0
- package/helps/ops-master/equipment.md +11 -0
- package/helps/ops-master/material-class.ko.md +11 -0
- package/helps/ops-master/material-class.md +11 -0
- package/helps/ops-master/material-definition.ko.md +11 -0
- package/helps/ops-master/material-definition.md +11 -0
- package/helps/ops-master/operation-definition.ko.md +11 -0
- package/helps/ops-master/operation-definition.md +11 -0
- package/helps/ops-master/operational-location.ko.md +11 -0
- package/helps/ops-master/operational-location.md +11 -0
- package/helps/ops-master/person.ko.md +11 -0
- package/helps/ops-master/person.md +11 -0
- package/helps/ops-master/personnel-class.ko.md +11 -0
- package/helps/ops-master/personnel-class.md +11 -0
- package/helps/ops-master/physical-asset-class.ko.md +11 -0
- package/helps/ops-master/physical-asset-class.md +11 -0
- package/helps/ops-master/physical-asset.ko.md +11 -0
- package/helps/ops-master/physical-asset.md +11 -0
- package/helps/ops-master/recipe-definition.ko.md +11 -0
- package/helps/ops-master/recipe-definition.md +11 -0
- package/helps/ops-master/route-definition.ko.md +11 -0
- package/helps/ops-master/route-definition.md +11 -0
- package/helps/ops-master/stop-reason.ko.md +11 -0
- package/helps/ops-master/stop-reason.md +11 -0
- package/helps/ops-master/test-specification.ko.md +11 -0
- package/helps/ops-master/test-specification.md +11 -0
- package/package.json +5 -5
- package/things-factory.config.js +22 -2
- package/translations/en.json +86 -3
- package/translations/ja.json +92 -1
- package/translations/ko.json +93 -10
- package/translations/ms.json +92 -1
- package/translations/zh.json +92 -1
- package/dist-client/pages/master-list.d.ts +0 -68
- package/dist-client/pages/master-list.js +0 -239
- package/dist-client/pages/master-list.js.map +0 -1
|
@@ -31,8 +31,45 @@ export declare class OperationalLocation {
|
|
|
31
31
|
parallelism?: number;
|
|
32
32
|
/** 상위 자리 — 표준 `OperationalLocationChild`(재귀)의 반대 방향. */
|
|
33
33
|
parentId?: string;
|
|
34
|
+
/**
|
|
35
|
+
* 바로 아래에 몇이 있나. **저장하지 않는다** — 목록 리졸버가 채운다(`attachChildCounts`).
|
|
36
|
+
*
|
|
37
|
+
* 계층을 한 단씩 걷는 화면이 이 값으로 「그 줄에 들어갈 것이 있나」를 말한다. 세어서 넣는 값이라
|
|
38
|
+
* 컬럼이 아니다 — 컬럼으로 두면 자식이 생기고 없어질 때마다 이 줄을 함께 고쳐야 하고, 한 번 빠뜨리면
|
|
39
|
+
* 틀린 개수가 남는다.
|
|
40
|
+
*/
|
|
41
|
+
childCount?: number;
|
|
34
42
|
/** 자리 속성 — 표준 `OperationalLocationProperty`. */
|
|
35
43
|
properties?: Record<string, unknown>[];
|
|
44
|
+
/**
|
|
45
|
+
* 이 자리에서 도는 **근무조 이름들** — 아래 단이 물려받는다.
|
|
46
|
+
*
|
|
47
|
+
* ── 왜 자리가 드나 (2026-09-07) ─────────────────────────────────────────
|
|
48
|
+
* 근무조는 지금까지 도메인 단위 선언이고 **어디에 걸리는지가 없었다.** 그래서 씨앗이 설비마다
|
|
49
|
+
* 사본을 뿌렸고(`Equipment.workCalendar`), 한 공장 22대가 같은 값을 22벌 들고 있었다. 3교대를
|
|
50
|
+
* 2교대로 바꾸면 22줄을 고쳐야 하고, 한 줄을 빠뜨리면 그 설비만 조용히 옛 근무로 계산된다.
|
|
51
|
+
*
|
|
52
|
+
* 근무조는 **사람의 일정**이다. 사람은 기계 한 대가 아니라 라인·셀에 배치된다. 그래서 자리가
|
|
53
|
+
* 들고 아래가 물려받는다 — 공장에 한 줄 적으면 그 아래 전부가 그것으로 돈다.
|
|
54
|
+
*
|
|
55
|
+
* ── 비어 있는 것이 정상이다 ───────────────────────────────────────────────
|
|
56
|
+
* 채우는 것은 **위와 다를 때뿐**이다. 라인 하나만 주간이면 그 라인에만 적는다. 아무 데도 안 적으면
|
|
57
|
+
* 도메인의 근무조 전부로 떨어져 지금 거동 그대로다 — 이 칸이 생겼다고 기존 공장이 바뀌지 않는다.
|
|
58
|
+
*
|
|
59
|
+
* ── 설비에는 적지 않는다 ──────────────────────────────────────────────────
|
|
60
|
+
* 「이 기계만 24시간 돈다」는 근무조가 다른 것이 아니라 **자기 가동 계획이 다른 것**이다(에이징
|
|
61
|
+
* 로가 그렇다). 그 사실은 `Equipment.workCalendar` — 이 설비가 가동하도록 계획된 시간 — 에 적힌다.
|
|
62
|
+
* 근무조로 적으면 아무도 근무하지 않는 시간이 근무 시간으로 기록된다.
|
|
63
|
+
*
|
|
64
|
+
* ── 거는 단은 work center 급 이상이다 — 규칙이지 제약이 아니다 ────────────
|
|
65
|
+
* ISA-95 Part 1 이 일정과 능력을 work center 에 둔다. 사람은 라인에 배치되지 기계 한 대에
|
|
66
|
+
* 배치되지 않으므로, `Site` · `Area` · `ProductionLine` · `WorkCenter` 가 이 칸을 든다.
|
|
67
|
+
*
|
|
68
|
+
* **거절하지는 않는다.** 아래 단에 적어도 상속이 그것을 먼저 만나 그냥 돈다. 셀 단위 근무가
|
|
69
|
+
* 실제로 있는 현장에서 저장을 막으면 그 현장은 모델링 자체가 안 된다 — 적합성 표에 줄 하나가
|
|
70
|
+
* 서는 것보다 나쁘다. 어긋난 것은 성숙도가 이름과 함께 낸다.
|
|
71
|
+
*/
|
|
72
|
+
workShiftNames?: string[];
|
|
36
73
|
/** 이 자리에 걸리는 `TestSpecification` 참조들 — 구역 온도 기준 같은 것이 여기 붙는다. */
|
|
37
74
|
testSpecificationIds?: string[];
|
|
38
75
|
/** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */
|
|
@@ -68,11 +68,23 @@ tslib_1.__decorate([
|
|
|
68
68
|
(0, type_graphql_1.Field)({ nullable: true }),
|
|
69
69
|
tslib_1.__metadata("design:type", String)
|
|
70
70
|
], OperationalLocation.prototype, "parentId", void 0);
|
|
71
|
+
tslib_1.__decorate([
|
|
72
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'How many locations sit directly under this one' }),
|
|
73
|
+
tslib_1.__metadata("design:type", Number)
|
|
74
|
+
], OperationalLocation.prototype, "childCount", void 0);
|
|
71
75
|
tslib_1.__decorate([
|
|
72
76
|
(0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
|
|
73
|
-
(0, type_graphql_1.Field)(type =>
|
|
77
|
+
(0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true, description: 'ResourceProperty[]' }),
|
|
74
78
|
tslib_1.__metadata("design:type", Array)
|
|
75
79
|
], OperationalLocation.prototype, "properties", void 0);
|
|
80
|
+
tslib_1.__decorate([
|
|
81
|
+
(0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
|
|
82
|
+
(0, type_graphql_1.Field)(type => [String], {
|
|
83
|
+
nullable: true,
|
|
84
|
+
description: 'work shift names that run here — locations below inherit this unless they say their own'
|
|
85
|
+
}),
|
|
86
|
+
tslib_1.__metadata("design:type", Array)
|
|
87
|
+
], OperationalLocation.prototype, "workShiftNames", void 0);
|
|
76
88
|
tslib_1.__decorate([
|
|
77
89
|
(0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
|
|
78
90
|
(0, type_graphql_1.Field)(type => [String], { nullable: true }),
|
|
@@ -126,7 +138,15 @@ exports.OperationalLocation = OperationalLocation = tslib_1.__decorate([
|
|
|
126
138
|
(0, typeorm_1.Entity)({ name: 'ops_operational_location' }),
|
|
127
139
|
(0, typeorm_1.Index)('ix_ops_operational_location_0', (location) => [location.domain, location.name], {
|
|
128
140
|
unique: true
|
|
129
|
-
})
|
|
141
|
+
})
|
|
142
|
+
/*
|
|
143
|
+
* 상위로 찾는 색인.
|
|
144
|
+
*
|
|
145
|
+
* 화면이 계층을 한 단씩 걷는다 — 「이 자리의 자식」을 부를 때마다 이 칸으로 거른다. 그리고 줄마다 하위
|
|
146
|
+
* 개수를 세므로(`childCount`), 색인이 없으면 한 화면에 표 전체 훑기가 스무 번 일어난다.
|
|
147
|
+
*/
|
|
148
|
+
,
|
|
149
|
+
(0, typeorm_1.Index)('ix_ops_operational_location_1', (location) => [location.domain, location.parentId]),
|
|
130
150
|
(0, type_graphql_1.ObjectType)({ description: 'ISA-95 OperationalLocation — where resources are placed' })
|
|
131
151
|
], OperationalLocation);
|
|
132
152
|
//# sourceMappingURL=operational-location.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operational-location.js","sourceRoot":"","sources":["../../../server/service/location/operational-location.ts"],"names":[],"mappings":";;;;AAAA,qCASgB;AAChB,+CAAyD;AAEzD,
|
|
1
|
+
{"version":3,"file":"operational-location.js","sourceRoot":"","sources":["../../../server/service/location/operational-location.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * 자리 — **ISA-95 `OperationalLocation`**.\n *\n * 표준 원문은 「자원이 놓이거나 놓일 것으로 예상되는 논리적·물리적 장소」다. 자기 계층을 재귀로 갖고,\n * 설비는 자기 자리를 이것으로 가리킨다. 자리와 설비를 나눠 두는 것은 표준의 구조다.\n *\n * ── 용량을 지어내지 않는다 ──────────────────────────────────────────────────\n * `capacity` 는 그 자리에 몇이 들어가는가다. 모르면 비워 둔다. 실측에서 임의로 1 을 넣었더니 물품\n * 하나에 포화 경고가 났다 — 없는 것보다 나쁜 수다.\n */\n@Entity({ name: 'ops_operational_location' })\n@Index('ix_ops_operational_location_0', (location: OperationalLocation) => [location.domain, location.name], {\n unique: true\n})\n/*\n * 상위로 찾는 색인.\n *\n * 화면이 계층을 한 단씩 걷는다 — 「이 자리의 자식」을 부를 때마다 이 칸으로 거른다. 그리고 줄마다 하위\n * 개수를 세므로(`childCount`), 색인이 없으면 한 화면에 표 전체 훑기가 스무 번 일어난다.\n */\n@Index('ix_ops_operational_location_1', (location: OperationalLocation) => [location.domain, location.parentId])\n@ObjectType({ description: 'ISA-95 OperationalLocation — where resources are placed' })\nexport class OperationalLocation {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain)\n domain?: Domain\n\n @RelationId((location: OperationalLocation) => location.domain)\n domainId?: string\n\n /** 표준 `ID` — 트윈으로 나갈 때 `locations[].id` 가 된다. */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /** 자리 타입 — 도메인 카탈로그가 정한 키(`raw-store` · `weld-station` 같은 것). */\n @Column()\n @Field()\n type: string\n\n /**\n * ISA-95 설비 계층 단 — `ProductionLine` · `WorkCell` · `StorageZone` · `ProcessCell` · `Unit` 등.\n *\n * 적지 않으면 타입으로 알아낸다. 적으면 그것이 기준이다 — 현장이 자기 계층을 더 잘 안다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n level?: string\n\n /** 이 자리에 몇이 들어가나. **모르면 비운다.** */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true })\n capacity?: number\n\n /** 동시에 몇을 처리하나(대기 이론의 서버 수). 표준에 없는 우리 축이다. */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true })\n parallelism?: number\n\n /** 상위 자리 — 표준 `OperationalLocationChild`(재귀)의 반대 방향. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n parentId?: string\n\n /**\n * 바로 아래에 몇이 있나. **저장하지 않는다** — 목록 리졸버가 채운다(`attachChildCounts`).\n *\n * 계층을 한 단씩 걷는 화면이 이 값으로 「그 줄에 들어갈 것이 있나」를 말한다. 세어서 넣는 값이라\n * 컬럼이 아니다 — 컬럼으로 두면 자식이 생기고 없어질 때마다 이 줄을 함께 고쳐야 하고, 한 번 빠뜨리면\n * 틀린 개수가 남는다.\n */\n @Field(type => Int, { nullable: true, description: 'How many locations sit directly under this one' })\n childCount?: number\n\n /** 자리 속성 — 표준 `OperationalLocationProperty`. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'ResourceProperty[]' })\n properties?: Record<string, unknown>[]\n\n /**\n * 이 자리에서 도는 **근무조 이름들** — 아래 단이 물려받는다.\n *\n * ── 왜 자리가 드나 (2026-09-07) ─────────────────────────────────────────\n * 근무조는 지금까지 도메인 단위 선언이고 **어디에 걸리는지가 없었다.** 그래서 씨앗이 설비마다\n * 사본을 뿌렸고(`Equipment.workCalendar`), 한 공장 22대가 같은 값을 22벌 들고 있었다. 3교대를\n * 2교대로 바꾸면 22줄을 고쳐야 하고, 한 줄을 빠뜨리면 그 설비만 조용히 옛 근무로 계산된다.\n *\n * 근무조는 **사람의 일정**이다. 사람은 기계 한 대가 아니라 라인·셀에 배치된다. 그래서 자리가\n * 들고 아래가 물려받는다 — 공장에 한 줄 적으면 그 아래 전부가 그것으로 돈다.\n *\n * ── 비어 있는 것이 정상이다 ───────────────────────────────────────────────\n * 채우는 것은 **위와 다를 때뿐**이다. 라인 하나만 주간이면 그 라인에만 적는다. 아무 데도 안 적으면\n * 도메인의 근무조 전부로 떨어져 지금 거동 그대로다 — 이 칸이 생겼다고 기존 공장이 바뀌지 않는다.\n *\n * ── 설비에는 적지 않는다 ──────────────────────────────────────────────────\n * 「이 기계만 24시간 돈다」는 근무조가 다른 것이 아니라 **자기 가동 계획이 다른 것**이다(에이징\n * 로가 그렇다). 그 사실은 `Equipment.workCalendar` — 이 설비가 가동하도록 계획된 시간 — 에 적힌다.\n * 근무조로 적으면 아무도 근무하지 않는 시간이 근무 시간으로 기록된다.\n *\n * ── 거는 단은 work center 급 이상이다 — 규칙이지 제약이 아니다 ────────────\n * ISA-95 Part 1 이 일정과 능력을 work center 에 둔다. 사람은 라인에 배치되지 기계 한 대에\n * 배치되지 않으므로, `Site` · `Area` · `ProductionLine` · `WorkCenter` 가 이 칸을 든다.\n *\n * **거절하지는 않는다.** 아래 단에 적어도 상속이 그것을 먼저 만나 그냥 돈다. 셀 단위 근무가\n * 실제로 있는 현장에서 저장을 막으면 그 현장은 모델링 자체가 안 된다 — 적합성 표에 줄 하나가\n * 서는 것보다 나쁘다. 어긋난 것은 성숙도가 이름과 함께 낸다.\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], {\n nullable: true,\n description: 'work shift names that run here — locations below inherit this unless they say their own'\n })\n workShiftNames?: string[]\n\n /** 이 자리에 걸리는 `TestSpecification` 참조들 — 구역 온도 기준 같은 것이 여기 붙는다. */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], { nullable: true })\n testSpecificationIds?: string[]\n\n /** 언제부터 유효한가 — 표준 `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /**\n * 언제까지 유효한가 — 표준 `EffectiveEndDate`.\n *\n * **없앨 때 행을 지우지 않고 이 칸을 채운다.** 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((location: OperationalLocation) => location.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((location: OperationalLocation) => location.updater)\n updaterId?: string\n}\n"]}
|
|
@@ -30,9 +30,9 @@ exports.MASTER_AXES = [
|
|
|
30
30
|
{ key: 'operationDefinition', entity: operation_definition_1.OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
|
|
31
31
|
{ key: 'testSpecification', entity: test_specification_1.TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true },
|
|
32
32
|
/*
|
|
33
|
-
*
|
|
33
|
+
* 라우팅과 레시피 (2026-08-31) — 커널에 `RouteDef`·`RecipeDef` 가 있고 `JobOrder.routeKey` 가 그것을
|
|
34
34
|
* 가리키는데 **그 값이 가리킬 표가 없었다.** 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은
|
|
35
|
-
* 작업지시에
|
|
35
|
+
* 작업지시에 라우팅을 자동으로 붙일 수 없었다.
|
|
36
36
|
*/
|
|
37
37
|
{ key: 'routeDefinition', entity: route_definition_1.RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
|
|
38
38
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-axis.js","sourceRoot":"","sources":["../../server/service/master-axis.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectLiteral } from 'typeorm'\n\nimport { Equipment } from './equipment/equipment'\nimport { EquipmentClass } from './equipment/equipment-class'\nimport { StopReason } from './equipment/stop-reason'\nimport { MaterialClass } from './material/material-class'\nimport { MaterialDefinition } from './material/material-definition'\nimport { OperationDefinition } from './operation/operation-definition'\nimport { OperationalLocation } from './location/operational-location'\nimport { Person } from './personnel/person'\nimport { PersonnelClass } from './personnel/personnel-class'\nimport { PhysicalAsset } from './asset/physical-asset'\nimport { PhysicalAssetClass } from './asset/physical-asset-class'\nimport { RecipeDefinition } from './recipe/recipe-definition'\nimport { RouteDefinition } from './recipe/route-definition'\nimport { TestSpecification } from './test-spec/test-specification'\n\n/**\n * 마스터 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.\n *\n * ── 왜 표로 두나 ───────────────────────────────────────────────────────────\n * 축이 열둘인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열두 벌이 되고,\n * 규칙 하나를 고칠 때 열두 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데\n * 오류는 나지 않는다.\n *\n * 그래서 **다른 것만** 적는다 — 어느 표인가, 무엇으로 찾는가, 없앨 때 지우는가 끝을 채우는가.\n *\n * ── 왜 유효 기간 여부가 여기 있나 ──────────────────────────────────────────\n * 지난 기록이 가리키는 축은 지우면 안 된다. 그것이 축마다 다르므로 축의 성질로 적는다. 정지 사유는\n * 유효 기간이 있고, 등급도 있다. 지금 열둘 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이\n * 생기면 그때 이 표만 고치면 된다.\n */\n\nexport interface MasterAxis {\n /** GraphQL 이름의 바탕. `person` → `persons` · `updateMultiplePerson`. */\n key: string\n entity: { new (): ObjectLiteral }\n /** 검색어가 걸리는 칸들. */\n searchables: string[]\n /** 없앨 때 행을 지우지 않고 `effectiveEnd` 를 채우나. */\n hasEffectivePeriod: boolean\n}\n\nexport const MASTER_AXES: readonly MasterAxis[] = [\n { key: 'personnelClass', entity: PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'person', entity: Person, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipmentClass', entity: EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipment', entity: Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'stopReason', entity: StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAssetClass', entity: PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAsset', entity: PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'materialClass', entity: MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'materialDefinition', entity: MaterialDefinition, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'operationalLocation', entity: OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'operationDefinition', entity: OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'testSpecification', entity: TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true },\n /*\n *
|
|
1
|
+
{"version":3,"file":"master-axis.js","sourceRoot":"","sources":["../../server/service/master-axis.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectLiteral } from 'typeorm'\n\nimport { Equipment } from './equipment/equipment'\nimport { EquipmentClass } from './equipment/equipment-class'\nimport { StopReason } from './equipment/stop-reason'\nimport { MaterialClass } from './material/material-class'\nimport { MaterialDefinition } from './material/material-definition'\nimport { OperationDefinition } from './operation/operation-definition'\nimport { OperationalLocation } from './location/operational-location'\nimport { Person } from './personnel/person'\nimport { PersonnelClass } from './personnel/personnel-class'\nimport { PhysicalAsset } from './asset/physical-asset'\nimport { PhysicalAssetClass } from './asset/physical-asset-class'\nimport { RecipeDefinition } from './recipe/recipe-definition'\nimport { RouteDefinition } from './recipe/route-definition'\nimport { TestSpecification } from './test-spec/test-specification'\n\n/**\n * 마스터 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.\n *\n * ── 왜 표로 두나 ───────────────────────────────────────────────────────────\n * 축이 열둘인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열두 벌이 되고,\n * 규칙 하나를 고칠 때 열두 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데\n * 오류는 나지 않는다.\n *\n * 그래서 **다른 것만** 적는다 — 어느 표인가, 무엇으로 찾는가, 없앨 때 지우는가 끝을 채우는가.\n *\n * ── 왜 유효 기간 여부가 여기 있나 ──────────────────────────────────────────\n * 지난 기록이 가리키는 축은 지우면 안 된다. 그것이 축마다 다르므로 축의 성질로 적는다. 정지 사유는\n * 유효 기간이 있고, 등급도 있다. 지금 열둘 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이\n * 생기면 그때 이 표만 고치면 된다.\n */\n\nexport interface MasterAxis {\n /** GraphQL 이름의 바탕. `person` → `persons` · `updateMultiplePerson`. */\n key: string\n entity: { new (): ObjectLiteral }\n /** 검색어가 걸리는 칸들. */\n searchables: string[]\n /** 없앨 때 행을 지우지 않고 `effectiveEnd` 를 채우나. */\n hasEffectivePeriod: boolean\n}\n\nexport const MASTER_AXES: readonly MasterAxis[] = [\n { key: 'personnelClass', entity: PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'person', entity: Person, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipmentClass', entity: EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'equipment', entity: Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'stopReason', entity: StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAssetClass', entity: PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'physicalAsset', entity: PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'materialClass', entity: MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'materialDefinition', entity: MaterialDefinition, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },\n { key: 'operationalLocation', entity: OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'operationDefinition', entity: OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },\n { key: 'testSpecification', entity: TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true },\n /*\n * 라우팅과 레시피 (2026-08-31) — 커널에 `RouteDef`·`RecipeDef` 가 있고 `JobOrder.routeKey` 가 그것을\n * 가리키는데 **그 값이 가리킬 표가 없었다.** 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은\n * 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n */\n { key: 'routeDefinition', entity: RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },\n {\n key: 'recipeDefinition',\n entity: RecipeDefinition,\n /* 만드는 품목으로도 찾는다 — 「이 품목을 만드는 레시피」가 MES 가 지시마다 묻는 물음이다. */\n searchables: ['name', 'description', 'materialDefinitionName'],\n hasEffectivePeriod: true\n }\n]\n\nexport function masterAxisOf(key: string): MasterAxis {\n const axis = MASTER_AXES.find(candidate => candidate.key === key)\n\n if (!axis) {\n /* 없는 축을 조용히 지나치지 않는다 — 지나치면 그 축의 화면이 빈 목록을 보이고 아무도 모른다. */\n throw new Error(`masterAxisOf: unknown master axis \"${key}\"`)\n }\n\n return axis\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 마스터 정합성 — **사람이 눌러서 도는 검사.**
|
|
3
|
+
*
|
|
4
|
+
* 목록 화면이 매번 부르는 것이 아니다. 표를 통째로 읽어야 답이 나오고, 상한을 두면 「어긋남 0건」이
|
|
5
|
+
* 거짓이 되기 때문이다. 정합성은 「전부 봤나」가 곧 답이다.
|
|
6
|
+
*/
|
|
7
|
+
export declare class MasterFinding {
|
|
8
|
+
axis: string;
|
|
9
|
+
subject?: string;
|
|
10
|
+
kind: string;
|
|
11
|
+
field: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class MasterFindingCount {
|
|
15
|
+
axis: string;
|
|
16
|
+
kind: string;
|
|
17
|
+
count: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class MasterScanned {
|
|
20
|
+
axis: string;
|
|
21
|
+
rows: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class MasterConformance {
|
|
24
|
+
/**
|
|
25
|
+
* 무엇을 몇 개 봤나. **어긋남 0건이 「멀쩡하다」인지 「안 돌았다」인지는 이것으로만 갈린다.**
|
|
26
|
+
*/
|
|
27
|
+
scanned: MasterScanned[];
|
|
28
|
+
total: number;
|
|
29
|
+
counts: MasterFindingCount[];
|
|
30
|
+
findings: MasterFinding[];
|
|
31
|
+
}
|
|
32
|
+
export declare class MasterConformanceResolver {
|
|
33
|
+
masterConformance(context: ResolverContext): Promise<MasterConformance>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MasterConformanceResolver = exports.MasterConformance = exports.MasterScanned = exports.MasterFindingCount = exports.MasterFinding = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const shell_1 = require("@things-factory/shell");
|
|
7
|
+
const physical_asset_1 = require("./asset/physical-asset");
|
|
8
|
+
const physical_asset_class_1 = require("./asset/physical-asset-class");
|
|
9
|
+
const equipment_1 = require("./equipment/equipment");
|
|
10
|
+
const equipment_class_1 = require("./equipment/equipment-class");
|
|
11
|
+
const stop_reason_1 = require("./equipment/stop-reason");
|
|
12
|
+
const operational_location_1 = require("./location/operational-location");
|
|
13
|
+
const material_class_1 = require("./material/material-class");
|
|
14
|
+
const material_definition_1 = require("./material/material-definition");
|
|
15
|
+
const operation_definition_1 = require("./operation/operation-definition");
|
|
16
|
+
const person_1 = require("./personnel/person");
|
|
17
|
+
const personnel_class_1 = require("./personnel/personnel-class");
|
|
18
|
+
const recipe_definition_1 = require("./recipe/recipe-definition");
|
|
19
|
+
const route_definition_1 = require("./recipe/route-definition");
|
|
20
|
+
const test_specification_1 = require("./test-spec/test-specification");
|
|
21
|
+
const master_conformance_1 = require("./master-conformance");
|
|
22
|
+
/**
|
|
23
|
+
* 마스터 정합성 — **사람이 눌러서 도는 검사.**
|
|
24
|
+
*
|
|
25
|
+
* 목록 화면이 매번 부르는 것이 아니다. 표를 통째로 읽어야 답이 나오고, 상한을 두면 「어긋남 0건」이
|
|
26
|
+
* 거짓이 되기 때문이다. 정합성은 「전부 봤나」가 곧 답이다.
|
|
27
|
+
*/
|
|
28
|
+
let MasterFinding = class MasterFinding {
|
|
29
|
+
};
|
|
30
|
+
exports.MasterFinding = MasterFinding;
|
|
31
|
+
tslib_1.__decorate([
|
|
32
|
+
(0, type_graphql_1.Field)(),
|
|
33
|
+
tslib_1.__metadata("design:type", String)
|
|
34
|
+
], MasterFinding.prototype, "axis", void 0);
|
|
35
|
+
tslib_1.__decorate([
|
|
36
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
37
|
+
tslib_1.__metadata("design:type", String)
|
|
38
|
+
], MasterFinding.prototype, "subject", void 0);
|
|
39
|
+
tslib_1.__decorate([
|
|
40
|
+
(0, type_graphql_1.Field)(),
|
|
41
|
+
tslib_1.__metadata("design:type", String)
|
|
42
|
+
], MasterFinding.prototype, "kind", void 0);
|
|
43
|
+
tslib_1.__decorate([
|
|
44
|
+
(0, type_graphql_1.Field)(),
|
|
45
|
+
tslib_1.__metadata("design:type", String)
|
|
46
|
+
], MasterFinding.prototype, "field", void 0);
|
|
47
|
+
tslib_1.__decorate([
|
|
48
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
49
|
+
tslib_1.__metadata("design:type", String)
|
|
50
|
+
], MasterFinding.prototype, "value", void 0);
|
|
51
|
+
exports.MasterFinding = MasterFinding = tslib_1.__decorate([
|
|
52
|
+
(0, type_graphql_1.ObjectType)({ description: 'One master data conformance finding' })
|
|
53
|
+
], MasterFinding);
|
|
54
|
+
let MasterFindingCount = class MasterFindingCount {
|
|
55
|
+
};
|
|
56
|
+
exports.MasterFindingCount = MasterFindingCount;
|
|
57
|
+
tslib_1.__decorate([
|
|
58
|
+
(0, type_graphql_1.Field)(),
|
|
59
|
+
tslib_1.__metadata("design:type", String)
|
|
60
|
+
], MasterFindingCount.prototype, "axis", void 0);
|
|
61
|
+
tslib_1.__decorate([
|
|
62
|
+
(0, type_graphql_1.Field)(),
|
|
63
|
+
tslib_1.__metadata("design:type", String)
|
|
64
|
+
], MasterFindingCount.prototype, "kind", void 0);
|
|
65
|
+
tslib_1.__decorate([
|
|
66
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int),
|
|
67
|
+
tslib_1.__metadata("design:type", Number)
|
|
68
|
+
], MasterFindingCount.prototype, "count", void 0);
|
|
69
|
+
exports.MasterFindingCount = MasterFindingCount = tslib_1.__decorate([
|
|
70
|
+
(0, type_graphql_1.ObjectType)({ description: 'How many findings per axis and kind' })
|
|
71
|
+
], MasterFindingCount);
|
|
72
|
+
let MasterScanned = class MasterScanned {
|
|
73
|
+
};
|
|
74
|
+
exports.MasterScanned = MasterScanned;
|
|
75
|
+
tslib_1.__decorate([
|
|
76
|
+
(0, type_graphql_1.Field)(),
|
|
77
|
+
tslib_1.__metadata("design:type", String)
|
|
78
|
+
], MasterScanned.prototype, "axis", void 0);
|
|
79
|
+
tslib_1.__decorate([
|
|
80
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int),
|
|
81
|
+
tslib_1.__metadata("design:type", Number)
|
|
82
|
+
], MasterScanned.prototype, "rows", void 0);
|
|
83
|
+
exports.MasterScanned = MasterScanned = tslib_1.__decorate([
|
|
84
|
+
(0, type_graphql_1.ObjectType)({ description: 'How many rows were scanned per axis' })
|
|
85
|
+
], MasterScanned);
|
|
86
|
+
let MasterConformance = class MasterConformance {
|
|
87
|
+
};
|
|
88
|
+
exports.MasterConformance = MasterConformance;
|
|
89
|
+
tslib_1.__decorate([
|
|
90
|
+
(0, type_graphql_1.Field)(type => [MasterScanned]),
|
|
91
|
+
tslib_1.__metadata("design:type", Array)
|
|
92
|
+
], MasterConformance.prototype, "scanned", void 0);
|
|
93
|
+
tslib_1.__decorate([
|
|
94
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'How many findings in total' }),
|
|
95
|
+
tslib_1.__metadata("design:type", Number)
|
|
96
|
+
], MasterConformance.prototype, "total", void 0);
|
|
97
|
+
tslib_1.__decorate([
|
|
98
|
+
(0, type_graphql_1.Field)(type => [MasterFindingCount]),
|
|
99
|
+
tslib_1.__metadata("design:type", Array)
|
|
100
|
+
], MasterConformance.prototype, "counts", void 0);
|
|
101
|
+
tslib_1.__decorate([
|
|
102
|
+
(0, type_graphql_1.Field)(type => [MasterFinding]),
|
|
103
|
+
tslib_1.__metadata("design:type", Array)
|
|
104
|
+
], MasterConformance.prototype, "findings", void 0);
|
|
105
|
+
exports.MasterConformance = MasterConformance = tslib_1.__decorate([
|
|
106
|
+
(0, type_graphql_1.ObjectType)({ description: 'Master data conformance report' })
|
|
107
|
+
], MasterConformance);
|
|
108
|
+
let MasterConformanceResolver = class MasterConformanceResolver {
|
|
109
|
+
async masterConformance(context) {
|
|
110
|
+
const { domain } = context.state;
|
|
111
|
+
const snapshot = await (0, master_conformance_1.readMasterSnapshot)(domain, shell_1.getRepository, {
|
|
112
|
+
Person: person_1.Person,
|
|
113
|
+
PersonnelClass: personnel_class_1.PersonnelClass,
|
|
114
|
+
Equipment: equipment_1.Equipment,
|
|
115
|
+
EquipmentClass: equipment_class_1.EquipmentClass,
|
|
116
|
+
PhysicalAsset: physical_asset_1.PhysicalAsset,
|
|
117
|
+
PhysicalAssetClass: physical_asset_class_1.PhysicalAssetClass,
|
|
118
|
+
MaterialDefinition: material_definition_1.MaterialDefinition,
|
|
119
|
+
MaterialClass: material_class_1.MaterialClass,
|
|
120
|
+
OperationDefinition: operation_definition_1.OperationDefinition,
|
|
121
|
+
RouteDefinition: route_definition_1.RouteDefinition,
|
|
122
|
+
RecipeDefinition: recipe_definition_1.RecipeDefinition,
|
|
123
|
+
OperationalLocation: operational_location_1.OperationalLocation,
|
|
124
|
+
StopReason: stop_reason_1.StopReason,
|
|
125
|
+
TestSpecification: test_specification_1.TestSpecification
|
|
126
|
+
});
|
|
127
|
+
const findings = (0, master_conformance_1.checkMaster)(snapshot);
|
|
128
|
+
return { scanned: (0, master_conformance_1.scannedCounts)(snapshot), total: findings.length, counts: (0, master_conformance_1.summarize)(findings), findings };
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
exports.MasterConformanceResolver = MasterConformanceResolver;
|
|
132
|
+
tslib_1.__decorate([
|
|
133
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
|
|
134
|
+
(0, type_graphql_1.Query)(returns => MasterConformance, { description: 'Check master data for missing fields and dangling references' }),
|
|
135
|
+
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
136
|
+
tslib_1.__metadata("design:type", Function),
|
|
137
|
+
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
138
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
139
|
+
], MasterConformanceResolver.prototype, "masterConformance", null);
|
|
140
|
+
exports.MasterConformanceResolver = MasterConformanceResolver = tslib_1.__decorate([
|
|
141
|
+
(0, type_graphql_1.Resolver)()
|
|
142
|
+
], MasterConformanceResolver);
|
|
143
|
+
//# sourceMappingURL=master-conformance-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"master-conformance-resolver.js","sourceRoot":"","sources":["../../server/service/master-conformance-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Directive, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from './asset/physical-asset'\nimport { PhysicalAssetClass } from './asset/physical-asset-class'\nimport { Equipment } from './equipment/equipment'\nimport { EquipmentClass } from './equipment/equipment-class'\nimport { StopReason } from './equipment/stop-reason'\nimport { OperationalLocation } from './location/operational-location'\nimport { MaterialClass } from './material/material-class'\nimport { MaterialDefinition } from './material/material-definition'\nimport { OperationDefinition } from './operation/operation-definition'\nimport { Person } from './personnel/person'\nimport { PersonnelClass } from './personnel/personnel-class'\nimport { RecipeDefinition } from './recipe/recipe-definition'\nimport { RouteDefinition } from './recipe/route-definition'\nimport { TestSpecification } from './test-spec/test-specification'\nimport { checkMaster, readMasterSnapshot, scannedCounts, summarize } from './master-conformance'\n\n/**\n * 마스터 정합성 — **사람이 눌러서 도는 검사.**\n *\n * 목록 화면이 매번 부르는 것이 아니다. 표를 통째로 읽어야 답이 나오고, 상한을 두면 「어긋남 0건」이\n * 거짓이 되기 때문이다. 정합성은 「전부 봤나」가 곧 답이다.\n */\n\n@ObjectType({ description: 'One master data conformance finding' })\nexport class MasterFinding {\n @Field()\n axis: string\n\n @Field({ nullable: true })\n subject?: string\n\n @Field()\n kind: string\n\n @Field()\n field: string\n\n @Field({ nullable: true })\n value?: string\n}\n\n@ObjectType({ description: 'How many findings per axis and kind' })\nexport class MasterFindingCount {\n @Field()\n axis: string\n\n @Field()\n kind: string\n\n @Field(type => Int)\n count: number\n}\n\n@ObjectType({ description: 'How many rows were scanned per axis' })\nexport class MasterScanned {\n @Field()\n axis: string\n\n @Field(type => Int)\n rows: number\n}\n\n@ObjectType({ description: 'Master data conformance report' })\nexport class MasterConformance {\n /**\n * 무엇을 몇 개 봤나. **어긋남 0건이 「멀쩡하다」인지 「안 돌았다」인지는 이것으로만 갈린다.**\n */\n @Field(type => [MasterScanned])\n scanned: MasterScanned[]\n\n @Field(type => Int, { description: 'How many findings in total' })\n total: number\n\n @Field(type => [MasterFindingCount])\n counts: MasterFindingCount[]\n\n @Field(type => [MasterFinding])\n findings: MasterFinding[]\n}\n\n@Resolver()\nexport class MasterConformanceResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => MasterConformance, { description: 'Check master data for missing fields and dangling references' })\n async masterConformance(@Ctx() context: ResolverContext): Promise<MasterConformance> {\n const { domain } = context.state\n\n const snapshot = await readMasterSnapshot(domain, getRepository, {\n Person,\n PersonnelClass,\n Equipment,\n EquipmentClass,\n PhysicalAsset,\n PhysicalAssetClass,\n MaterialDefinition,\n MaterialClass,\n OperationDefinition,\n RouteDefinition,\n RecipeDefinition,\n OperationalLocation,\n StopReason,\n TestSpecification\n })\n\n const findings = checkMaster(snapshot)\n\n return { scanned: scannedCounts(snapshot), total: findings.length, counts: summarize(findings), findings }\n }\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 마스터가 **말이 되나** — 축을 가로질러 한 번에 본다.
|
|
3
|
+
*
|
|
4
|
+
* ── 왜 필요한가 ────────────────────────────────────────────────────────────
|
|
5
|
+
* 축마다 화면이 있고 축마다 값이 차 있는데, 그 사이가 이어지는지는 아무도 안 봤다. 설비 등급 다섯이
|
|
6
|
+
* 서 있고 설비 스물둘이 어느 등급에도 안 들어 있던 일이 그랬다 — 두 화면 다 멀쩡해 보였다.
|
|
7
|
+
* 카메라 자산 다섯이 ECU 등급에 들어 있던 것도, 정지 사유 고르기가 빈 목록으로 보이던 것도 같다.
|
|
8
|
+
*
|
|
9
|
+
* 사람이 열두 화면을 오가며 맞춰 보는 것으로는 안 된다. 맞춰 보지 않으면 아무도 모르고, 맞춰 봤는지
|
|
10
|
+
* 아닌지도 남지 않는다.
|
|
11
|
+
*
|
|
12
|
+
* ── 두 가지만 본다 ─────────────────────────────────────────────────────────
|
|
13
|
+
* **비어 있으면 안 되는 칸**과 **가리키는데 없는 것**이다. 둘 다 사실 판정이라 다툼이 없다.
|
|
14
|
+
* 「이 값이 옳은가」는 여기서 안 본다 — 그것은 현장이 정한다.
|
|
15
|
+
*
|
|
16
|
+
* ── 없는 것을 없다고 말한다 ────────────────────────────────────────────────
|
|
17
|
+
* 표가 통째로 비었으면 그것도 낸다. 「빠진 것 0건」과 「아직 아무것도 안 세웠다」는 다르고, 세지
|
|
18
|
+
* 않으면 둘이 같아 보인다.
|
|
19
|
+
*/
|
|
20
|
+
/** 한 건의 어긋남. 사실만 담는다 — 무엇을, 어디서, 무엇이. */
|
|
21
|
+
export interface Finding {
|
|
22
|
+
/** 어느 축인가. `person` · `equipment` · `recipeDefinition` … */
|
|
23
|
+
axis: string;
|
|
24
|
+
/** 어느 줄인가. 표가 통째로 빈 경우에는 없다. */
|
|
25
|
+
subject?: string;
|
|
26
|
+
/** `missing` = 비어 있으면 안 되는 칸이 비었다 · `dangling` = 가리키는데 그런 것이 없다 · `empty` = 표가 비었다 · `unused` = 아무도 안 가리킨다 */
|
|
27
|
+
kind: 'missing' | 'dangling' | 'empty' | 'unused';
|
|
28
|
+
/** 어느 칸인가(`missing`·`dangling`), 또는 무엇이 비었나(`empty`·`unused`). */
|
|
29
|
+
field: string;
|
|
30
|
+
/** 가리키던 값. `dangling` 일 때만. */
|
|
31
|
+
value?: string;
|
|
32
|
+
}
|
|
33
|
+
/** 한 축이 내놓는 줄들. 이름과 칸 몇 개만 본다. */
|
|
34
|
+
export type Row = Record<string, any>;
|
|
35
|
+
export interface MasterSnapshot {
|
|
36
|
+
persons: Row[];
|
|
37
|
+
equipment: Row[];
|
|
38
|
+
physicalAssets: Row[];
|
|
39
|
+
materialDefinitions: Row[];
|
|
40
|
+
operationDefinitions: Row[];
|
|
41
|
+
routeDefinitions: Row[];
|
|
42
|
+
recipeDefinitions: Row[];
|
|
43
|
+
operationalLocations: Row[];
|
|
44
|
+
stopReasons: Row[];
|
|
45
|
+
testSpecifications: Row[];
|
|
46
|
+
personnelClasses: Row[];
|
|
47
|
+
equipmentClasses: Row[];
|
|
48
|
+
physicalAssetClasses: Row[];
|
|
49
|
+
materialClasses: Row[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 마스터 전체를 잰다.
|
|
53
|
+
*
|
|
54
|
+
* **순서에 뜻이 없다.** 어느 축부터 보든 같은 답이 나와야 하고, 그래야 이 결과를 두 번 재서
|
|
55
|
+
* 견줄 수 있다.
|
|
56
|
+
*/
|
|
57
|
+
export declare function checkMaster(snapshot: MasterSnapshot): Finding[];
|
|
58
|
+
/**
|
|
59
|
+
* **무엇을 몇 개 봤나.**
|
|
60
|
+
*
|
|
61
|
+
* 어긋남 0건이 「멀쩡하다」인지 「안 돌았다」인지는 이것으로만 갈린다. 본 수를 안 내면 두 답이
|
|
62
|
+
* 같은 글자로 나온다 — 검사기가 스스로를 못 믿게 만드는 가장 흔한 방식이다.
|
|
63
|
+
*/
|
|
64
|
+
export declare function scannedCounts(snapshot: MasterSnapshot): {
|
|
65
|
+
axis: string;
|
|
66
|
+
rows: number;
|
|
67
|
+
}[];
|
|
68
|
+
/** 축·종류별로 몇 건인지. 사람이 먼저 볼 것을 고르는 데 쓴다. */
|
|
69
|
+
export declare function summarize(findings: Finding[]): {
|
|
70
|
+
axis: string;
|
|
71
|
+
kind: string;
|
|
72
|
+
count: number;
|
|
73
|
+
}[];
|
|
74
|
+
/**
|
|
75
|
+
* 한 도메인의 마스터를 통째로 읽어 잰다.
|
|
76
|
+
*
|
|
77
|
+
* **상한을 두지 않는다.** 정합성은 「전부 봤나」가 곧 답이라, 일부만 보고 「어긋남 0건」이라고 하면
|
|
78
|
+
* 그 답이 거짓이 된다. 그래서 이것은 목록 화면이 부르는 것이 아니라 **사람이 눌러서 도는 검사**다.
|
|
79
|
+
*/
|
|
80
|
+
export declare function readMasterSnapshot(domain: any, getRepository: any, entities: Record<string, any>): Promise<MasterSnapshot>;
|