@things-factory/ops-master 10.1.35 → 10.1.37

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 (66) hide show
  1. package/dist-client/bootstrap.d.ts +1 -0
  2. package/dist-client/bootstrap.js +26 -0
  3. package/dist-client/bootstrap.js.map +1 -1
  4. package/dist-client/master-change-policy-axes.d.ts +26 -0
  5. package/dist-client/master-change-policy-axes.js +18 -0
  6. package/dist-client/master-change-policy-axes.js.map +1 -0
  7. package/dist-client/master-change-policy-panel.d.ts +49 -0
  8. package/dist-client/master-change-policy-panel.js +393 -0
  9. package/dist-client/master-change-policy-panel.js.map +1 -0
  10. package/dist-client/pages/master/applied-when-dialog.d.ts +45 -0
  11. package/dist-client/pages/master/applied-when-dialog.js +139 -0
  12. package/dist-client/pages/master/applied-when-dialog.js.map +1 -0
  13. package/dist-client/pages/master/assignment-fields.d.ts +27 -0
  14. package/dist-client/pages/master/assignment-fields.js +62 -0
  15. package/dist-client/pages/master/assignment-fields.js.map +1 -0
  16. package/dist-client/pages/master/equipment.js +1 -0
  17. package/dist-client/pages/master/equipment.js.map +1 -1
  18. package/dist-client/pages/master/master-page-base.d.ts +12 -0
  19. package/dist-client/pages/master/master-page-base.js +38 -4
  20. package/dist-client/pages/master/master-page-base.js.map +1 -1
  21. package/dist-client/pages/master/operational-location.js +1 -0
  22. package/dist-client/pages/master/operational-location.js.map +1 -1
  23. package/dist-client/tsconfig.tsbuildinfo +1 -1
  24. package/dist-server/service/approval-route.d.ts +37 -0
  25. package/dist-server/service/approval-route.js +34 -0
  26. package/dist-server/service/approval-route.js.map +1 -0
  27. package/dist-server/service/assignment/applied-when.d.ts +24 -0
  28. package/dist-server/service/assignment/applied-when.js +16 -0
  29. package/dist-server/service/assignment/applied-when.js.map +1 -0
  30. package/dist-server/service/assignment/assignment-history.d.ts +92 -0
  31. package/dist-server/service/assignment/assignment-history.js +289 -0
  32. package/dist-server/service/assignment/assignment-history.js.map +1 -0
  33. package/dist-server/service/assignment/assignment-write.d.ts +42 -0
  34. package/dist-server/service/assignment/assignment-write.js +101 -0
  35. package/dist-server/service/assignment/assignment-write.js.map +1 -0
  36. package/dist-server/service/assignment/master-value-claim.d.ts +135 -0
  37. package/dist-server/service/assignment/master-value-claim.js +174 -0
  38. package/dist-server/service/assignment/master-value-claim.js.map +1 -0
  39. package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -1
  40. package/dist-server/service/equipment/equipment-master-resolver.js +6 -3
  41. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  42. package/dist-server/service/index.d.ts +6 -1
  43. package/dist-server/service/index.js +10 -0
  44. package/dist-server/service/index.js.map +1 -1
  45. package/dist-server/service/location/location-master-resolver.d.ts +4 -1
  46. package/dist-server/service/location/location-master-resolver.js +6 -3
  47. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  48. package/dist-server/service/master-change-policy.d.ts +65 -2
  49. package/dist-server/service/master-change-policy.js +166 -21
  50. package/dist-server/service/master-change-policy.js.map +1 -1
  51. package/dist-server/service/master-crud.d.ts +11 -0
  52. package/dist-server/service/master-crud.js +12 -1
  53. package/dist-server/service/master-crud.js.map +1 -1
  54. package/dist-server/service/material/material-revision-service.js +13 -0
  55. package/dist-server/service/material/material-revision-service.js.map +1 -1
  56. package/dist-server/service/recipe/recipe-revision-service.js +13 -0
  57. package/dist-server/service/recipe/recipe-revision-service.js.map +1 -1
  58. package/dist-server/service/test-spec/test-spec-revision-service.js +14 -0
  59. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -1
  60. package/dist-server/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +6 -4
  62. package/translations/en.json +59 -2
  63. package/translations/ja.json +58 -1
  64. package/translations/ko.json +59 -2
  65. package/translations/ms.json +58 -1
  66. package/translations/zh.json +60 -1
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.claimedField = void 0;
4
+ exports.claimAssignmentChanges = claimAssignmentChanges;
5
+ exports.toClaimValue = toClaimValue;
6
+ exports.rowOf = rowOf;
7
+ const master_value_claim_1 = require("./master-value-claim");
8
+ Object.defineProperty(exports, "claimedField", { enumerable: true, get: function () { return master_value_claim_1.claimedField; } });
9
+ const master_axis_1 = require("../master-axis");
10
+ const assignment_history_1 = require("./assignment-history");
11
+ /**
12
+ * 그리드가 보낸 변경에서 **배정·선언 칸을 골라 주장으로 남긴다.**
13
+ *
14
+ * ── 왜 본행을 되돌려 놓나 ──────────────────────────────────────────────────
15
+ * 적용 시각이 미래면 **본행은 그대로여야 한다.** 그리드는 값을 보냈지만 그것은 「다음 달부터」라는
16
+ * 뜻이고, 오늘의 값이 아니다. 그래서 `keepOnRow` 로 옛 값을 되돌려 저장에 얹는다 — 도래하면
17
+ * `projectDueClaims` 가 옮긴다.
18
+ *
19
+ * ── 마스터 변경과 한 트랜잭션이다 ──────────────────────────────────────────
20
+ * 부르는 쪽이 `saveMasterPatches` 안이므로 같은 `manager` 다. 갈라지면 값은 바뀌고 이력은 없는
21
+ * 상태가 생긴다.
22
+ */
23
+ async function claimAssignmentChanges(manager, domain, axis, found, values, user, options) {
24
+ const keepOnRow = {};
25
+ const fields = master_value_claim_1.CLAIMED_FIELDS.filter(one => one.axis === axis);
26
+ if (!fields.length) {
27
+ return { keepOnRow };
28
+ }
29
+ const now = new Date();
30
+ const appliedAt = options.appliedAt ?? now;
31
+ const future = appliedAt.getTime() > now.getTime();
32
+ for (const declared of fields) {
33
+ if (!(declared.field in values)) {
34
+ continue;
35
+ }
36
+ const next = values[declared.field];
37
+ /* 안 바뀐 칸은 주장을 남기지 않는다 — 같은 값을 다시 적으면 이력이 뜻 없이 길어진다. */
38
+ if (sameValue(found[declared.field], next)) {
39
+ continue;
40
+ }
41
+ await (0, assignment_history_1.recordClaim)(manager, domain, {
42
+ target: { axis, rowId: String(found.id), field: declared.field },
43
+ value: await toClaimValue(manager, domain, declared, next),
44
+ /* 바뀌기 전 값 — 첫 주장이면 그 앞을 덮는 줄이 같이 선다(§`recordClaim`). */
45
+ previous: await toClaimValue(manager, domain, declared, found[declared.field]),
46
+ validFrom: appliedAt,
47
+ validTo: options.appliedUntil,
48
+ reason: options.appliedKind === 'correction' ? 'correction' : 'change',
49
+ recorder: user
50
+ }, now);
51
+ if (future) {
52
+ keepOnRow[declared.field] = found[declared.field] ?? null;
53
+ }
54
+ }
55
+ return { keepOnRow };
56
+ }
57
+ /**
58
+ * 마스터 칸의 값 → 주장의 값. 「선언 없음」은 `null` 이다.
59
+ *
60
+ * ⚠ **관계 칸은 UUID 가 아니라 이름을 담는다.** `ops_equipment.home_location` 에 `UNCOIL-01` 이
61
+ * 들어 있고, `parentId` 는 id 일 수도 이름일 수도 있다(이 제품의 규약). 처음에 칸 값을 그대로
62
+ * 식별자로 썼다가 **정상 편집이 전부 거절됐다**(2026-09-22, 화면에서 드러남).
63
+ *
64
+ * 그래서 여기서 **가리키는 행을 찾아 식별자와 그때의 이름을 함께 담는다.** 마스터 칸은 그대로
65
+ * 이름을 들고, 주장은 이름이 바뀌어도 안 끊긴다.
66
+ */
67
+ async function toClaimValue(manager, domain, declared, value) {
68
+ if (value === null || value === undefined || value === '') {
69
+ return null;
70
+ }
71
+ /* 값 그 자체인 칸 — 가리키는 것이 없으므로 푸는 일도 없다. */
72
+ if (declared.kind === 'literal') {
73
+ return { literal: String(value) };
74
+ }
75
+ if (declared.kind === 'names') {
76
+ return { names: Array.isArray(value) ? value.map(String) : [String(value)] };
77
+ }
78
+ const found = await rowOf(manager, domain, declared.target, String(value));
79
+ /* 못 찾으면 그대로 담는다 — 검증이 그 다음에 거절한다. 여기서 지어내지 않는다. */
80
+ return found ? { ref: found.id, label: found.name } : { ref: String(value) };
81
+ }
82
+ /** 이름으로도 id 로도 찾는다 — 이 제품의 관계 칸이 둘 다 담는다. */
83
+ async function rowOf(manager, domain, targetAxis, given) {
84
+ const axis = master_axis_1.MASTER_AXES.find(one => one.key === targetAxis);
85
+ if (!axis) {
86
+ return undefined;
87
+ }
88
+ const byId = UUID.test(given)
89
+ ? await manager.findOne(axis.entity, { where: { id: given, domain: { id: domain.id } } })
90
+ : undefined;
91
+ const found = byId ?? (await manager.findOne(axis.entity, { where: { name: given, domain: { id: domain.id } } }));
92
+ return found;
93
+ }
94
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
95
+ function sameValue(before, after) {
96
+ if (Array.isArray(before) || Array.isArray(after)) {
97
+ return JSON.stringify(before ?? null) === JSON.stringify(after ?? null);
98
+ }
99
+ return (before ?? null) === (after ?? null);
100
+ }
101
+ //# sourceMappingURL=assignment-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignment-write.js","sourceRoot":"","sources":["../../../server/service/assignment/assignment-write.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, ObjectLiteral } from 'typeorm'\nimport { Domain } from '@things-factory/shell'\n\nimport { CLAIMED_FIELDS, claimedField } from './master-value-claim'\nimport { MASTER_AXES } from '../master-axis'\nimport { recordClaim } from './assignment-history'\n\n/**\n * 그리드가 보낸 변경에서 **배정·선언 칸을 골라 주장으로 남긴다.**\n *\n * ── 왜 본행을 되돌려 놓나 ──────────────────────────────────────────────────\n * 적용 시각이 미래면 **본행은 그대로여야 한다.** 그리드는 값을 보냈지만 그것은 「다음 달부터」라는\n * 뜻이고, 오늘의 값이 아니다. 그래서 `keepOnRow` 로 옛 값을 되돌려 저장에 얹는다 — 도래하면\n * `projectDueClaims` 가 옮긴다.\n *\n * ── 마스터 변경과 한 트랜잭션이다 ──────────────────────────────────────────\n * 부르는 쪽이 `saveMasterPatches` 안이므로 같은 `manager` 다. 갈라지면 값은 바뀌고 이력은 없는\n * 상태가 생긴다.\n */\nexport async function claimAssignmentChanges(\n manager: EntityManager,\n domain: Domain,\n axis: string,\n found: ObjectLiteral,\n values: ObjectLiteral,\n user: unknown,\n options: { appliedAt?: Date; appliedKind?: 'change' | 'correction'; appliedUntil?: Date }\n): Promise<{ keepOnRow: ObjectLiteral }> {\n const keepOnRow: ObjectLiteral = {}\n const fields = CLAIMED_FIELDS.filter(one => one.axis === axis)\n\n if (!fields.length) {\n return { keepOnRow }\n }\n\n const now = new Date()\n const appliedAt = options.appliedAt ?? now\n const future = appliedAt.getTime() > now.getTime()\n\n for (const declared of fields) {\n if (!(declared.field in values)) {\n continue\n }\n\n const next = values[declared.field]\n\n /* 안 바뀐 칸은 주장을 남기지 않는다 — 같은 값을 다시 적으면 이력이 뜻 없이 길어진다. */\n if (sameValue(found[declared.field], next)) {\n continue\n }\n\n await recordClaim(\n manager,\n domain,\n {\n target: { axis, rowId: String(found.id), field: declared.field },\n value: await toClaimValue(manager, domain, declared, next),\n /* 바뀌기 전 값 — 첫 주장이면 그 앞을 덮는 줄이 같이 선다(§`recordClaim`). */\n previous: await toClaimValue(manager, domain, declared, found[declared.field]),\n validFrom: appliedAt,\n validTo: options.appliedUntil,\n reason: options.appliedKind === 'correction' ? 'correction' : 'change',\n recorder: user\n },\n now\n )\n\n if (future) {\n keepOnRow[declared.field] = found[declared.field] ?? null\n }\n }\n\n return { keepOnRow }\n}\n\n/**\n * 마스터 칸의 값 → 주장의 값. 「선언 없음」은 `null` 이다.\n *\n * ⚠ **관계 칸은 UUID 가 아니라 이름을 담는다.** `ops_equipment.home_location` 에 `UNCOIL-01` 이\n * 들어 있고, `parentId` 는 id 일 수도 이름일 수도 있다(이 제품의 규약). 처음에 칸 값을 그대로\n * 식별자로 썼다가 **정상 편집이 전부 거절됐다**(2026-09-22, 화면에서 드러남).\n *\n * 그래서 여기서 **가리키는 행을 찾아 식별자와 그때의 이름을 함께 담는다.** 마스터 칸은 그대로\n * 이름을 들고, 주장은 이름이 바뀌어도 안 끊긴다.\n */\nexport async function toClaimValue(\n manager: EntityManager,\n domain: Domain,\n declared: { kind: 'relation' | 'names' | 'literal'; target?: string },\n value: unknown\n): Promise<Record<string, unknown> | null> {\n if (value === null || value === undefined || value === '') {\n return null\n }\n\n /* 값 그 자체인 칸 — 가리키는 것이 없으므로 푸는 일도 없다. */\n if (declared.kind === 'literal') {\n return { literal: String(value) }\n }\n\n if (declared.kind === 'names') {\n return { names: Array.isArray(value) ? value.map(String) : [String(value)] }\n }\n\n const found = await rowOf(manager, domain, declared.target, String(value))\n\n /* 못 찾으면 그대로 담는다 — 검증이 그 다음에 거절한다. 여기서 지어내지 않는다. */\n return found ? { ref: found.id, label: found.name } : { ref: String(value) }\n}\n\n/** 이름으로도 id 로도 찾는다 — 이 제품의 관계 칸이 둘 다 담는다. */\nexport async function rowOf(\n manager: EntityManager,\n domain: Domain,\n targetAxis: string | undefined,\n given: string\n): Promise<{ id: string; name: string } | undefined> {\n const axis = MASTER_AXES.find(one => one.key === targetAxis)\n\n if (!axis) {\n return undefined\n }\n\n const byId = UUID.test(given)\n ? await manager.findOne(axis.entity as never, { where: { id: given, domain: { id: domain.id } } } as never)\n : undefined\n\n const found = byId ?? (await manager.findOne(axis.entity as never, { where: { name: given, domain: { id: domain.id } } } as never))\n\n return found as unknown as { id: string; name: string } | undefined\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nfunction sameValue(before: unknown, after: unknown): boolean {\n if (Array.isArray(before) || Array.isArray(after)) {\n return JSON.stringify(before ?? null) === JSON.stringify(after ?? null)\n }\n\n return (before ?? null) === (after ?? null)\n}\n\nexport { claimedField }\n"]}
@@ -0,0 +1,135 @@
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 MasterValueClaim {
42
+ readonly id: string;
43
+ domain?: Domain;
44
+ domainId?: string;
45
+ /** 어느 축의 무엇인가 — `equipment` · `operationalLocation`. */
46
+ axis: string;
47
+ /** 그 축의 어느 행인가. UUID 다. */
48
+ rowId: string;
49
+ /** 어느 칸인가 — 선언한 셋만 온다(§`CLAIMED_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
+ * 언제부터 그러했나 — 업무의 시간.
63
+ *
64
+ * **비어 있으면 「그때까지 쭉」이다.** 어느 칸이 처음 바뀔 때, 바뀌기 전 값도 같이 적는데 그 값이
65
+ * 언제부터였는지는 아무도 안 적었다. 1970 년 같은 시각을 넣으면 적은 적 없는 것을 적은 것이 되므로
66
+ * 비워 둔다(2026-09-23).
67
+ */
68
+ validFrom?: Date;
69
+ /** 언제까지. 비면 아직 끝나지 않았다. */
70
+ validTo?: Date;
71
+ /** 언제 그렇게 적었나. 서버가 붙인다 — 사람이 넣지 않는다. */
72
+ recordedAt: Date;
73
+ /** 언제 물러났나. 비면 지금도 서 있는 주장이다. */
74
+ supersededAt?: Date;
75
+ /**
76
+ * 왜 이 주장이 섰나 — 읽는 사람이 「지연 기록인가 정정인가」를 알아야 한다.
77
+ *
78
+ * ```
79
+ * initial 도입 시각에 확인된 초기 상태
80
+ * change 바꾼다는 선언
81
+ * correction 이미 적은 것을 고친다
82
+ * remainder 정정이 남긴 앞뒤 조각
83
+ * ```
84
+ */
85
+ reason: string;
86
+ recorder?: User;
87
+ recorderId?: string;
88
+ createdAt?: Date;
89
+ }
90
+ /**
91
+ * **이력을 받는 칸은 여기 적힌 것뿐이다.**
92
+ *
93
+ * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이
94
+ * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.
95
+ *
96
+ * ── 셋에서 넷이 됐다 (2026-09-23) ─────────────────────────────────────────
97
+ * 앞의 셋은 **배정**이다 — 무엇이 어디에 속하나. 넷째는 아니다. 공정의 표준시간은 값 자체이고,
98
+ * 실적의 성능률이 그것을 나눈다. 지금은 **오늘 마스터를 다시 읽어서 지난달 성능이 움직인다**
99
+ * (`operation-effectivity-db.test.cjs` 가 그 거동을 적어 두고 있다).
100
+ *
101
+ * 그래서 이 표의 이름이 「배정」이 아니라 「마스터 값의 주장」이다. 기전은 같다 — 두 시각, 정정은
102
+ * 물리고 앞뒤를 다시 주장, 주장이 없으면 한 번도 안 바뀐 것.
103
+ */
104
+ export declare const CLAIMED_FIELDS: ({
105
+ axis: string;
106
+ field: string;
107
+ kind: "relation";
108
+ target: string;
109
+ } | {
110
+ axis: string;
111
+ field: string;
112
+ kind: "names";
113
+ target?: undefined;
114
+ } | {
115
+ axis: string;
116
+ field: string;
117
+ kind: "literal";
118
+ target?: undefined;
119
+ })[];
120
+ export declare function claimedField(axis: string, field: string): {
121
+ axis: string;
122
+ field: string;
123
+ kind: "relation";
124
+ target: string;
125
+ } | {
126
+ axis: string;
127
+ field: string;
128
+ kind: "names";
129
+ target?: undefined;
130
+ } | {
131
+ axis: string;
132
+ field: string;
133
+ kind: "literal";
134
+ target?: undefined;
135
+ };
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLAIMED_FIELDS = exports.MasterValueClaim = void 0;
4
+ exports.claimedField = claimedField;
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 MasterValueClaim = class MasterValueClaim {
49
+ };
50
+ exports.MasterValueClaim = MasterValueClaim;
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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.prototype, "domain", void 0);
61
+ tslib_1.__decorate([
62
+ (0, typeorm_1.RelationId)((claim) => claim.domain),
63
+ tslib_1.__metadata("design:type", String)
64
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.prototype, "value", void 0);
85
+ tslib_1.__decorate([
86
+ (0, typeorm_1.Column)({ nullable: true }),
87
+ (0, type_graphql_1.Field)({ nullable: true }),
88
+ tslib_1.__metadata("design:type", Date
89
+ /** 언제까지. 비면 아직 끝나지 않았다. */
90
+ )
91
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.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
+ ], MasterValueClaim.prototype, "recorder", void 0);
132
+ tslib_1.__decorate([
133
+ (0, typeorm_1.RelationId)((claim) => claim.recorder),
134
+ tslib_1.__metadata("design:type", String)
135
+ ], MasterValueClaim.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
+ ], MasterValueClaim.prototype, "createdAt", void 0);
141
+ exports.MasterValueClaim = MasterValueClaim = tslib_1.__decorate([
142
+ (0, typeorm_1.Entity)({ name: 'ops_master_value_claim' }),
143
+ (0, typeorm_1.Index)('ix_ops_master_value_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
+ ], MasterValueClaim);
146
+ /**
147
+ * **이력을 받는 칸은 여기 적힌 것뿐이다.**
148
+ *
149
+ * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이
150
+ * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.
151
+ *
152
+ * ── 셋에서 넷이 됐다 (2026-09-23) ─────────────────────────────────────────
153
+ * 앞의 셋은 **배정**이다 — 무엇이 어디에 속하나. 넷째는 아니다. 공정의 표준시간은 값 자체이고,
154
+ * 실적의 성능률이 그것을 나눈다. 지금은 **오늘 마스터를 다시 읽어서 지난달 성능이 움직인다**
155
+ * (`operation-effectivity-db.test.cjs` 가 그 거동을 적어 두고 있다).
156
+ *
157
+ * 그래서 이 표의 이름이 「배정」이 아니라 「마스터 값의 주장」이다. 기전은 같다 — 두 시각, 정정은
158
+ * 물리고 앞뒤를 다시 주장, 주장이 없으면 한 번도 안 바뀐 것.
159
+ */
160
+ exports.CLAIMED_FIELDS = [
161
+ { axis: 'equipment', field: 'homeLocation', kind: 'relation', target: 'operationalLocation' },
162
+ { axis: 'operationalLocation', field: 'workShiftNames', kind: 'names' },
163
+ { axis: 'operationalLocation', field: 'parentId', kind: 'relation', target: 'operationalLocation' },
164
+ /*
165
+ * ISO 8601 기간 문자열 그대로 담는다(`PT1M`). ms 로 바꿔 담으면 사람이 읽을 때 원문이 없고,
166
+ * 원문만 담으면 파싱 규칙이 바뀌는 날 값이 달라진다 — **읽는 쪽이 둘 다 내도록** 원문을 담고
167
+ * 부르는 쪽이 파싱한다(착수 카드 §3-1).
168
+ */
169
+ { axis: 'operationDefinition', field: 'duration', kind: 'literal' }
170
+ ];
171
+ function claimedField(axis, field) {
172
+ return exports.CLAIMED_FIELDS.find(one => one.axis === axis && one.field === field);
173
+ }
174
+ //# sourceMappingURL=master-value-claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"master-value-claim.js","sourceRoot":"","sources":["../../../server/service/assignment/master-value-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_master_value_claim' })\n@Index('ix_ops_master_value_claim_0', (claim: MasterValueClaim) => [claim.domain, claim.axis, claim.rowId, claim.field])\n@ObjectType({ description: 'what an assignment or declaration was, from when, as recorded when' })\nexport class MasterValueClaim {\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: MasterValueClaim) => 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 /** 어느 칸인가 — 선언한 셋만 온다(§`CLAIMED_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 * 언제부터 그러했나 — 업무의 시간.\n *\n * **비어 있으면 「그때까지 쭉」이다.** 어느 칸이 처음 바뀔 때, 바뀌기 전 값도 같이 적는데 그 값이\n * 언제부터였는지는 아무도 안 적었다. 1970 년 같은 시각을 넣으면 적은 적 없는 것을 적은 것이 되므로\n * 비워 둔다(2026-09-23).\n */\n @Column({ nullable: true })\n @Field({ nullable: true })\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: MasterValueClaim) => 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 *\n * ── 셋에서 넷이 됐다 (2026-09-23) ─────────────────────────────────────────\n * 앞의 셋은 **배정**이다 — 무엇이 어디에 속하나. 넷째는 아니다. 공정의 표준시간은 값 자체이고,\n * 실적의 성능률이 그것을 나눈다. 지금은 **오늘 마스터를 다시 읽어서 지난달 성능이 움직인다**\n * (`operation-effectivity-db.test.cjs` 가 그 거동을 적어 두고 있다).\n *\n * 그래서 이 표의 이름이 「배정」이 아니라 「마스터 값의 주장」이다. 기전은 같다 — 두 시각, 정정은\n * 물리고 앞뒤를 다시 주장, 주장이 없으면 한 번도 안 바뀐 것.\n */\nexport const CLAIMED_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 * ISO 8601 기간 문자열 그대로 담는다(`PT1M`). ms 로 바꿔 담으면 사람이 읽을 때 원문이 없고,\n * 원문만 담으면 파싱 규칙이 바뀌는 날 값이 달라진다 — **읽는 쪽이 둘 다 내도록** 원문을 담고\n * 부르는 쪽이 파싱한다(착수 카드 §3-1).\n */\n { axis: 'operationDefinition', field: 'duration', kind: 'literal' as const }\n]\n\nexport function claimedField(axis: string, field: string) {\n return CLAIMED_FIELDS.find(one => one.axis === axis && one.field === field)\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { AppliedWhen } from '../assignment/applied-when';
1
2
  import { ListParam } from '@things-factory/shell';
2
3
  import { MasterPatch } from '../master-crud';
3
4
  import { EquipmentClass } from './equipment-class';
@@ -50,7 +51,14 @@ export declare class EquipmentResolver {
50
51
  * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
51
52
  * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
52
53
  */
53
- updateMultipleEquipment(patches: MasterPatch[], context: ResolverContext): Promise<Equipment[]>;
54
+ updateMultipleEquipment(patches: MasterPatch[], context: ResolverContext,
55
+ /**
56
+ * **언제부터 그러한가** — 배정·선언 칸을 고칠 때만 온다.
57
+ *
58
+ * 안 오면 지금부터다. 미래면 주장만 남고 본행은 그 시각이 올 때까지 그대로다
59
+ * (§`claimAssignmentChanges`).
60
+ */
61
+ applied?: AppliedWhen): Promise<Equipment[]>;
54
62
  }
55
63
  export declare class StopReasonList {
56
64
  items: StopReason[];
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StopReasonResolver = exports.StopReasonList = exports.EquipmentResolver = exports.EquipmentList = exports.EquipmentClassResolver = exports.EquipmentClassList = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
+ const applied_when_1 = require("../assignment/applied-when");
6
7
  const shell_1 = require("@things-factory/shell");
7
8
  const type_graphql_2 = require("type-graphql");
8
9
  const state_period_rules_js_1 = require("./state-period-rules.js");
@@ -153,11 +154,12 @@ let EquipmentResolver = class EquipmentResolver {
153
154
  * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
154
155
  * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
155
156
  */
156
- async updateMultipleEquipment(patches, context) {
157
+ async updateMultipleEquipment(patches, context, applied) {
157
158
  const { domain, user, tx } = context.state;
158
159
  const axis = (0, master_axis_1.masterAxisOf)('equipment');
159
160
  const saved = await (0, master_crud_1.saveMasterPatches)(tx, domain, axis.entity, patches, user, {
160
- hasEffectivePeriod: axis.hasEffectivePeriod
161
+ hasEffectivePeriod: axis.hasEffectivePeriod,
162
+ ...(0, applied_when_1.appliedOptions)(applied)
161
163
  });
162
164
  return saved;
163
165
  }
@@ -178,8 +180,9 @@ tslib_1.__decorate([
178
180
  (0, type_graphql_2.Mutation)(returns => [equipment_1.Equipment], { description: 'To create, change or retire equipment' }),
179
181
  tslib_1.__param(0, (0, type_graphql_2.Arg)('patches', type => [shell_2.ScalarObject])),
180
182
  tslib_1.__param(1, (0, type_graphql_2.Ctx)()),
183
+ tslib_1.__param(2, (0, type_graphql_2.Arg)('applied', type => shell_2.ScalarObject, { nullable: true })),
181
184
  tslib_1.__metadata("design:type", Function),
182
- tslib_1.__metadata("design:paramtypes", [Array, Object]),
185
+ tslib_1.__metadata("design:paramtypes", [Array, Object, Object]),
183
186
  tslib_1.__metadata("design:returntype", Promise)
184
187
  ], EquipmentResolver.prototype, "updateMultipleEquipment", null);
185
188
  exports.EquipmentResolver = EquipmentResolver = tslib_1.__decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"equipment-master-resolver.js","sourceRoot":"","sources":["../../../server/service/equipment/equipment-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { UseMiddleware } from 'type-graphql'\nimport { SpeakRefusals } from '@things-factory/shell'\nimport { FieldResolver, Root, Args, Arg, Ctx, Directive, Int, Mutation, ObjectType, Field, Query, Resolver } from 'type-graphql'\nimport { stopReasonStatusGap } from './state-period-rules.js'\n\nimport { Domain, ListParam, ScalarObject } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { ClassMembers, scanClassMembers } from '../resource/class-members'\nimport { EquipmentClass } from './equipment-class'\nimport { Equipment } from './equipment'\nimport { StopReason } from './stop-reason'\n\n/**\n * 설비 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of equipment classes' })\nexport class EquipmentClassList {\n @Field(type => [EquipmentClass])\n items: EquipmentClass[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * **이 등급에 무엇이 드나** — 들어오는 관계.\n *\n * 등급을 만들어 놓고 아무도 안 들어 있는 일이 실제로 있었다. 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다. 없앨 때 무엇이 딸려 자격을 잃는지도 알 수 없었다.\n *\n * 세는 방식과 상한은 `class-members.ts` 머리말에 적었다 — 등급 넷이 같은 함수를 쓴다.\n */\nfunction equipmentClassMembersOf(context: ResolverContext): Promise<ClassMembers> {\n const state = context.state as { domain: Domain; equipmentClassMembers?: Promise<ClassMembers> }\n\n state.equipmentClassMembers = state.equipmentClassMembers || scanClassMembers(Equipment, 'equipmentClassIds', state.domain)\n\n return state.equipmentClassMembers\n}\n\n@Resolver(EquipmentClass)\n@UseMiddleware(SpeakRefusals)\nexport class EquipmentClassResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EquipmentClassList, { description: 'To fetch equipment classes' })\n async equipmentClasses(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EquipmentClassList> {\n const { domain } = context.state\n const { items, total } = await listMaster('equipmentClass', params, domain)\n\n return { items: items as EquipmentClass[], total }\n }\n\n /**\n * 이 등급에 든 설비들\n *\n * 아무도 안 들면 빈 목록이다. 다만 그것이 「없다」인지 「다 못 봤다」인지는 `memberScanCapped`\n * 가 말한다 — 조용히 자르면 고쳐야 할 선언과 멀쩡한 등급이 같아 보인다.\n */\n @FieldResolver(type => [String], { description: 'Equipment belonging to this class' })\n async members(@Root() klass: EquipmentClass, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await equipmentClassMembersOf(context)).members.get(klass.name) ?? []\n }\n\n /** 소속을 다 보지 못했나. */\n @FieldResolver(type => Boolean, { description: 'True when members were too many to scan fully' })\n async memberScanCapped(@Root() klass: EquipmentClass, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await equipmentClassMembersOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [EquipmentClass], { description: 'To create, change or retire equipment classes' })\n async updateMultipleEquipmentClass(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<EquipmentClass[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('equipmentClass')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as EquipmentClass[]\n }\n}\n\n@ObjectType({ description: 'A page of equipment' })\nexport class EquipmentList {\n @Field(type => [Equipment])\n items: Equipment[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(Equipment)\n@UseMiddleware(SpeakRefusals)\nexport class EquipmentResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EquipmentList, { description: 'To fetch equipment' })\n async equipmentList(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EquipmentList> {\n const { domain } = context.state\n const { items, total } = await listMaster('equipment', params, domain)\n\n return { items: items as Equipment[], 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 => [Equipment], { description: 'To create, change or retire equipment' })\n async updateMultipleEquipment(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<Equipment[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('equipment')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as Equipment[]\n }\n}\n\n@ObjectType({ description: 'A page of stop reasons' })\nexport class StopReasonList {\n @Field(type => [StopReason])\n items: StopReason[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(StopReason)\n@UseMiddleware(SpeakRefusals)\nexport class StopReasonResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => StopReasonList, { description: 'To fetch stop reasons' })\n async stopReasonList(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<StopReasonList> {\n const { domain } = context.state\n const { items, total } = await listMaster('stopReason', params, domain)\n\n return { items: items as StopReason[], 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 => [StopReason], { description: 'To create, change or retire stop reasons' })\n async updateMultipleStopReason(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<StopReason[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('stopReason')\n\n /*\n * **모르는 상태로는 저장하지 않는다.** 이 값이 가동률을 깎느냐 마느냐를 정하므로, 목록 밖 값이\n * 들어오면 그 사유를 쓴 구간이 전부 어긋남으로 걸린다 — 잘못은 여기서 났는데 불은 거기서 켜진다.\n *\n * 지우는 패치(`retire`)는 상태를 다시 말하지 않으므로 값이 있을 때만 본다.\n */\n for (const patch of patches) {\n const said = (patch as any)?.status\n if (said === undefined || said === null) continue\n\n const gap = stopReasonStatusGap(said)\n if (gap) throw new Error(`정지 사유 \"${(patch as any)?.name ?? ''}\" — ${gap}`)\n }\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as StopReason[]\n }\n}\n"]}
1
+ {"version":3,"file":"equipment-master-resolver.js","sourceRoot":"","sources":["../../../server/service/equipment/equipment-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { UseMiddleware } from 'type-graphql'\nimport { AppliedWhen, appliedOptions } from '../assignment/applied-when'\nimport { SpeakRefusals } from '@things-factory/shell'\nimport { FieldResolver, Root, Args, Arg, Ctx, Directive, Int, Mutation, ObjectType, Field, Query, Resolver } from 'type-graphql'\nimport { stopReasonStatusGap } from './state-period-rules.js'\n\nimport { Domain, ListParam, ScalarObject } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { ClassMembers, scanClassMembers } from '../resource/class-members'\nimport { EquipmentClass } from './equipment-class'\nimport { Equipment } from './equipment'\nimport { StopReason } from './stop-reason'\n\n/**\n * 설비 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n */\n\n@ObjectType({ description: 'A page of equipment classes' })\nexport class EquipmentClassList {\n @Field(type => [EquipmentClass])\n items: EquipmentClass[]\n\n @Field(type => Int)\n total: number\n}\n\n/**\n * **이 등급에 무엇이 드나** — 들어오는 관계.\n *\n * 등급을 만들어 놓고 아무도 안 들어 있는 일이 실제로 있었다. 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다. 없앨 때 무엇이 딸려 자격을 잃는지도 알 수 없었다.\n *\n * 세는 방식과 상한은 `class-members.ts` 머리말에 적었다 — 등급 넷이 같은 함수를 쓴다.\n */\nfunction equipmentClassMembersOf(context: ResolverContext): Promise<ClassMembers> {\n const state = context.state as { domain: Domain; equipmentClassMembers?: Promise<ClassMembers> }\n\n state.equipmentClassMembers = state.equipmentClassMembers || scanClassMembers(Equipment, 'equipmentClassIds', state.domain)\n\n return state.equipmentClassMembers\n}\n\n@Resolver(EquipmentClass)\n@UseMiddleware(SpeakRefusals)\nexport class EquipmentClassResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EquipmentClassList, { description: 'To fetch equipment classes' })\n async equipmentClasses(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EquipmentClassList> {\n const { domain } = context.state\n const { items, total } = await listMaster('equipmentClass', params, domain)\n\n return { items: items as EquipmentClass[], total }\n }\n\n /**\n * 이 등급에 든 설비들\n *\n * 아무도 안 들면 빈 목록이다. 다만 그것이 「없다」인지 「다 못 봤다」인지는 `memberScanCapped`\n * 가 말한다 — 조용히 자르면 고쳐야 할 선언과 멀쩡한 등급이 같아 보인다.\n */\n @FieldResolver(type => [String], { description: 'Equipment belonging to this class' })\n async members(@Root() klass: EquipmentClass, @Ctx() context: ResolverContext): Promise<string[]> {\n return (await equipmentClassMembersOf(context)).members.get(klass.name) ?? []\n }\n\n /** 소속을 다 보지 못했나. */\n @FieldResolver(type => Boolean, { description: 'True when members were too many to scan fully' })\n async memberScanCapped(@Root() klass: EquipmentClass, @Ctx() context: ResolverContext): Promise<boolean> {\n return (await equipmentClassMembersOf(context)).capped\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [EquipmentClass], { description: 'To create, change or retire equipment classes' })\n async updateMultipleEquipmentClass(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<EquipmentClass[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('equipmentClass')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as EquipmentClass[]\n }\n}\n\n@ObjectType({ description: 'A page of equipment' })\nexport class EquipmentList {\n @Field(type => [Equipment])\n items: Equipment[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(Equipment)\n@UseMiddleware(SpeakRefusals)\nexport class EquipmentResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EquipmentList, { description: 'To fetch equipment' })\n async equipmentList(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EquipmentList> {\n const { domain } = context.state\n const { items, total } = await listMaster('equipment', params, domain)\n\n return { items: items as Equipment[], 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 => [Equipment], { description: 'To create, change or retire equipment' })\n async updateMultipleEquipment(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext,\n /**\n * **언제부터 그러한가** — 배정·선언 칸을 고칠 때만 온다.\n *\n * 안 오면 지금부터다. 미래면 주장만 남고 본행은 그 시각이 올 때까지 그대로다\n * (§`claimAssignmentChanges`).\n */\n @Arg('applied', type => ScalarObject, { nullable: true }) applied?: AppliedWhen\n ): Promise<Equipment[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('equipment')\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod,\n ...appliedOptions(applied)\n })\n\n return saved as Equipment[]\n }\n}\n\n@ObjectType({ description: 'A page of stop reasons' })\nexport class StopReasonList {\n @Field(type => [StopReason])\n items: StopReason[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(StopReason)\n@UseMiddleware(SpeakRefusals)\nexport class StopReasonResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => StopReasonList, { description: 'To fetch stop reasons' })\n async stopReasonList(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<StopReasonList> {\n const { domain } = context.state\n const { items, total } = await listMaster('stopReason', params, domain)\n\n return { items: items as StopReason[], 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 => [StopReason], { description: 'To create, change or retire stop reasons' })\n async updateMultipleStopReason(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<StopReason[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('stopReason')\n\n /*\n * **모르는 상태로는 저장하지 않는다.** 이 값이 가동률을 깎느냐 마느냐를 정하므로, 목록 밖 값이\n * 들어오면 그 사유를 쓴 구간이 전부 어긋남으로 걸린다 — 잘못은 여기서 났는데 불은 거기서 켜진다.\n *\n * 지우는 패치(`retire`)는 상태를 다시 말하지 않으므로 값이 있을 때만 본다.\n */\n for (const patch of patches) {\n const said = (patch as any)?.status\n if (said === undefined || said === null) continue\n\n const gap = stopReasonStatusGap(said)\n if (gap) throw new Error(`정지 사유 \"${(patch as any)?.name ?? ''}\" — ${gap}`)\n }\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, patches, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as StopReason[]\n }\n}\n"]}
@@ -10,6 +10,7 @@ export * from './test-spec/test-spec-master-resolver';
10
10
  export * from './master-crud';
11
11
  export * from './master-axis';
12
12
  export * from './master-change-policy';
13
+ export * from './approval-route';
13
14
  export * from './master-conformance';
14
15
  export * from './master-conformance-resolver';
15
16
  export * from './master-list';
@@ -67,7 +68,11 @@ export * from './status/master-status';
67
68
  export * from './quality/nonconformance-cause';
68
69
  import { MasterConformanceResolver } from './master-conformance-resolver';
69
70
  import { MasterChangePolicy, MasterChangePolicyResolver } from './master-change-policy';
70
- export declare const entities: (typeof import("./equipment/equipment").Equipment | typeof import("./equipment/equipment-class").EquipmentClass | typeof import("./quality/nonconformance-cause").NonconformanceCause | typeof import("./material/material-definition").MaterialDefinition | typeof import("./operation/operation-definition").OperationDefinition | typeof import("./location/operational-location").OperationalLocation | typeof import("./personnel/person").Person | typeof import("./asset/physical-asset").PhysicalAsset | typeof import("./recipe").RecipeDefinition | typeof import("./recipe").RouteDefinition | typeof import("./test-spec/test-specification").TestSpecification | typeof MasterChangePolicy | typeof import("./recipe/recipe-revision-line").RecipeRevisionOutput | typeof import("./recipe/recipe-revision").RecipeRevision | typeof import("./recipe/recipe-revision").RecipePlan | typeof import("./recipe/recipe-revision").RecipeSegment | typeof import("./recipe/recipe-revision").RecipeBasis | typeof import("./material/material-revision").MaterialDefinitionRevision | typeof import("./material/material-revision").MaterialDefinitionPlan | typeof import("./material/material-revision").MaterialDefinitionSegment | typeof import("./material/material-revision").MaterialDefinitionBasis | typeof import("./test-spec/test-spec-revision").TestSpecificationRevision | typeof import("./test-spec/test-spec-revision").TestSpecificationPlan | typeof import("./test-spec/test-spec-revision").TestSpecificationSegment | typeof import("./test-spec/test-spec-revision").TestSpecificationBasis)[];
71
+ import { MasterValueClaim } from './assignment/master-value-claim';
72
+ export declare const entities: (typeof import("./equipment/equipment").Equipment | typeof import("./equipment/equipment-class").EquipmentClass | typeof import("./quality/nonconformance-cause").NonconformanceCause | typeof import("./material/material-definition").MaterialDefinition | typeof import("./operation/operation-definition").OperationDefinition | typeof import("./location/operational-location").OperationalLocation | typeof import("./personnel/person").Person | typeof import("./asset/physical-asset").PhysicalAsset | typeof import("./recipe").RecipeDefinition | typeof import("./recipe").RouteDefinition | typeof import("./test-spec/test-specification").TestSpecification | typeof MasterChangePolicy | typeof MasterValueClaim | typeof import("./recipe/recipe-revision-line").RecipeRevisionOutput | typeof import("./recipe/recipe-revision").RecipeRevision | typeof import("./recipe/recipe-revision").RecipePlan | typeof import("./recipe/recipe-revision").RecipeSegment | typeof import("./recipe/recipe-revision").RecipeBasis | typeof import("./material/material-revision").MaterialDefinitionRevision | typeof import("./material/material-revision").MaterialDefinitionPlan | typeof import("./material/material-revision").MaterialDefinitionSegment | typeof import("./material/material-revision").MaterialDefinitionBasis | typeof import("./test-spec/test-spec-revision").TestSpecificationRevision | typeof import("./test-spec/test-spec-revision").TestSpecificationPlan | typeof import("./test-spec/test-spec-revision").TestSpecificationSegment | typeof import("./test-spec/test-spec-revision").TestSpecificationBasis)[];
71
73
  export declare const schema: {
72
74
  resolverClasses: (typeof MasterChangePolicyResolver | typeof import("./personnel/personnel-master-resolver").PersonnelClassResolver | typeof import("./personnel/personnel-master-resolver").PersonResolver | typeof import("./equipment/equipment-master-resolver").EquipmentClassResolver | typeof import("./equipment/equipment-master-resolver").EquipmentResolver | typeof import("./equipment/equipment-master-resolver").StopReasonResolver | typeof import("./asset/asset-master-resolver").PhysicalAssetClassResolver | typeof import("./asset/asset-master-resolver").PhysicalAssetResolver | typeof import("./material/material-master-resolver").MaterialClassResolver | typeof import("./material/material-master-resolver").MaterialDefinitionResolver | typeof import("./location/location-master-resolver").OperationalLocationResolver | typeof import("./operation/operation-master-resolver").OperationDefinitionResolver | typeof import("./recipe/recipe-master-resolver").RecipeDefinitionResolver | typeof import("./recipe/recipe-master-resolver").RouteDefinitionResolver | typeof import("./test-spec/test-spec-master-resolver").TestSpecificationResolver | typeof MasterConformanceResolver | typeof import("./personnel/personnel-query").PersonnelQuery | typeof import("./equipment/equipment-query").EquipmentQuery | typeof import("./asset/asset-query").AssetQuery | typeof import("./location/location-query").LocationQuery | typeof import("./material/material-revision-resolver").MaterialDefinitionRevisionResolver | typeof import("./material/material-query").MaterialQuery | typeof import("./operation/operation-query").OperationQuery | typeof import("./operation/operation-readiness-resolver").OperationReadinessResolver | typeof import("./recipe/recipe-revision-resolver").RecipeRevisionResolver | typeof import("./recipe/recipe-query").RecipeQuery | typeof import("./test-spec/test-spec-revision-resolver").TestSpecificationRevisionResolver | typeof import("./test-spec/test-spec-query").TestSpecificationQuery | typeof import("./status/master-status").OpsMasterStatusQuery | typeof import("./equipment/stop-reason-query").StopReasonQuery | typeof import("./quality/nonconformance-cause-resolver").NonconformanceCauseResolver)[];
73
75
  };
76
+ export * from './assignment/master-value-claim';
77
+ export * from './assignment/assignment-history';
78
+ export * from './assignment/assignment-write';