@things-factory/ops-master 10.1.36 → 10.1.38

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 (81) hide show
  1. package/dist-client/bootstrap.d.ts +1 -0
  2. package/dist-client/bootstrap.js +12 -0
  3. package/dist-client/bootstrap.js.map +1 -1
  4. package/dist-client/master-change-policy-axes.d.ts +43 -0
  5. package/dist-client/master-change-policy-axes.js +35 -0
  6. package/dist-client/master-change-policy-axes.js.map +1 -0
  7. package/dist-client/master-change-policy-panel.d.ts +59 -0
  8. package/dist-client/master-change-policy-panel.js +452 -0
  9. package/dist-client/master-change-policy-panel.js.map +1 -0
  10. package/dist-client/pages/master/applied-when-dialog.d.ts +2 -2
  11. package/dist-client/pages/master/applied-when-dialog.js +2 -2
  12. package/dist-client/pages/master/applied-when-dialog.js.map +1 -1
  13. package/dist-client/pages/master/assignment-fields.d.ts +8 -9
  14. package/dist-client/pages/master/assignment-fields.js +25 -18
  15. package/dist-client/pages/master/assignment-fields.js.map +1 -1
  16. package/dist-client/pages/master/master-page-base.d.ts +0 -2
  17. package/dist-client/pages/master/master-page-base.js +1 -3
  18. package/dist-client/pages/master/master-page-base.js.map +1 -1
  19. package/dist-client/tsconfig.tsbuildinfo +1 -1
  20. package/dist-server/index.js +10 -0
  21. package/dist-server/index.js.map +1 -1
  22. package/dist-server/service/approval-route.d.ts +37 -0
  23. package/dist-server/service/approval-route.js +34 -0
  24. package/dist-server/service/approval-route.js.map +1 -0
  25. package/dist-server/service/assignment/assignment-history.d.ts +21 -16
  26. package/dist-server/service/assignment/assignment-history.js +91 -52
  27. package/dist-server/service/assignment/assignment-history.js.map +1 -1
  28. package/dist-server/service/assignment/assignment-write.d.ts +21 -4
  29. package/dist-server/service/assignment/assignment-write.js +41 -9
  30. package/dist-server/service/assignment/assignment-write.js.map +1 -1
  31. package/dist-server/service/assignment/{assignment-claim.d.ts → master-value-claim.d.ts} +31 -7
  32. package/dist-server/service/assignment/{assignment-claim.js → master-value-claim.js} +45 -31
  33. package/dist-server/service/assignment/master-value-claim.js.map +1 -0
  34. package/dist-server/service/index.d.ts +5 -2
  35. package/dist-server/service/index.js +9 -1
  36. package/dist-server/service/index.js.map +1 -1
  37. package/dist-server/service/master-axis.d.ts +14 -3
  38. package/dist-server/service/master-axis.js +16 -15
  39. package/dist-server/service/master-axis.js.map +1 -1
  40. package/dist-server/service/master-change-policy.d.ts +74 -2
  41. package/dist-server/service/master-change-policy.js +187 -21
  42. package/dist-server/service/master-change-policy.js.map +1 -1
  43. package/dist-server/service/material/material-revision-resolver.d.ts +7 -2
  44. package/dist-server/service/material/material-revision-resolver.js +18 -0
  45. package/dist-server/service/material/material-revision-resolver.js.map +1 -1
  46. package/dist-server/service/material/material-revision-service.d.ts +32 -2
  47. package/dist-server/service/material/material-revision-service.js +129 -0
  48. package/dist-server/service/material/material-revision-service.js.map +1 -1
  49. package/dist-server/service/material/material-revision.d.ts +7 -5
  50. package/dist-server/service/material/material-revision.js +2 -2
  51. package/dist-server/service/material/material-revision.js.map +1 -1
  52. package/dist-server/service/recipe/index.d.ts +1 -1
  53. package/dist-server/service/recipe/recipe-revision-resolver.d.ts +7 -2
  54. package/dist-server/service/recipe/recipe-revision-resolver.js +18 -0
  55. package/dist-server/service/recipe/recipe-revision-resolver.js.map +1 -1
  56. package/dist-server/service/recipe/recipe-revision-service.d.ts +33 -2
  57. package/dist-server/service/recipe/recipe-revision-service.js +139 -0
  58. package/dist-server/service/recipe/recipe-revision-service.js.map +1 -1
  59. package/dist-server/service/recipe/recipe-revision.d.ts +8 -6
  60. package/dist-server/service/recipe/recipe-revision.js +2 -2
  61. package/dist-server/service/recipe/recipe-revision.js.map +1 -1
  62. package/dist-server/service/revision-baseline.d.ts +63 -0
  63. package/dist-server/service/revision-baseline.js +72 -0
  64. package/dist-server/service/revision-baseline.js.map +1 -0
  65. package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +7 -2
  66. package/dist-server/service/test-spec/test-spec-revision-resolver.js +18 -0
  67. package/dist-server/service/test-spec/test-spec-revision-resolver.js.map +1 -1
  68. package/dist-server/service/test-spec/test-spec-revision-service.d.ts +37 -2
  69. package/dist-server/service/test-spec/test-spec-revision-service.js +122 -0
  70. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -1
  71. package/dist-server/service/test-spec/test-spec-revision.d.ts +7 -5
  72. package/dist-server/service/test-spec/test-spec-revision.js +2 -2
  73. package/dist-server/service/test-spec/test-spec-revision.js.map +1 -1
  74. package/dist-server/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +5 -4
  76. package/translations/en.json +63 -2
  77. package/translations/ja.json +62 -1
  78. package/translations/ko.json +63 -2
  79. package/translations/ms.json +62 -1
  80. package/translations/zh.json +64 -1
  81. package/dist-server/service/assignment/assignment-claim.js.map +0 -1
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assignmentField = void 0;
3
+ exports.claimedField = void 0;
4
4
  exports.claimAssignmentChanges = claimAssignmentChanges;
5
5
  exports.toClaimValue = toClaimValue;
6
- const assignment_claim_1 = require("./assignment-claim");
7
- Object.defineProperty(exports, "assignmentField", { enumerable: true, get: function () { return assignment_claim_1.assignmentField; } });
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");
8
10
  const assignment_history_1 = require("./assignment-history");
9
11
  /**
10
12
  * 그리드가 보낸 변경에서 **배정·선언 칸을 골라 주장으로 남긴다.**
@@ -20,7 +22,7 @@ const assignment_history_1 = require("./assignment-history");
20
22
  */
21
23
  async function claimAssignmentChanges(manager, domain, axis, found, values, user, options) {
22
24
  const keepOnRow = {};
23
- const fields = assignment_claim_1.ASSIGNMENT_FIELDS.filter(one => one.axis === axis);
25
+ const fields = master_value_claim_1.CLAIMED_FIELDS.filter(one => one.axis === axis);
24
26
  if (!fields.length) {
25
27
  return { keepOnRow };
26
28
  }
@@ -38,7 +40,9 @@ async function claimAssignmentChanges(manager, domain, axis, found, values, user
38
40
  }
39
41
  await (0, assignment_history_1.recordClaim)(manager, domain, {
40
42
  target: { axis, rowId: String(found.id), field: declared.field },
41
- value: toClaimValue(declared.kind, next),
43
+ value: await toClaimValue(manager, domain, declared, next),
44
+ /* 바뀌기 전 값 — 첫 주장이면 그 앞을 덮는 줄이 같이 선다(§`recordClaim`). */
45
+ previous: await toClaimValue(manager, domain, declared, found[declared.field]),
42
46
  validFrom: appliedAt,
43
47
  validTo: options.appliedUntil,
44
48
  reason: options.appliedKind === 'correction' ? 'correction' : 'change',
@@ -50,16 +54,44 @@ async function claimAssignmentChanges(manager, domain, axis, found, values, user
50
54
  }
51
55
  return { keepOnRow };
52
56
  }
53
- /** 마스터 칸의 값 → 주장의 값. 「선언 없음」은 `null` 이다. */
54
- function toClaimValue(kind, value) {
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) {
55
68
  if (value === null || value === undefined || value === '') {
56
69
  return null;
57
70
  }
58
- if (kind === 'names') {
71
+ /* 값 그 자체인 칸 — 가리키는 것이 없으므로 푸는 일도 없다. */
72
+ if (declared.kind === 'literal') {
73
+ return { literal: String(value) };
74
+ }
75
+ if (declared.kind === 'names') {
59
76
  return { names: Array.isArray(value) ? value.map(String) : [String(value)] };
60
77
  }
61
- return { ref: String(value) };
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;
62
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;
63
95
  function sameValue(before, after) {
64
96
  if (Array.isArray(before) || Array.isArray(after)) {
65
97
  return JSON.stringify(before ?? null) === JSON.stringify(after ?? null);
@@ -1 +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 { ASSIGNMENT_FIELDS, assignmentField } from './assignment-claim'\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 = ASSIGNMENT_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: toClaimValue(declared.kind, next),\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/** 마스터 칸의 값 → 주장의 값. 「선언 없음」은 `null` 이다. */\nexport function toClaimValue(kind: 'relation' | 'names', value: unknown): Record<string, unknown> | null {\n if (value === null || value === undefined || value === '') {\n return null\n }\n\n if (kind === 'names') {\n return { names: Array.isArray(value) ? value.map(String) : [String(value)] }\n }\n\n return { ref: String(value) }\n}\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 { assignmentField }\n"]}
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"]}
@@ -38,7 +38,7 @@ import { User } from '@things-factory/auth-base';
38
38
  * 바꾼 날 과거 관계가 끊긴다(적용 가이드 §0-1 의 그 부류).
39
39
  * ═══════════════════════════════════════════════════════════════════════════
40
40
  */
41
- export declare class AssignmentClaim {
41
+ export declare class MasterValueClaim {
42
42
  readonly id: string;
43
43
  domain?: Domain;
44
44
  domainId?: string;
@@ -46,7 +46,7 @@ export declare class AssignmentClaim {
46
46
  axis: string;
47
47
  /** 그 축의 어느 행인가. UUID 다. */
48
48
  rowId: string;
49
- /** 어느 칸인가 — 선언한 셋만 온다(§`ASSIGNMENT_FIELDS`). */
49
+ /** 어느 칸인가 — 선언한 셋만 온다(§`CLAIMED_FIELDS`). */
50
50
  field: string;
51
51
  /**
52
52
  * 그때의 값.
@@ -58,8 +58,14 @@ export declare class AssignmentClaim {
58
58
  * ```
59
59
  */
60
60
  value?: Record<string, unknown> | null;
61
- /** 언제부터 그러했나 — 업무의 시간. */
62
- validFrom: Date;
61
+ /**
62
+ * 언제부터 그러했나 — 업무의 시간.
63
+ *
64
+ * **비어 있으면 「그때까지 쭉」이다.** 어느 칸이 처음 바뀔 때, 바뀌기 전 값도 같이 적는데 그 값이
65
+ * 언제부터였는지는 아무도 안 적었다. 1970 년 같은 시각을 넣으면 적은 적 없는 것을 적은 것이 되므로
66
+ * 비워 둔다(2026-09-23).
67
+ */
68
+ validFrom?: Date;
63
69
  /** 언제까지. 비면 아직 끝나지 않았다. */
64
70
  validTo?: Date;
65
71
  /** 언제 그렇게 적었나. 서버가 붙인다 — 사람이 넣지 않는다. */
@@ -82,12 +88,20 @@ export declare class AssignmentClaim {
82
88
  createdAt?: Date;
83
89
  }
84
90
  /**
85
- * **이력을 받는 칸은 셋뿐이다.**
91
+ * **이력을 받는 칸은 여기 적힌 것뿐이다.**
86
92
  *
87
93
  * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이
88
94
  * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.
95
+ *
96
+ * ── 셋에서 넷이 됐다 (2026-09-23) ─────────────────────────────────────────
97
+ * 앞의 셋은 **배정**이다 — 무엇이 어디에 속하나. 넷째는 아니다. 공정의 표준시간은 값 자체이고,
98
+ * 실적의 성능률이 그것을 나눈다. 지금은 **오늘 마스터를 다시 읽어서 지난달 성능이 움직인다**
99
+ * (`operation-effectivity-db.test.cjs` 가 그 거동을 적어 두고 있다).
100
+ *
101
+ * 그래서 이 표의 이름이 「배정」이 아니라 「마스터 값의 주장」이다. 기전은 같다 — 두 시각, 정정은
102
+ * 물리고 앞뒤를 다시 주장, 주장이 없으면 한 번도 안 바뀐 것.
89
103
  */
90
- export declare const ASSIGNMENT_FIELDS: ({
104
+ export declare const CLAIMED_FIELDS: ({
91
105
  axis: string;
92
106
  field: string;
93
107
  kind: "relation";
@@ -97,8 +111,13 @@ export declare const ASSIGNMENT_FIELDS: ({
97
111
  field: string;
98
112
  kind: "names";
99
113
  target?: undefined;
114
+ } | {
115
+ axis: string;
116
+ field: string;
117
+ kind: "literal";
118
+ target?: undefined;
100
119
  })[];
101
- export declare function assignmentField(axis: string, field: string): {
120
+ export declare function claimedField(axis: string, field: string): {
102
121
  axis: string;
103
122
  field: string;
104
123
  kind: "relation";
@@ -108,4 +127,9 @@ export declare function assignmentField(axis: string, field: string): {
108
127
  field: string;
109
128
  kind: "names";
110
129
  target?: undefined;
130
+ } | {
131
+ axis: string;
132
+ field: string;
133
+ kind: "literal";
134
+ target?: undefined;
111
135
  };
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ASSIGNMENT_FIELDS = exports.AssignmentClaim = void 0;
4
- exports.assignmentField = assignmentField;
3
+ exports.CLAIMED_FIELDS = exports.MasterValueClaim = void 0;
4
+ exports.claimedField = claimedField;
5
5
  const tslib_1 = require("tslib");
6
6
  const typeorm_1 = require("typeorm");
7
7
  const type_graphql_1 = require("type-graphql");
@@ -45,64 +45,64 @@ const auth_base_1 = require("@things-factory/auth-base");
45
45
  * 바꾼 날 과거 관계가 끊긴다(적용 가이드 §0-1 의 그 부류).
46
46
  * ═══════════════════════════════════════════════════════════════════════════
47
47
  */
48
- let AssignmentClaim = class AssignmentClaim {
48
+ let MasterValueClaim = class MasterValueClaim {
49
49
  };
50
- exports.AssignmentClaim = AssignmentClaim;
50
+ exports.MasterValueClaim = MasterValueClaim;
51
51
  tslib_1.__decorate([
52
52
  (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
53
53
  (0, type_graphql_1.Field)(type => type_graphql_1.ID),
54
54
  tslib_1.__metadata("design:type", String)
55
- ], AssignmentClaim.prototype, "id", void 0);
55
+ ], MasterValueClaim.prototype, "id", void 0);
56
56
  tslib_1.__decorate([
57
57
  (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
58
58
  (0, type_graphql_1.Field)(type => shell_1.Domain),
59
59
  tslib_1.__metadata("design:type", shell_1.Domain)
60
- ], AssignmentClaim.prototype, "domain", void 0);
60
+ ], MasterValueClaim.prototype, "domain", void 0);
61
61
  tslib_1.__decorate([
62
62
  (0, typeorm_1.RelationId)((claim) => claim.domain),
63
63
  tslib_1.__metadata("design:type", String)
64
- ], AssignmentClaim.prototype, "domainId", void 0);
64
+ ], MasterValueClaim.prototype, "domainId", void 0);
65
65
  tslib_1.__decorate([
66
66
  (0, typeorm_1.Column)(),
67
67
  (0, type_graphql_1.Field)(),
68
68
  tslib_1.__metadata("design:type", String)
69
- ], AssignmentClaim.prototype, "axis", void 0);
69
+ ], MasterValueClaim.prototype, "axis", void 0);
70
70
  tslib_1.__decorate([
71
71
  (0, typeorm_1.Column)(),
72
72
  (0, type_graphql_1.Field)(),
73
73
  tslib_1.__metadata("design:type", String)
74
- ], AssignmentClaim.prototype, "rowId", void 0);
74
+ ], MasterValueClaim.prototype, "rowId", void 0);
75
75
  tslib_1.__decorate([
76
76
  (0, typeorm_1.Column)(),
77
77
  (0, type_graphql_1.Field)(),
78
78
  tslib_1.__metadata("design:type", String)
79
- ], AssignmentClaim.prototype, "field", void 0);
79
+ ], MasterValueClaim.prototype, "field", void 0);
80
80
  tslib_1.__decorate([
81
81
  (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
82
82
  (0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true }),
83
83
  tslib_1.__metadata("design:type", Object)
84
- ], AssignmentClaim.prototype, "value", void 0);
84
+ ], MasterValueClaim.prototype, "value", void 0);
85
85
  tslib_1.__decorate([
86
- (0, typeorm_1.Column)(),
87
- (0, type_graphql_1.Field)(),
86
+ (0, typeorm_1.Column)({ nullable: true }),
87
+ (0, type_graphql_1.Field)({ nullable: true }),
88
88
  tslib_1.__metadata("design:type", Date
89
89
  /** 언제까지. 비면 아직 끝나지 않았다. */
90
90
  )
91
- ], AssignmentClaim.prototype, "validFrom", void 0);
91
+ ], MasterValueClaim.prototype, "validFrom", void 0);
92
92
  tslib_1.__decorate([
93
93
  (0, typeorm_1.Column)({ nullable: true }),
94
94
  (0, type_graphql_1.Field)({ nullable: true }),
95
95
  tslib_1.__metadata("design:type", Date
96
96
  /** 언제 그렇게 적었나. 서버가 붙인다 — 사람이 넣지 않는다. */
97
97
  )
98
- ], AssignmentClaim.prototype, "validTo", void 0);
98
+ ], MasterValueClaim.prototype, "validTo", void 0);
99
99
  tslib_1.__decorate([
100
100
  (0, typeorm_1.Column)(),
101
101
  (0, type_graphql_1.Field)(),
102
102
  tslib_1.__metadata("design:type", Date
103
103
  /** 언제 물러났나. 비면 지금도 서 있는 주장이다. */
104
104
  )
105
- ], AssignmentClaim.prototype, "recordedAt", void 0);
105
+ ], MasterValueClaim.prototype, "recordedAt", void 0);
106
106
  tslib_1.__decorate([
107
107
  (0, typeorm_1.Column)({ nullable: true }),
108
108
  (0, type_graphql_1.Field)({ nullable: true }),
@@ -118,43 +118,57 @@ tslib_1.__decorate([
118
118
  * ```
119
119
  */
120
120
  )
121
- ], AssignmentClaim.prototype, "supersededAt", void 0);
121
+ ], MasterValueClaim.prototype, "supersededAt", void 0);
122
122
  tslib_1.__decorate([
123
123
  (0, typeorm_1.Column)(),
124
124
  (0, type_graphql_1.Field)(),
125
125
  tslib_1.__metadata("design:type", String)
126
- ], AssignmentClaim.prototype, "reason", void 0);
126
+ ], MasterValueClaim.prototype, "reason", void 0);
127
127
  tslib_1.__decorate([
128
128
  (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
129
129
  (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
130
130
  tslib_1.__metadata("design:type", auth_base_1.User)
131
- ], AssignmentClaim.prototype, "recorder", void 0);
131
+ ], MasterValueClaim.prototype, "recorder", void 0);
132
132
  tslib_1.__decorate([
133
133
  (0, typeorm_1.RelationId)((claim) => claim.recorder),
134
134
  tslib_1.__metadata("design:type", String)
135
- ], AssignmentClaim.prototype, "recorderId", void 0);
135
+ ], MasterValueClaim.prototype, "recorderId", void 0);
136
136
  tslib_1.__decorate([
137
137
  (0, typeorm_1.CreateDateColumn)(),
138
138
  (0, type_graphql_1.Field)({ nullable: true }),
139
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]),
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
144
  (0, type_graphql_1.ObjectType)({ description: 'what an assignment or declaration was, from when, as recorded when' })
145
- ], AssignmentClaim);
145
+ ], MasterValueClaim);
146
146
  /**
147
- * **이력을 받는 칸은 셋뿐이다.**
147
+ * **이력을 받는 칸은 여기 적힌 것뿐이다.**
148
148
  *
149
149
  * 임의의 축·칸을 받는 범용 쓰기 문을 열지 않는다(설계 점검 2026-09-22). 칸마다 값의 모양과 검증이
150
150
  * 다르고, 아무 칸이나 받는 문은 그 검증을 못 한다.
151
+ *
152
+ * ── 셋에서 넷이 됐다 (2026-09-23) ─────────────────────────────────────────
153
+ * 앞의 셋은 **배정**이다 — 무엇이 어디에 속하나. 넷째는 아니다. 공정의 표준시간은 값 자체이고,
154
+ * 실적의 성능률이 그것을 나눈다. 지금은 **오늘 마스터를 다시 읽어서 지난달 성능이 움직인다**
155
+ * (`operation-effectivity-db.test.cjs` 가 그 거동을 적어 두고 있다).
156
+ *
157
+ * 그래서 이 표의 이름이 「배정」이 아니라 「마스터 값의 주장」이다. 기전은 같다 — 두 시각, 정정은
158
+ * 물리고 앞뒤를 다시 주장, 주장이 없으면 한 번도 안 바뀐 것.
151
159
  */
152
- exports.ASSIGNMENT_FIELDS = [
160
+ exports.CLAIMED_FIELDS = [
153
161
  { axis: 'equipment', field: 'homeLocation', kind: 'relation', target: 'operationalLocation' },
154
162
  { axis: 'operationalLocation', field: 'workShiftNames', kind: 'names' },
155
- { axis: 'operationalLocation', field: 'parentId', kind: 'relation', target: 'operationalLocation' }
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' }
156
170
  ];
157
- function assignmentField(axis, field) {
158
- return exports.ASSIGNMENT_FIELDS.find(one => one.axis === axis && one.field === field);
171
+ function claimedField(axis, field) {
172
+ return exports.CLAIMED_FIELDS.find(one => one.axis === axis && one.field === field);
159
173
  }
160
- //# sourceMappingURL=assignment-claim.js.map
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"]}
@@ -10,6 +10,8 @@ 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 './revision-baseline';
14
+ export * from './approval-route';
13
15
  export * from './master-conformance';
14
16
  export * from './master-conformance-resolver';
15
17
  export * from './master-list';
@@ -67,10 +69,11 @@ export * from './status/master-status';
67
69
  export * from './quality/nonconformance-cause';
68
70
  import { MasterConformanceResolver } from './master-conformance-resolver';
69
71
  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)[];
72
+ import { MasterValueClaim } from './assignment/master-value-claim';
73
+ 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("./material/material-revision").MaterialDefinitionRevision | typeof import("./material/material-revision").MaterialDefinitionPlan | typeof import("./material/material-revision").MaterialDefinitionSegment | typeof import("./material/material-revision").MaterialDefinitionBasis | typeof import("./recipe/recipe-revision").RecipeRevision | typeof import("./recipe/recipe-revision-line").RecipeRevisionOutput | typeof import("./recipe/recipe-revision").RecipePlan | typeof import("./recipe/recipe-revision").RecipeSegment | typeof import("./recipe/recipe-revision").RecipeBasis | 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 | typeof MasterValueClaim)[];
71
74
  export declare const schema: {
72
75
  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
76
  };
74
- export * from './assignment/assignment-claim';
77
+ export * from './assignment/master-value-claim';
75
78
  export * from './assignment/assignment-history';
76
79
  export * from './assignment/assignment-write';
@@ -15,6 +15,8 @@ tslib_1.__exportStar(require("./test-spec/test-spec-master-resolver"), exports);
15
15
  tslib_1.__exportStar(require("./master-crud"), exports);
16
16
  tslib_1.__exportStar(require("./master-axis"), exports);
17
17
  tslib_1.__exportStar(require("./master-change-policy"), exports);
18
+ tslib_1.__exportStar(require("./revision-baseline"), exports);
19
+ tslib_1.__exportStar(require("./approval-route"), exports);
18
20
  tslib_1.__exportStar(require("./master-conformance"), exports);
19
21
  tslib_1.__exportStar(require("./master-conformance-resolver"), exports);
20
22
  tslib_1.__exportStar(require("./master-list"), exports);
@@ -84,10 +86,16 @@ const status_1 = require("./status");
84
86
  /* 마스터 정합성 — 축을 가로지르므로 어느 축에도 안 속한다. */
85
87
  const master_conformance_resolver_1 = require("./master-conformance-resolver");
86
88
  const master_change_policy_1 = require("./master-change-policy");
89
+ const master_value_claim_1 = require("./assignment/master-value-claim");
87
90
  const test_spec_1 = require("./test-spec");
88
91
  exports.entities = [
89
92
  /* ENTITIES */
90
93
  master_change_policy_1.MasterChangePolicy,
94
+ /*
95
+ * 배정 이력. **등록이 빠져 있었다**(2026-09-23 확인) — 표가 안 서니 이력을 쓰는 길이 실제
96
+ * 설치본에는 없었다. 시험 하네스가 자기 목록에 직접 넣어서 초록이었다.
97
+ */
98
+ master_value_claim_1.MasterValueClaim,
91
99
  ...personnel_1.entities,
92
100
  ...quality_1.entities,
93
101
  ...equipment_1.entities,
@@ -119,7 +127,7 @@ exports.schema = {
119
127
  master_conformance_resolver_1.MasterConformanceResolver
120
128
  ]
121
129
  };
122
- tslib_1.__exportStar(require("./assignment/assignment-claim"), exports);
130
+ tslib_1.__exportStar(require("./assignment/master-value-claim"), exports);
123
131
  tslib_1.__exportStar(require("./assignment/assignment-history"), exports);
124
132
  tslib_1.__exportStar(require("./assignment/assignment-write"), exports);
125
133
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,oEAAyC;AACzC,gFAAqD;AACrD,gFAAqD;AACrD,wEAA6C;AAC7C,8EAAmD;AACnD,8EAAmD;AACnD,gFAAqD;AACrD,0EAA+C;AAC/C,gFAAqD;AACrD,wDAA6B;AAC7B,wDAA6B;AAC7B,iEAAsC;AACtC,+DAAoC;AACpC,wEAA6C;AAC7C,wDAA6B;AAC7B,6DAAkC;AAClC,sEAA2C;AAC3C,uEAA4C;AAC5C,sEAA2C;AAC3C,oEAAyC;AACzC,gEAAqC;AACrC,sEAA2C;AAC3C,yEAA8C;AAC9C,kEAAuC;AACvC,uEAA4C;AAC5C,sEAA2C;AAC3C,kEAAuC;AACvC,iEAAsC;AACtC,uEAA4C;AAC5C,+DAAoC;AACpC,8DAAmC;AACnC,0EAA+C;AAC/C,qEAA0C;AAC1C,oEAAyC;AACzC,oEAAyC;AACzC,yEAA8C;AAC9C,uEAA4C;AAC5C,+EAAoD;AACpD,gFAAqD;AACrD,6EAAkD;AAClD,qEAA0C;AAC1C,oEAAyC;AACzC,2EAAgD;AAChD,uEAA4C;AAC5C,sEAA2C;AAC3C,0EAA+C;AAC/C,mFAAwD;AACxD,qEAA0C;AAC1C,mEAAwC;AACxC,wEAA6C;AAC7C,2EAAgD;AAChD,2EAAgD;AAChD,4EAAiD;AACjD,yEAA8C;AAC9C,oEAAyC;AACzC,iEAAsC;AACtC,gEAAqC;AACrC,yEAA8C;AAC9C,yEAA8C;AAC9C,iFAAsD;AACtD,kFAAuD;AACvD,+EAAoD;AACpD,uEAA4C;AAC5C,sEAA2C;AAC3C,8DAAmC;AACnC,iEAAsC;AACtC,yEAA8C;AAE9C,mCAAmC;AACnC,mCAAgF;AAChF,2CAA4F;AAC5F,yCAAyF;AACzF,yCAAyF;AACzF,2CAA4F;AAC5F,2CAA4F;AAC5F,uCAAsF;AACtF,wEAAwE;AACxE,qCAAmF;AACnF,qCAAuD;AACvD,uCAAuC;AACvC,+EAAyE;AACzE,iEAAuF;AACvF,2CAA0F;AAE7E,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,yCAAkB;IAClB,GAAG,oBAAiB;IACpB,GAAG,kBAAe;IAClB,GAAG,oBAAiB;IACpB,GAAG,gBAAa;IAChB,GAAG,mBAAgB;IACnB,GAAG,mBAAgB;IACnB,GAAG,oBAAiB;IACpB,GAAG,iBAAc;IACjB,GAAG,oBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,iDAA0B;QAC1B,GAAG,qBAAkB;QACrB,GAAG,mBAAgB;QACnB,GAAG,qBAAkB;QACrB,GAAG,iBAAc;QACjB,GAAG,oBAAiB;QACpB,GAAG,oBAAiB;QACpB,GAAG,qBAAkB;QACrB,GAAG,kBAAe;QAClB,GAAG,qBAAiB;QACpB,GAAG,kBAAe;QAElB;;;WAGG;QACH,uDAAyB;KAC1B;CACF,CAAA;AACD,wEAA6C;AAC7C,0EAA+C;AAC/C,wEAA6C","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './resource/resource-class'\nexport * from './personnel/personnel-master-resolver'\nexport * from './equipment/equipment-master-resolver'\nexport * from './asset/asset-master-resolver'\nexport * from './material/material-master-resolver'\nexport * from './location/location-master-resolver'\nexport * from './operation/operation-master-resolver'\nexport * from './recipe/recipe-master-resolver'\nexport * from './test-spec/test-spec-master-resolver'\nexport * from './master-crud'\nexport * from './master-axis'\nexport * from './master-change-policy'\nexport * from './master-conformance'\nexport * from './master-conformance-resolver'\nexport * from './master-list'\nexport * from './personnel/person'\nexport * from './personnel/personnel-class'\nexport * from './personnel/personnel-master'\nexport * from './personnel/personnel-query'\nexport * from './personnel/qualification'\nexport * from './equipment/equipment'\nexport * from './equipment/equipment-class'\nexport * from './equipment/state-period-rules'\nexport * from './equipment/stop-reason'\nexport * from './equipment/equipment-master'\nexport * from './equipment/equipment-query'\nexport * from './asset/asset-lifecycle'\nexport * from './asset/physical-asset'\nexport * from './asset/physical-asset-class'\nexport * from './asset/asset-master'\nexport * from './asset/asset-query'\nexport * from './location/operational-location'\nexport * from './location/location-master'\nexport * from './location/location-query'\nexport * from './material/material-class'\nexport * from './material/material-definition'\nexport * from './material/material-revision'\nexport * from './material/material-revision-service'\nexport * from './material/material-revision-resolver'\nexport * from './material/material-effective-list'\nexport * from './material/material-master'\nexport * from './material/material-query'\nexport * from './operation/operation-definition'\nexport * from './operation/operation-master'\nexport * from './operation/operation-query'\nexport * from './operation/operation-readiness'\nexport * from './operation/operation-readiness-resolver'\nexport * from './recipe/recipe-definition'\nexport * from './recipe/recipe-revision'\nexport * from './recipe/recipe-revision-line'\nexport * from './recipe/recipe-revision-content'\nexport * from './recipe/recipe-revision-service'\nexport * from './recipe/recipe-revision-resolver'\nexport * from './recipe/recipe-effective-list'\nexport * from './recipe/route-definition'\nexport * from './recipe/recipe-master'\nexport * from './recipe/recipe-query'\nexport * from './test-spec/test-specification'\nexport * from './test-spec/test-spec-revision'\nexport * from './test-spec/test-spec-revision-service'\nexport * from './test-spec/test-spec-revision-resolver'\nexport * from './test-spec/test-spec-effective-list'\nexport * from './test-spec/test-spec-master'\nexport * from './test-spec/test-spec-query'\nexport * from './status/axis-stamp'\nexport * from './status/master-status'\nexport * from './quality/nonconformance-cause'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as AssetEntities, resolvers as AssetResolvers } from './asset'\nimport { entities as EquipmentEntities, resolvers as EquipmentResolvers } from './equipment'\nimport { entities as LocationEntities, resolvers as LocationResolvers } from './location'\nimport { entities as MaterialEntities, resolvers as MaterialResolvers } from './material'\nimport { entities as OperationEntities, resolvers as OperationResolvers } from './operation'\nimport { entities as PersonnelEntities, resolvers as PersonnelResolvers } from './personnel'\nimport { entities as QualityEntities, resolvers as QualityResolvers } from './quality'\n/* 라우팅·레시피 — 품목→지시서를 이으려면 그 사이에 이 둘이 있어야 한다(§`recipe-definition.ts`). */\nimport { entities as RecipeEntities, resolvers as RecipeResolvers } from './recipe'\nimport { resolvers as StatusResolvers } from './status'\n/* 마스터 정합성 — 축을 가로지르므로 어느 축에도 안 속한다. */\nimport { MasterConformanceResolver } from './master-conformance-resolver'\nimport { MasterChangePolicy, MasterChangePolicyResolver } from './master-change-policy'\nimport { entities as TestSpecEntities, resolvers as TestSpecResolvers } from './test-spec'\n\nexport const entities = [\n /* ENTITIES */\n MasterChangePolicy,\n ...PersonnelEntities,\n ...QualityEntities,\n ...EquipmentEntities,\n ...AssetEntities,\n ...LocationEntities,\n ...MaterialEntities,\n ...OperationEntities,\n ...RecipeEntities,\n ...TestSpecEntities\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n MasterChangePolicyResolver,\n ...PersonnelResolvers,\n ...QualityResolvers,\n ...EquipmentResolvers,\n ...AssetResolvers,\n ...LocationResolvers,\n ...MaterialResolvers,\n ...OperationResolvers,\n ...RecipeResolvers,\n ...TestSpecResolvers,\n ...StatusResolvers,\n\n /*\n * **파일을 export 하는 것만으로는 스키마에 안 올라온다.** 여기 적어야 한다 — 처음에 그것을\n * 빠뜨려 `masterConformance` 가 「Cannot query field」로 나왔다. [확인함] 2026-09-04\n */\n MasterConformanceResolver\n ]\n}\nexport * from './assignment/assignment-claim'\nexport * from './assignment/assignment-history'\nexport * from './assignment/assignment-write'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,oEAAyC;AACzC,gFAAqD;AACrD,gFAAqD;AACrD,wEAA6C;AAC7C,8EAAmD;AACnD,8EAAmD;AACnD,gFAAqD;AACrD,0EAA+C;AAC/C,gFAAqD;AACrD,wDAA6B;AAC7B,wDAA6B;AAC7B,iEAAsC;AACtC,8DAAmC;AACnC,2DAAgC;AAChC,+DAAoC;AACpC,wEAA6C;AAC7C,wDAA6B;AAC7B,6DAAkC;AAClC,sEAA2C;AAC3C,uEAA4C;AAC5C,sEAA2C;AAC3C,oEAAyC;AACzC,gEAAqC;AACrC,sEAA2C;AAC3C,yEAA8C;AAC9C,kEAAuC;AACvC,uEAA4C;AAC5C,sEAA2C;AAC3C,kEAAuC;AACvC,iEAAsC;AACtC,uEAA4C;AAC5C,+DAAoC;AACpC,8DAAmC;AACnC,0EAA+C;AAC/C,qEAA0C;AAC1C,oEAAyC;AACzC,oEAAyC;AACzC,yEAA8C;AAC9C,uEAA4C;AAC5C,+EAAoD;AACpD,gFAAqD;AACrD,6EAAkD;AAClD,qEAA0C;AAC1C,oEAAyC;AACzC,2EAAgD;AAChD,uEAA4C;AAC5C,sEAA2C;AAC3C,0EAA+C;AAC/C,mFAAwD;AACxD,qEAA0C;AAC1C,mEAAwC;AACxC,wEAA6C;AAC7C,2EAAgD;AAChD,2EAAgD;AAChD,4EAAiD;AACjD,yEAA8C;AAC9C,oEAAyC;AACzC,iEAAsC;AACtC,gEAAqC;AACrC,yEAA8C;AAC9C,yEAA8C;AAC9C,iFAAsD;AACtD,kFAAuD;AACvD,+EAAoD;AACpD,uEAA4C;AAC5C,sEAA2C;AAC3C,8DAAmC;AACnC,iEAAsC;AACtC,yEAA8C;AAE9C,mCAAmC;AACnC,mCAAgF;AAChF,2CAA4F;AAC5F,yCAAyF;AACzF,yCAAyF;AACzF,2CAA4F;AAC5F,2CAA4F;AAC5F,uCAAsF;AACtF,wEAAwE;AACxE,qCAAmF;AACnF,qCAAuD;AACvD,uCAAuC;AACvC,+EAAyE;AACzE,iEAAuF;AACvF,wEAAkE;AAClE,2CAA0F;AAE7E,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,yCAAkB;IAClB;;;OAGG;IACH,qCAAgB;IAChB,GAAG,oBAAiB;IACpB,GAAG,kBAAe;IAClB,GAAG,oBAAiB;IACpB,GAAG,gBAAa;IAChB,GAAG,mBAAgB;IACnB,GAAG,mBAAgB;IACnB,GAAG,oBAAiB;IACpB,GAAG,iBAAc;IACjB,GAAG,oBAAgB;CACpB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,iDAA0B;QAC1B,GAAG,qBAAkB;QACrB,GAAG,mBAAgB;QACnB,GAAG,qBAAkB;QACrB,GAAG,iBAAc;QACjB,GAAG,oBAAiB;QACpB,GAAG,oBAAiB;QACpB,GAAG,qBAAkB;QACrB,GAAG,kBAAe;QAClB,GAAG,qBAAiB;QACpB,GAAG,kBAAe;QAElB;;;WAGG;QACH,uDAAyB;KAC1B;CACF,CAAA;AACD,0EAA+C;AAC/C,0EAA+C;AAC/C,wEAA6C","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './resource/resource-class'\nexport * from './personnel/personnel-master-resolver'\nexport * from './equipment/equipment-master-resolver'\nexport * from './asset/asset-master-resolver'\nexport * from './material/material-master-resolver'\nexport * from './location/location-master-resolver'\nexport * from './operation/operation-master-resolver'\nexport * from './recipe/recipe-master-resolver'\nexport * from './test-spec/test-spec-master-resolver'\nexport * from './master-crud'\nexport * from './master-axis'\nexport * from './master-change-policy'\nexport * from './revision-baseline'\nexport * from './approval-route'\nexport * from './master-conformance'\nexport * from './master-conformance-resolver'\nexport * from './master-list'\nexport * from './personnel/person'\nexport * from './personnel/personnel-class'\nexport * from './personnel/personnel-master'\nexport * from './personnel/personnel-query'\nexport * from './personnel/qualification'\nexport * from './equipment/equipment'\nexport * from './equipment/equipment-class'\nexport * from './equipment/state-period-rules'\nexport * from './equipment/stop-reason'\nexport * from './equipment/equipment-master'\nexport * from './equipment/equipment-query'\nexport * from './asset/asset-lifecycle'\nexport * from './asset/physical-asset'\nexport * from './asset/physical-asset-class'\nexport * from './asset/asset-master'\nexport * from './asset/asset-query'\nexport * from './location/operational-location'\nexport * from './location/location-master'\nexport * from './location/location-query'\nexport * from './material/material-class'\nexport * from './material/material-definition'\nexport * from './material/material-revision'\nexport * from './material/material-revision-service'\nexport * from './material/material-revision-resolver'\nexport * from './material/material-effective-list'\nexport * from './material/material-master'\nexport * from './material/material-query'\nexport * from './operation/operation-definition'\nexport * from './operation/operation-master'\nexport * from './operation/operation-query'\nexport * from './operation/operation-readiness'\nexport * from './operation/operation-readiness-resolver'\nexport * from './recipe/recipe-definition'\nexport * from './recipe/recipe-revision'\nexport * from './recipe/recipe-revision-line'\nexport * from './recipe/recipe-revision-content'\nexport * from './recipe/recipe-revision-service'\nexport * from './recipe/recipe-revision-resolver'\nexport * from './recipe/recipe-effective-list'\nexport * from './recipe/route-definition'\nexport * from './recipe/recipe-master'\nexport * from './recipe/recipe-query'\nexport * from './test-spec/test-specification'\nexport * from './test-spec/test-spec-revision'\nexport * from './test-spec/test-spec-revision-service'\nexport * from './test-spec/test-spec-revision-resolver'\nexport * from './test-spec/test-spec-effective-list'\nexport * from './test-spec/test-spec-master'\nexport * from './test-spec/test-spec-query'\nexport * from './status/axis-stamp'\nexport * from './status/master-status'\nexport * from './quality/nonconformance-cause'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as AssetEntities, resolvers as AssetResolvers } from './asset'\nimport { entities as EquipmentEntities, resolvers as EquipmentResolvers } from './equipment'\nimport { entities as LocationEntities, resolvers as LocationResolvers } from './location'\nimport { entities as MaterialEntities, resolvers as MaterialResolvers } from './material'\nimport { entities as OperationEntities, resolvers as OperationResolvers } from './operation'\nimport { entities as PersonnelEntities, resolvers as PersonnelResolvers } from './personnel'\nimport { entities as QualityEntities, resolvers as QualityResolvers } from './quality'\n/* 라우팅·레시피 — 품목→지시서를 이으려면 그 사이에 이 둘이 있어야 한다(§`recipe-definition.ts`). */\nimport { entities as RecipeEntities, resolvers as RecipeResolvers } from './recipe'\nimport { resolvers as StatusResolvers } from './status'\n/* 마스터 정합성 — 축을 가로지르므로 어느 축에도 안 속한다. */\nimport { MasterConformanceResolver } from './master-conformance-resolver'\nimport { MasterChangePolicy, MasterChangePolicyResolver } from './master-change-policy'\nimport { MasterValueClaim } from './assignment/master-value-claim'\nimport { entities as TestSpecEntities, resolvers as TestSpecResolvers } from './test-spec'\n\nexport const entities = [\n /* ENTITIES */\n MasterChangePolicy,\n /*\n * 배정 이력. **등록이 빠져 있었다**(2026-09-23 확인) — 표가 안 서니 이력을 쓰는 길이 실제\n * 설치본에는 없었다. 시험 하네스가 자기 목록에 직접 넣어서 초록이었다.\n */\n MasterValueClaim,\n ...PersonnelEntities,\n ...QualityEntities,\n ...EquipmentEntities,\n ...AssetEntities,\n ...LocationEntities,\n ...MaterialEntities,\n ...OperationEntities,\n ...RecipeEntities,\n ...TestSpecEntities\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n MasterChangePolicyResolver,\n ...PersonnelResolvers,\n ...QualityResolvers,\n ...EquipmentResolvers,\n ...AssetResolvers,\n ...LocationResolvers,\n ...MaterialResolvers,\n ...OperationResolvers,\n ...RecipeResolvers,\n ...TestSpecResolvers,\n ...StatusResolvers,\n\n /*\n * **파일을 export 하는 것만으로는 스키마에 안 올라온다.** 여기 적어야 한다 — 처음에 그것을\n * 빠뜨려 `masterConformance` 가 「Cannot query field」로 나왔다. [확인함] 2026-09-04\n */\n MasterConformanceResolver\n ]\n}\nexport * from './assignment/master-value-claim'\nexport * from './assignment/assignment-history'\nexport * from './assignment/assignment-write'\n"]}
@@ -3,15 +3,15 @@ import { ObjectLiteral } from 'typeorm';
3
3
  * 마스터 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.
4
4
  *
5
5
  * ── 왜 표로 두나 ───────────────────────────────────────────────────────────
6
- * 축이 열둘인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열두 벌이 되고,
7
- * 규칙 하나를 고칠 때 열두 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데
6
+ * 축이 열다섯인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열다섯 벌이 되고,
7
+ * 규칙 하나를 고칠 때 열다섯 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데
8
8
  * 오류는 나지 않는다.
9
9
  *
10
10
  * 그래서 **다른 것만** 적는다 — 어느 표인가, 무엇으로 찾는가, 없앨 때 지우는가 끝을 채우는가.
11
11
  *
12
12
  * ── 왜 유효 기간 여부가 여기 있나 ──────────────────────────────────────────
13
13
  * 지난 기록이 가리키는 축은 지우면 안 된다. 그것이 축마다 다르므로 축의 성질로 적는다. 정지 사유는
14
- * 유효 기간이 있고, 등급도 있다. 지금 열둘 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이
14
+ * 유효 기간이 있고, 등급도 있다. 지금 열다섯 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이
15
15
  * 생기면 그때 이 표만 고치면 된다.
16
16
  */
17
17
  export interface MasterAxis {
@@ -24,6 +24,17 @@ export interface MasterAxis {
24
24
  searchables: string[];
25
25
  /** 없앨 때 행을 지우지 않고 `effectiveEnd` 를 채우나. */
26
26
  hasEffectivePeriod: boolean;
27
+ /**
28
+ * 이 축의 바뀜을 **개정본으로 결재에 보낼 수 있나.**
29
+ *
30
+ * 승인으로 켜면 `assertDirectMasterChange` 가 이 축의 직접 쓰기를 전부 거절한다. 그 뒤로 고치는 길은
31
+ * 개정본을 결재로 보내는 것뿐이라, 그 길이 없는 축을 켜면 **그 축이 잠긴다**(2026-09-24). 그래서
32
+ * 정책은 이 칸이 참인 축만 승인으로 켜게 한다(`declareMasterChangePolicy`).
33
+ *
34
+ * 앱이 정하지 않는다 — 개정본 문은 이 꾸러미가 지었고, 어느 앱에서도 없는 문으로는 보낼 수 없다.
35
+ * 축마다 적게 한 것은, 새 축이 이 물음에 답하지 않고 들어오지 못하게 하려는 것이다.
36
+ */
37
+ revisable: boolean;
27
38
  }
28
39
  export declare const MASTER_AXES: readonly MasterAxis[];
29
40
  export declare function masterAxisOf(key: string): MasterAxis;
@@ -18,31 +18,32 @@ const recipe_definition_1 = require("./recipe/recipe-definition");
18
18
  const route_definition_1 = require("./recipe/route-definition");
19
19
  const test_specification_1 = require("./test-spec/test-specification");
20
20
  exports.MASTER_AXES = [
21
- { key: 'personnelClass', entity: personnel_class_1.PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true },
22
- { key: 'person', entity: person_1.Person, searchables: ['name', 'description'], hasEffectivePeriod: true },
23
- { key: 'equipmentClass', entity: equipment_class_1.EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true },
24
- { key: 'equipment', entity: equipment_1.Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true },
25
- { key: 'stopReason', entity: stop_reason_1.StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true },
26
- { key: 'nonconformanceCause', entity: nonconformance_cause_1.NonconformanceCause, searchables: ['name', 'description'], hasEffectivePeriod: true },
27
- { key: 'physicalAssetClass', entity: physical_asset_class_1.PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true },
28
- { key: 'physicalAsset', entity: physical_asset_1.PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true },
29
- { key: 'materialClass', entity: material_class_1.MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true },
30
- { key: 'materialDefinition', entity: material_definition_1.MaterialDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
31
- { key: 'operationalLocation', entity: operational_location_1.OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true },
32
- { key: 'operationDefinition', entity: operation_definition_1.OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
33
- { key: 'testSpecification', entity: test_specification_1.TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true },
21
+ { key: 'personnelClass', entity: personnel_class_1.PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
22
+ { key: 'person', entity: person_1.Person, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
23
+ { key: 'equipmentClass', entity: equipment_class_1.EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
24
+ { key: 'equipment', entity: equipment_1.Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
25
+ { key: 'stopReason', entity: stop_reason_1.StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
26
+ { key: 'nonconformanceCause', entity: nonconformance_cause_1.NonconformanceCause, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
27
+ { key: 'physicalAssetClass', entity: physical_asset_class_1.PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
28
+ { key: 'physicalAsset', entity: physical_asset_1.PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true, revisable: false },
29
+ { key: 'materialClass', entity: material_class_1.MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
30
+ { key: 'materialDefinition', entity: material_definition_1.MaterialDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: true },
31
+ { key: 'operationalLocation', entity: operational_location_1.OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
32
+ { key: 'operationDefinition', entity: operation_definition_1.OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
33
+ { key: 'testSpecification', entity: test_specification_1.TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: true },
34
34
  /*
35
35
  * 라우팅과 레시피 (2026-08-31) — 커널에 `RouteDef`·`RecipeDef` 가 있고 `JobOrder.routeKey` 가 그것을
36
36
  * 가리키는데 **그 값이 가리킬 표가 없었다.** 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은
37
37
  * 작업지시에 라우팅을 자동으로 붙일 수 없었다.
38
38
  */
39
- { key: 'routeDefinition', entity: route_definition_1.RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true },
39
+ { key: 'routeDefinition', entity: route_definition_1.RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },
40
40
  {
41
41
  key: 'recipeDefinition',
42
42
  entity: recipe_definition_1.RecipeDefinition,
43
43
  /* 만드는 품목으로도 찾는다 — 「이 품목을 만드는 레시피」가 MES 가 지시마다 묻는 물음이다. */
44
44
  searchables: ['name', 'description', 'materialDefinitionName'],
45
- hasEffectivePeriod: true
45
+ hasEffectivePeriod: true,
46
+ revisable: true
46
47
  }
47
48
  ];
48
49
  function masterAxisOf(key) {