@things-factory/ops-master 10.1.35 → 10.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist-client/pages/master/applied-when-dialog.d.ts +45 -0
  2. package/dist-client/pages/master/applied-when-dialog.js +139 -0
  3. package/dist-client/pages/master/applied-when-dialog.js.map +1 -0
  4. package/dist-client/pages/master/assignment-fields.d.ts +28 -0
  5. package/dist-client/pages/master/assignment-fields.js +55 -0
  6. package/dist-client/pages/master/assignment-fields.js.map +1 -0
  7. package/dist-client/pages/master/equipment.js +1 -0
  8. package/dist-client/pages/master/equipment.js.map +1 -1
  9. package/dist-client/pages/master/master-page-base.d.ts +14 -0
  10. package/dist-client/pages/master/master-page-base.js +40 -4
  11. package/dist-client/pages/master/master-page-base.js.map +1 -1
  12. package/dist-client/pages/master/operational-location.js +1 -0
  13. package/dist-client/pages/master/operational-location.js.map +1 -1
  14. package/dist-client/tsconfig.tsbuildinfo +1 -1
  15. package/dist-server/service/assignment/applied-when.d.ts +24 -0
  16. package/dist-server/service/assignment/applied-when.js +16 -0
  17. package/dist-server/service/assignment/applied-when.js.map +1 -0
  18. package/dist-server/service/assignment/assignment-claim.d.ts +111 -0
  19. package/dist-server/service/assignment/assignment-claim.js +160 -0
  20. package/dist-server/service/assignment/assignment-claim.js.map +1 -0
  21. package/dist-server/service/assignment/assignment-history.d.ts +87 -0
  22. package/dist-server/service/assignment/assignment-history.js +250 -0
  23. package/dist-server/service/assignment/assignment-history.js.map +1 -0
  24. package/dist-server/service/assignment/assignment-write.d.ts +25 -0
  25. package/dist-server/service/assignment/assignment-write.js +69 -0
  26. package/dist-server/service/assignment/assignment-write.js.map +1 -0
  27. package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -1
  28. package/dist-server/service/equipment/equipment-master-resolver.js +6 -3
  29. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  30. package/dist-server/service/index.d.ts +3 -0
  31. package/dist-server/service/index.js +3 -0
  32. package/dist-server/service/index.js.map +1 -1
  33. package/dist-server/service/location/location-master-resolver.d.ts +4 -1
  34. package/dist-server/service/location/location-master-resolver.js +6 -3
  35. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  36. package/dist-server/service/master-crud.d.ts +11 -0
  37. package/dist-server/service/master-crud.js +12 -1
  38. package/dist-server/service/master-crud.js.map +1 -1
  39. package/dist-server/service/material/material-revision-service.js +13 -0
  40. package/dist-server/service/material/material-revision-service.js.map +1 -1
  41. package/dist-server/service/recipe/recipe-revision-service.js +13 -0
  42. package/dist-server/service/recipe/recipe-revision-service.js.map +1 -1
  43. package/dist-server/service/test-spec/test-spec-revision-service.js +14 -0
  44. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -1
  45. package/dist-server/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +2 -2
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 화면이 「언제부터」를 말하는 모양.
3
+ *
4
+ * ── 왜 날짜 칸 하나가 아닌가 ───────────────────────────────────────────────
5
+ * 과거 시각을 받으면 그것이 **지연 기록**인지 **이미 적은 것의 정정**인지 알 수 없다. 둘은 다른
6
+ * 일이다 — 앞은 「그때 옮겼는데 이제 적는다」이고 뒤는 「그때 적은 것이 틀렸다」다. 이력이 그
7
+ * 구분을 들어야 나중에 「무엇이 왜 바뀌었나」에 답한다.
8
+ *
9
+ * **기록 시각은 여기 없다.** 사람이 넣지 않고 서버가 붙인다.
10
+ */
11
+ export interface AppliedWhen {
12
+ /** 언제부터. 없으면 지금부터다. */
13
+ at?: string;
14
+ /** 과거일 때 — 지연 기록인가 정정인가. 없으면 지연 기록으로 본다. */
15
+ kind?: 'change' | 'correction';
16
+ /** 정정이면 어느 구간까지인가. */
17
+ until?: string;
18
+ }
19
+ /** 화면이 준 것을 `saveMasterPatches` 의 선택지로. */
20
+ export declare function appliedOptions(applied?: AppliedWhen): {
21
+ appliedUntil?: Date;
22
+ appliedKind?: "change" | "correction";
23
+ appliedAt?: Date;
24
+ };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appliedOptions = appliedOptions;
4
+ const when = (said) => (said ? new Date(said) : undefined);
5
+ /** 화면이 준 것을 `saveMasterPatches` 의 선택지로. */
6
+ function appliedOptions(applied) {
7
+ if (!applied) {
8
+ return {};
9
+ }
10
+ return {
11
+ ...(applied.at ? { appliedAt: when(applied.at) } : {}),
12
+ ...(applied.kind ? { appliedKind: applied.kind } : {}),
13
+ ...(applied.until ? { appliedUntil: when(applied.until) } : {})
14
+ };
15
+ }
16
+ //# sourceMappingURL=applied-when.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applied-when.js","sourceRoot":"","sources":["../../../server/service/assignment/applied-when.ts"],"names":[],"mappings":";;AAsBA,wCAUC;AAbD,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AAEnE,2CAA2C;AAC3C,SAAgB,cAAc,CAAC,OAAqB;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO;QACL,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChE,CAAA;AACH,CAAC","sourcesContent":["/**\n * 화면이 「언제부터」를 말하는 모양.\n *\n * ── 왜 날짜 칸 하나가 아닌가 ───────────────────────────────────────────────\n * 과거 시각을 받으면 그것이 **지연 기록**인지 **이미 적은 것의 정정**인지 알 수 없다. 둘은 다른\n * 일이다 — 앞은 「그때 옮겼는데 이제 적는다」이고 뒤는 「그때 적은 것이 틀렸다」다. 이력이 그\n * 구분을 들어야 나중에 「무엇이 왜 바뀌었나」에 답한다.\n *\n * **기록 시각은 여기 없다.** 사람이 넣지 않고 서버가 붙인다.\n */\nexport interface AppliedWhen {\n /** 언제부터. 없으면 지금부터다. */\n at?: string\n /** 과거일 때 — 지연 기록인가 정정인가. 없으면 지연 기록으로 본다. */\n kind?: 'change' | 'correction'\n /** 정정이면 어느 구간까지인가. */\n until?: string\n}\n\nconst when = (said?: string) => (said ? new Date(said) : undefined)\n\n/** 화면이 준 것을 `saveMasterPatches` 의 선택지로. */\nexport function appliedOptions(applied?: AppliedWhen) {\n if (!applied) {\n return {}\n }\n\n return {\n ...(applied.at ? { appliedAt: when(applied.at) } : {}),\n ...(applied.kind ? { appliedKind: applied.kind } : {}),\n ...(applied.until ? { appliedUntil: when(applied.until) } : {})\n }\n}\n"]}
@@ -0,0 +1,111 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ import { User } from '@things-factory/auth-base';
3
+ /**
4
+ * **언제부터 그러했고, 언제 그렇게 적었나** — 배정과 선언의 주장 한 줄.
5
+ *
6
+ * ═══════════════════════════════════════════════════════════════════════════
7
+ * ── 무엇이 없었나 ──────────────────────────────────────────────────────────
8
+ * 가동률의 분모는 **설비가 어느 자리에 있었나 · 그 자리가 어느 근무조로 돌았나 · 그 자리가 어디에
9
+ * 속했나**로 정해진다. 셋 다 칸 하나였다. 그래서 설비를 옮기면 **지난달 가동률이 바뀌었고**, 아무도
10
+ * 아무것도 정정하지 않았는데 숫자가 달라진 까닭을 말할 수 없었다.
11
+ *
12
+ * ── 시각이 둘인 까닭 ───────────────────────────────────────────────────────
13
+ * 「언제부터 그렇게 하기로 했나」와 「언제 그렇게 적었나」는 다르다.
14
+ *
15
+ * ```
16
+ * validFrom · validTo 적용 시각 — 업무의 시간. 분모를 나누는 경계가 이것이다
17
+ * recordedAt 기록 시각 — 우리가 그것을 안 시각
18
+ * supersededAt 이 주장이 물러난 시각. 덮어쓰지 않고 물러나게 한다
19
+ * ```
20
+ *
21
+ * 사흘 전에 옮긴 것을 오늘 적으면 둘이 갈라진다. **이틀 전 기록시점으로 물으면 옛 자리가 나와야
22
+ * 한다** — 그때 우리가 알던 것이 그것이기 때문이다(ADR-0088 결정 2 ③).
23
+ *
24
+ * ── 정정은 덮지 않고 조각을 남긴다 ─────────────────────────────────────────
25
+ * ```
26
+ * 지금까지 A [1일, ∞)
27
+ * 오늘 정정 「10~15일은 B 였다」
28
+ * 최신 A [1일, 10일) · B [10일, 15일) · A [15일, ∞)
29
+ * 정정 전 시점 A [1일, ∞) ← 조각은 그때 아직 기록되지 않았다
30
+ * ```
31
+ *
32
+ * ── 이 표가 기준이 되는 원본이고, 마스터 행은 투영이다 ─────────────────────
33
+ * 미래 주장은 마스터 행을 **안 건드린다.** 다음 달 이동을 지금 적었다고 다른 제품이 오늘부터 새
34
+ * 자리를 읽으면 안 된다. 도래하면 투영이 행을 맞춘다(§`projectDueClaims`).
35
+ *
36
+ * ── 이름이 아니라 식별자로 가리킨다 ────────────────────────────────────────
37
+ * `value.ref` 가 안정된 식별자이고 `value.label` 은 **그때 뭐라고 불렀나**다. 이름을 담으면 이름을
38
+ * 바꾼 날 과거 관계가 끊긴다(적용 가이드 §0-1 의 그 부류).
39
+ * ═══════════════════════════════════════════════════════════════════════════
40
+ */
41
+ export declare class AssignmentClaim {
42
+ readonly id: string;
43
+ domain?: Domain;
44
+ domainId?: string;
45
+ /** 어느 축의 무엇인가 — `equipment` · `operationalLocation`. */
46
+ axis: string;
47
+ /** 그 축의 어느 행인가. UUID 다. */
48
+ rowId: string;
49
+ /** 어느 칸인가 — 선언한 셋만 온다(§`ASSIGNMENT_FIELDS`). */
50
+ field: string;
51
+ /**
52
+ * 그때의 값.
53
+ *
54
+ * ```
55
+ * { ref, label } 관계를 가리키는 칸 — 식별자와 그때의 표시값
56
+ * { names: [...] } 이름 목록을 드는 칸 — 근무조 선언
57
+ * null **선언 없음.** 「그때부터 부모가 없다」는 정상 값이고 결측이 아니다
58
+ * ```
59
+ */
60
+ value?: Record<string, unknown> | null;
61
+ /** 언제부터 그러했나 — 업무의 시간. */
62
+ validFrom: Date;
63
+ /** 언제까지. 비면 아직 끝나지 않았다. */
64
+ validTo?: Date;
65
+ /** 언제 그렇게 적었나. 서버가 붙인다 — 사람이 넣지 않는다. */
66
+ recordedAt: Date;
67
+ /** 언제 물러났나. 비면 지금도 서 있는 주장이다. */
68
+ supersededAt?: Date;
69
+ /**
70
+ * 왜 이 주장이 섰나 — 읽는 사람이 「지연 기록인가 정정인가」를 알아야 한다.
71
+ *
72
+ * ```
73
+ * initial 도입 시각에 확인된 초기 상태
74
+ * change 바꾼다는 선언
75
+ * correction 이미 적은 것을 고친다
76
+ * remainder 정정이 남긴 앞뒤 조각
77
+ * ```
78
+ */
79
+ reason: string;
80
+ recorder?: User;
81
+ recorderId?: string;
82
+ createdAt?: Date;
83
+ }
84
+ /**
85
+ * **이력을 받는 칸은 셋뿐이다.**
86
+ *
87
+ * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이
88
+ * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.
89
+ */
90
+ export declare const ASSIGNMENT_FIELDS: ({
91
+ axis: string;
92
+ field: string;
93
+ kind: "relation";
94
+ target: string;
95
+ } | {
96
+ axis: string;
97
+ field: string;
98
+ kind: "names";
99
+ target?: undefined;
100
+ })[];
101
+ export declare function assignmentField(axis: string, field: string): {
102
+ axis: string;
103
+ field: string;
104
+ kind: "relation";
105
+ target: string;
106
+ } | {
107
+ axis: string;
108
+ field: string;
109
+ kind: "names";
110
+ target?: undefined;
111
+ };
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ASSIGNMENT_FIELDS = exports.AssignmentClaim = void 0;
4
+ exports.assignmentField = assignmentField;
5
+ const tslib_1 = require("tslib");
6
+ const typeorm_1 = require("typeorm");
7
+ const type_graphql_1 = require("type-graphql");
8
+ const shell_1 = require("@things-factory/shell");
9
+ const auth_base_1 = require("@things-factory/auth-base");
10
+ /**
11
+ * **언제부터 그러했고, 언제 그렇게 적었나** — 배정과 선언의 주장 한 줄.
12
+ *
13
+ * ═══════════════════════════════════════════════════════════════════════════
14
+ * ── 무엇이 없었나 ──────────────────────────────────────────────────────────
15
+ * 가동률의 분모는 **설비가 어느 자리에 있었나 · 그 자리가 어느 근무조로 돌았나 · 그 자리가 어디에
16
+ * 속했나**로 정해진다. 셋 다 칸 하나였다. 그래서 설비를 옮기면 **지난달 가동률이 바뀌었고**, 아무도
17
+ * 아무것도 정정하지 않았는데 숫자가 달라진 까닭을 말할 수 없었다.
18
+ *
19
+ * ── 시각이 둘인 까닭 ───────────────────────────────────────────────────────
20
+ * 「언제부터 그렇게 하기로 했나」와 「언제 그렇게 적었나」는 다르다.
21
+ *
22
+ * ```
23
+ * validFrom · validTo 적용 시각 — 업무의 시간. 분모를 나누는 경계가 이것이다
24
+ * recordedAt 기록 시각 — 우리가 그것을 안 시각
25
+ * supersededAt 이 주장이 물러난 시각. 덮어쓰지 않고 물러나게 한다
26
+ * ```
27
+ *
28
+ * 사흘 전에 옮긴 것을 오늘 적으면 둘이 갈라진다. **이틀 전 기록시점으로 물으면 옛 자리가 나와야
29
+ * 한다** — 그때 우리가 알던 것이 그것이기 때문이다(ADR-0088 결정 2 ③).
30
+ *
31
+ * ── 정정은 덮지 않고 조각을 남긴다 ─────────────────────────────────────────
32
+ * ```
33
+ * 지금까지 A [1일, ∞)
34
+ * 오늘 정정 「10~15일은 B 였다」
35
+ * 최신 A [1일, 10일) · B [10일, 15일) · A [15일, ∞)
36
+ * 정정 전 시점 A [1일, ∞) ← 조각은 그때 아직 기록되지 않았다
37
+ * ```
38
+ *
39
+ * ── 이 표가 기준이 되는 원본이고, 마스터 행은 투영이다 ─────────────────────
40
+ * 미래 주장은 마스터 행을 **안 건드린다.** 다음 달 이동을 지금 적었다고 다른 제품이 오늘부터 새
41
+ * 자리를 읽으면 안 된다. 도래하면 투영이 행을 맞춘다(§`projectDueClaims`).
42
+ *
43
+ * ── 이름이 아니라 식별자로 가리킨다 ────────────────────────────────────────
44
+ * `value.ref` 가 안정된 식별자이고 `value.label` 은 **그때 뭐라고 불렀나**다. 이름을 담으면 이름을
45
+ * 바꾼 날 과거 관계가 끊긴다(적용 가이드 §0-1 의 그 부류).
46
+ * ═══════════════════════════════════════════════════════════════════════════
47
+ */
48
+ let AssignmentClaim = class AssignmentClaim {
49
+ };
50
+ exports.AssignmentClaim = AssignmentClaim;
51
+ tslib_1.__decorate([
52
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
53
+ (0, type_graphql_1.Field)(type => type_graphql_1.ID),
54
+ tslib_1.__metadata("design:type", String)
55
+ ], AssignmentClaim.prototype, "id", void 0);
56
+ tslib_1.__decorate([
57
+ (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
58
+ (0, type_graphql_1.Field)(type => shell_1.Domain),
59
+ tslib_1.__metadata("design:type", shell_1.Domain)
60
+ ], AssignmentClaim.prototype, "domain", void 0);
61
+ tslib_1.__decorate([
62
+ (0, typeorm_1.RelationId)((claim) => claim.domain),
63
+ tslib_1.__metadata("design:type", String)
64
+ ], AssignmentClaim.prototype, "domainId", void 0);
65
+ tslib_1.__decorate([
66
+ (0, typeorm_1.Column)(),
67
+ (0, type_graphql_1.Field)(),
68
+ tslib_1.__metadata("design:type", String)
69
+ ], AssignmentClaim.prototype, "axis", void 0);
70
+ tslib_1.__decorate([
71
+ (0, typeorm_1.Column)(),
72
+ (0, type_graphql_1.Field)(),
73
+ tslib_1.__metadata("design:type", String)
74
+ ], AssignmentClaim.prototype, "rowId", void 0);
75
+ tslib_1.__decorate([
76
+ (0, typeorm_1.Column)(),
77
+ (0, type_graphql_1.Field)(),
78
+ tslib_1.__metadata("design:type", String)
79
+ ], AssignmentClaim.prototype, "field", void 0);
80
+ tslib_1.__decorate([
81
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
82
+ (0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true }),
83
+ tslib_1.__metadata("design:type", Object)
84
+ ], AssignmentClaim.prototype, "value", void 0);
85
+ tslib_1.__decorate([
86
+ (0, typeorm_1.Column)(),
87
+ (0, type_graphql_1.Field)(),
88
+ tslib_1.__metadata("design:type", Date
89
+ /** 언제까지. 비면 아직 끝나지 않았다. */
90
+ )
91
+ ], AssignmentClaim.prototype, "validFrom", void 0);
92
+ tslib_1.__decorate([
93
+ (0, typeorm_1.Column)({ nullable: true }),
94
+ (0, type_graphql_1.Field)({ nullable: true }),
95
+ tslib_1.__metadata("design:type", Date
96
+ /** 언제 그렇게 적었나. 서버가 붙인다 — 사람이 넣지 않는다. */
97
+ )
98
+ ], AssignmentClaim.prototype, "validTo", void 0);
99
+ tslib_1.__decorate([
100
+ (0, typeorm_1.Column)(),
101
+ (0, type_graphql_1.Field)(),
102
+ tslib_1.__metadata("design:type", Date
103
+ /** 언제 물러났나. 비면 지금도 서 있는 주장이다. */
104
+ )
105
+ ], AssignmentClaim.prototype, "recordedAt", void 0);
106
+ tslib_1.__decorate([
107
+ (0, typeorm_1.Column)({ nullable: true }),
108
+ (0, type_graphql_1.Field)({ nullable: true }),
109
+ tslib_1.__metadata("design:type", Date
110
+ /**
111
+ * 왜 이 주장이 섰나 — 읽는 사람이 「지연 기록인가 정정인가」를 알아야 한다.
112
+ *
113
+ * ```
114
+ * initial 도입 시각에 확인된 초기 상태
115
+ * change 바꾼다는 선언
116
+ * correction 이미 적은 것을 고친다
117
+ * remainder 정정이 남긴 앞뒤 조각
118
+ * ```
119
+ */
120
+ )
121
+ ], AssignmentClaim.prototype, "supersededAt", void 0);
122
+ tslib_1.__decorate([
123
+ (0, typeorm_1.Column)(),
124
+ (0, type_graphql_1.Field)(),
125
+ tslib_1.__metadata("design:type", String)
126
+ ], AssignmentClaim.prototype, "reason", void 0);
127
+ tslib_1.__decorate([
128
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
129
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
130
+ tslib_1.__metadata("design:type", auth_base_1.User)
131
+ ], AssignmentClaim.prototype, "recorder", void 0);
132
+ tslib_1.__decorate([
133
+ (0, typeorm_1.RelationId)((claim) => claim.recorder),
134
+ tslib_1.__metadata("design:type", String)
135
+ ], AssignmentClaim.prototype, "recorderId", void 0);
136
+ tslib_1.__decorate([
137
+ (0, typeorm_1.CreateDateColumn)(),
138
+ (0, type_graphql_1.Field)({ nullable: true }),
139
+ tslib_1.__metadata("design:type", Date)
140
+ ], AssignmentClaim.prototype, "createdAt", void 0);
141
+ exports.AssignmentClaim = AssignmentClaim = tslib_1.__decorate([
142
+ (0, typeorm_1.Entity)({ name: 'ops_assignment_claim' }),
143
+ (0, typeorm_1.Index)('ix_ops_assignment_claim_0', (claim) => [claim.domain, claim.axis, claim.rowId, claim.field]),
144
+ (0, type_graphql_1.ObjectType)({ description: 'what an assignment or declaration was, from when, as recorded when' })
145
+ ], AssignmentClaim);
146
+ /**
147
+ * **이력을 받는 칸은 셋뿐이다.**
148
+ *
149
+ * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이
150
+ * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.
151
+ */
152
+ exports.ASSIGNMENT_FIELDS = [
153
+ { axis: 'equipment', field: 'homeLocation', kind: 'relation', target: 'operationalLocation' },
154
+ { axis: 'operationalLocation', field: 'workShiftNames', kind: 'names' },
155
+ { axis: 'operationalLocation', field: 'parentId', kind: 'relation', target: 'operationalLocation' }
156
+ ];
157
+ function assignmentField(axis, field) {
158
+ return exports.ASSIGNMENT_FIELDS.find(one => one.axis === axis && one.field === field);
159
+ }
160
+ //# sourceMappingURL=assignment-claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignment-claim.js","sourceRoot":"","sources":["../../../server/service/assignment/assignment-claim.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\nimport { Domain, ScalarObject } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * **언제부터 그러했고, 언제 그렇게 적었나** — 배정과 선언의 주장 한 줄.\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── 무엇이 없었나 ──────────────────────────────────────────────────────────\n * 가동률의 분모는 **설비가 어느 자리에 있었나 · 그 자리가 어느 근무조로 돌았나 · 그 자리가 어디에\n * 속했나**로 정해진다. 셋 다 칸 하나였다. 그래서 설비를 옮기면 **지난달 가동률이 바뀌었고**, 아무도\n * 아무것도 정정하지 않았는데 숫자가 달라진 까닭을 말할 수 없었다.\n *\n * ── 시각이 둘인 까닭 ───────────────────────────────────────────────────────\n * 「언제부터 그렇게 하기로 했나」와 「언제 그렇게 적었나」는 다르다.\n *\n * ```\n * validFrom · validTo 적용 시각 — 업무의 시간. 분모를 나누는 경계가 이것이다\n * recordedAt 기록 시각 — 우리가 그것을 안 시각\n * supersededAt 이 주장이 물러난 시각. 덮어쓰지 않고 물러나게 한다\n * ```\n *\n * 사흘 전에 옮긴 것을 오늘 적으면 둘이 갈라진다. **이틀 전 기록시점으로 물으면 옛 자리가 나와야\n * 한다** — 그때 우리가 알던 것이 그것이기 때문이다(ADR-0088 결정 2 ③).\n *\n * ── 정정은 덮지 않고 조각을 남긴다 ─────────────────────────────────────────\n * ```\n * 지금까지 A [1일, ∞)\n * 오늘 정정 「10~15일은 B 였다」\n * 최신 A [1일, 10일) · B [10일, 15일) · A [15일, ∞)\n * 정정 전 시점 A [1일, ∞) ← 조각은 그때 아직 기록되지 않았다\n * ```\n *\n * ── 이 표가 기준이 되는 원본이고, 마스터 행은 투영이다 ─────────────────────\n * 미래 주장은 마스터 행을 **안 건드린다.** 다음 달 이동을 지금 적었다고 다른 제품이 오늘부터 새\n * 자리를 읽으면 안 된다. 도래하면 투영이 행을 맞춘다(§`projectDueClaims`).\n *\n * ── 이름이 아니라 식별자로 가리킨다 ────────────────────────────────────────\n * `value.ref` 가 안정된 식별자이고 `value.label` 은 **그때 뭐라고 불렀나**다. 이름을 담으면 이름을\n * 바꾼 날 과거 관계가 끊긴다(적용 가이드 §0-1 의 그 부류).\n * ═══════════════════════════════════════════════════════════════════════════\n */\n@Entity({ name: 'ops_assignment_claim' })\n@Index('ix_ops_assignment_claim_0', (claim: AssignmentClaim) => [claim.domain, claim.axis, claim.rowId, claim.field])\n@ObjectType({ description: 'what an assignment or declaration was, from when, as recorded when' })\nexport class AssignmentClaim {\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((claim: AssignmentClaim) => claim.domain)\n domainId?: string\n\n /** 어느 축의 무엇인가 — `equipment` · `operationalLocation`. */\n @Column()\n @Field()\n axis: string\n\n /** 그 축의 어느 행인가. UUID 다. */\n @Column()\n @Field()\n rowId: string\n\n /** 어느 칸인가 — 선언한 셋만 온다(§`ASSIGNMENT_FIELDS`). */\n @Column()\n @Field()\n field: string\n\n /**\n * 그때의 값.\n *\n * ```\n * { ref, label } 관계를 가리키는 칸 — 식별자와 그때의 표시값\n * { names: [...] } 이름 목록을 드는 칸 — 근무조 선언\n * null **선언 없음.** 「그때부터 부모가 없다」는 정상 값이고 결측이 아니다\n * ```\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true })\n value?: Record<string, unknown> | null\n\n /** 언제부터 그러했나 — 업무의 시간. */\n @Column()\n @Field()\n validFrom: Date\n\n /** 언제까지. 비면 아직 끝나지 않았다. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n validTo?: Date\n\n /** 언제 그렇게 적었나. 서버가 붙인다 — 사람이 넣지 않는다. */\n @Column()\n @Field()\n recordedAt: Date\n\n /** 언제 물러났나. 비면 지금도 서 있는 주장이다. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n supersededAt?: Date\n\n /**\n * 왜 이 주장이 섰나 — 읽는 사람이 「지연 기록인가 정정인가」를 알아야 한다.\n *\n * ```\n * initial 도입 시각에 확인된 초기 상태\n * change 바꾼다는 선언\n * correction 이미 적은 것을 고친다\n * remainder 정정이 남긴 앞뒤 조각\n * ```\n */\n @Column()\n @Field()\n reason: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n recorder?: User\n\n @RelationId((claim: AssignmentClaim) => claim.recorder)\n recorderId?: string\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n}\n\n/**\n * **이력을 받는 칸은 셋뿐이다.**\n *\n * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이\n * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.\n */\nexport const ASSIGNMENT_FIELDS = [\n { axis: 'equipment', field: 'homeLocation', kind: 'relation' as const, target: 'operationalLocation' },\n { axis: 'operationalLocation', field: 'workShiftNames', kind: 'names' as const },\n { axis: 'operationalLocation', field: 'parentId', kind: 'relation' as const, target: 'operationalLocation' }\n]\n\nexport function assignmentField(axis: string, field: string) {\n return ASSIGNMENT_FIELDS.find(one => one.axis === axis && one.field === field)\n}\n"]}
@@ -0,0 +1,87 @@
1
+ import { EntityManager } from 'typeorm';
2
+ import { Domain } from '@things-factory/shell';
3
+ import { AssignmentClaim } from './assignment-claim';
4
+ /**
5
+ * 주장을 남기고 읽는 규칙 — **덮어쓰지 않는다.**
6
+ *
7
+ * ── 정정이 양옆을 남긴다 ───────────────────────────────────────────────────
8
+ * 겹치는 주장을 물러나게 하는 것만으로는 모자란다. `A [1일, ∞)` 에 「10~15는 B」를 정정하면 A 의
9
+ * **앞뒤 조각을 같은 값으로 다시 주장**해야 최신 답이 `A·B·A` 가 된다. 조각도 새 주장이므로 정정 전
10
+ * 기록시점으로 읽으면 원래 A 하나가 그대로 나온다.
11
+ *
12
+ * ── 동시 정정은 거절한다 ───────────────────────────────────────────────────
13
+ * 물러나게 하려던 주장이 이미 물러나 있으면 충돌이다. **자동으로 다시 시도하지 않는다** — 덮으면
14
+ * 한쪽의 판단이 사라진다. 다시 읽고 다시 정하는 것은 사람의 일이다.
15
+ */
16
+ export interface ClaimAt {
17
+ /** 어느 업무시각의 이야기인가. */
18
+ asOfBusiness: Date;
19
+ /** 어느 기록시점까지 알려진 것만 볼 것인가. 안 주면 지금까지 아는 전부다. */
20
+ asOfRecord?: Date;
21
+ }
22
+ export interface ClaimTarget {
23
+ axis: string;
24
+ rowId: string;
25
+ field: string;
26
+ }
27
+ /**
28
+ * 그 업무시각에 유효했던 주장.
29
+ *
30
+ * **없으면 `undefined` 다 — 「선언 없음」과 다르다.** 선언 없음은 `value` 가 `null` 인 주장이 있는
31
+ * 것이고, 여기서 `undefined` 는 **그때 무엇이었는지 모른다**는 뜻이다.
32
+ */
33
+ export declare function claimAt(manager: EntityManager, domain: Domain, target: ClaimTarget, at: ClaimAt): Promise<AssignmentClaim | undefined>;
34
+ /** 한 창을 덮는 주장들 — **계산 구간을 나눌 경계**가 여기서 나온다. 시작 시각 순이다. */
35
+ export declare function claimsDuring(manager: EntityManager, domain: Domain, target: ClaimTarget, window: {
36
+ from: Date;
37
+ to: Date;
38
+ }, asOfRecord?: Date): Promise<AssignmentClaim[]>;
39
+ export interface RecordClaim {
40
+ target: ClaimTarget;
41
+ value: Record<string, unknown> | null;
42
+ /** 언제부터 그러한가. 안 주면 지금부터다 — 그 기본값이 곧 「지금부터」라는 주장이다. */
43
+ validFrom?: Date;
44
+ /** 언제까지. 정정에서만 쓴다. */
45
+ validTo?: Date;
46
+ reason: 'initial' | 'change' | 'correction';
47
+ recorder?: unknown;
48
+ /**
49
+ * 내가 읽었을 때 서 있던 주장들의 id.
50
+ *
51
+ * 주면 **그 사이에 누가 바꿨는지** 본다 — 화면에서 읽고 고치는 동안 다른 사람이 같은 구간을
52
+ * 정정했으면 내 정정이 그것을 덮는다. 덮지 않고 거절한다.
53
+ */
54
+ expecting?: readonly string[];
55
+ }
56
+ /**
57
+ * 주장을 남긴다. **정정이면 겹치는 것을 물러나게 하고 앞뒤 조각을 다시 주장한다.**
58
+ *
59
+ * 부르는 쪽이 마스터 변경과 **같은 트랜잭션**으로 부른다 — 갈라지면 값은 바뀌고 이력은 없는 상태가
60
+ * 생긴다.
61
+ */
62
+ export declare function recordClaim(manager: EntityManager, domain: Domain, said: RecordClaim, now?: Date): Promise<AssignmentClaim>;
63
+ /**
64
+ * 도래한 미래 주장을 마스터 행에 옮긴다 — **앱의 주기 작업이 부른다.**
65
+ *
66
+ * ops-master 는 라이브러리라 스스로 돌지 않는다. 그 사이 본행은 낡아 있고, **정확해야 하는 소비처는
67
+ * 본행이 아니라 주장을 읽는다.**
68
+ */
69
+ export declare function projectDueClaims(manager: EntityManager, domain: Domain, entities: {
70
+ axis: string;
71
+ entity: Function;
72
+ }[], now?: Date): Promise<number>;
73
+ /** 주장의 값을 마스터 칸이 드는 모양으로. 「선언 없음」은 `null` 이다. */
74
+ export declare function valueOnRow(claim: AssignmentClaim): unknown;
75
+ /**
76
+ * 도입할 때 — **지금 값을 초기 상태로 적는다.**
77
+ *
78
+ * 과거를 소급해 만드는 것과 다르다. 「도입 시각에 이 값이었다는 것을 확인했다」는 주장이고, **그
79
+ * 전은 결측 그대로** 둔다. 이것이 없으면 **한 번도 안 옮긴 설비가 영영 이력이 빈다** — 그런 설비가
80
+ * 가장 많다.
81
+ *
82
+ * 이미 주장이 있는 행은 건너뛴다. 두 번 불러도 같다.
83
+ */
84
+ export declare function recordInitialAssignments(manager: EntityManager, domain: Domain, entities: {
85
+ axis: string;
86
+ entity: Function;
87
+ }[], at?: Date, recorder?: unknown): Promise<number>;