@things-factory/station 10.1.247 → 10.1.248

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 (177) hide show
  1. package/package.json +4 -4
  2. package/dist-client/index.d.ts +0 -11
  3. package/dist-client/index.js +0 -24
  4. package/dist-client/index.js.map +0 -1
  5. package/dist-client/station/scene/index.d.ts +0 -11
  6. package/dist-client/station/scene/index.js +0 -12
  7. package/dist-client/station/scene/index.js.map +0 -1
  8. package/dist-client/station/scene/scene-kit.d.ts +0 -9
  9. package/dist-client/station/scene/scene-kit.js +0 -10
  10. package/dist-client/station/scene/scene-kit.js.map +0 -1
  11. package/dist-client/station/scene/station-context-binding.d.ts +0 -25
  12. package/dist-client/station/scene/station-context-binding.js +0 -39
  13. package/dist-client/station/scene/station-context-binding.js.map +0 -1
  14. package/dist-client/station/scene/station-context-scene.d.ts +0 -17
  15. package/dist-client/station/scene/station-context-scene.js +0 -127
  16. package/dist-client/station/scene/station-context-scene.js.map +0 -1
  17. package/dist-client/station/scene/station-counts-scene.d.ts +0 -6
  18. package/dist-client/station/scene/station-counts-scene.js +0 -51
  19. package/dist-client/station/scene/station-counts-scene.js.map +0 -1
  20. package/dist-client/station/scene/station-field-nature.d.ts +0 -2
  21. package/dist-client/station/scene/station-field-nature.js +0 -42
  22. package/dist-client/station/scene/station-field-nature.js.map +0 -1
  23. package/dist-client/station/scene/station-field-scene.d.ts +0 -26
  24. package/dist-client/station/scene/station-field-scene.js +0 -73
  25. package/dist-client/station/scene/station-field-scene.js.map +0 -1
  26. package/dist-client/station/scene/station-icons.d.ts +0 -82
  27. package/dist-client/station/scene/station-icons.js +0 -131
  28. package/dist-client/station/scene/station-icons.js.map +0 -1
  29. package/dist-client/station/scene/station-numpad-scene.d.ts +0 -9
  30. package/dist-client/station/scene/station-numpad-scene.js +0 -144
  31. package/dist-client/station/scene/station-numpad-scene.js.map +0 -1
  32. package/dist-client/station/scene/station-scene-style.d.ts +0 -51
  33. package/dist-client/station/scene/station-scene-style.js +0 -108
  34. package/dist-client/station/scene/station-scene-style.js.map +0 -1
  35. package/dist-client/station/scene/station-scene.d.ts +0 -294
  36. package/dist-client/station/scene/station-scene.js +0 -68
  37. package/dist-client/station/scene/station-scene.js.map +0 -1
  38. package/dist-client/station/scene/station-who-scene.d.ts +0 -6
  39. package/dist-client/station/scene/station-who-scene.js +0 -48
  40. package/dist-client/station/scene/station-who-scene.js.map +0 -1
  41. package/dist-client/station/scene/station-work-panel-scene.d.ts +0 -28
  42. package/dist-client/station/scene/station-work-panel-scene.js +0 -37
  43. package/dist-client/station/scene/station-work-panel-scene.js.map +0 -1
  44. package/dist-client/station/station-board-binding.d.ts +0 -26
  45. package/dist-client/station/station-board-binding.js +0 -60
  46. package/dist-client/station/station-board-binding.js.map +0 -1
  47. package/dist-client/station/station-context.d.ts +0 -178
  48. package/dist-client/station/station-context.js +0 -30
  49. package/dist-client/station/station-context.js.map +0 -1
  50. package/dist-client/station/station-palette.d.ts +0 -34
  51. package/dist-client/station/station-palette.js +0 -119
  52. package/dist-client/station/station-palette.js.map +0 -1
  53. package/dist-client/station/station-refresh-policy.d.ts +0 -17
  54. package/dist-client/station/station-refresh-policy.js +0 -18
  55. package/dist-client/station/station-refresh-policy.js.map +0 -1
  56. package/dist-client/station/station-runtime.d.ts +0 -47
  57. package/dist-client/station/station-runtime.js +0 -47
  58. package/dist-client/station/station-runtime.js.map +0 -1
  59. package/dist-client/station/station-scene-context.d.ts +0 -68
  60. package/dist-client/station/station-scene-context.js +0 -62
  61. package/dist-client/station/station-scene-context.js.map +0 -1
  62. package/dist-client/station/station-screen-flow.d.ts +0 -16
  63. package/dist-client/station/station-screen-flow.js +0 -58
  64. package/dist-client/station/station-screen-flow.js.map +0 -1
  65. package/dist-client/station/station-tile-registry.d.ts +0 -89
  66. package/dist-client/station/station-tile-registry.js +0 -86
  67. package/dist-client/station/station-tile-registry.js.map +0 -1
  68. package/dist-client/station/station-work-panel-view.d.ts +0 -23
  69. package/dist-client/station/station-work-panel-view.js +0 -125
  70. package/dist-client/station/station-work-panel-view.js.map +0 -1
  71. package/dist-client/station/station-work-runtime.d.ts +0 -47
  72. package/dist-client/station/station-work-runtime.js +0 -161
  73. package/dist-client/station/station-work-runtime.js.map +0 -1
  74. package/dist-client/station/station-work-scenarios.d.ts +0 -41
  75. package/dist-client/station/station-work-scenarios.js +0 -59
  76. package/dist-client/station/station-work-scenarios.js.map +0 -1
  77. package/dist-client/station/task-counts.d.ts +0 -32
  78. package/dist-client/station/task-counts.js +0 -26
  79. package/dist-client/station/task-counts.js.map +0 -1
  80. package/dist-client/tsconfig.tsbuildinfo +0 -1
  81. package/dist-server/activities/activity-station-screen-control-withdrawal.d.ts +0 -62
  82. package/dist-server/activities/activity-station-screen-control-withdrawal.js +0 -143
  83. package/dist-server/activities/activity-station-screen-control-withdrawal.js.map +0 -1
  84. package/dist-server/activities/activity-station-screen-release.d.ts +0 -81
  85. package/dist-server/activities/activity-station-screen-release.js +0 -147
  86. package/dist-server/activities/activity-station-screen-release.js.map +0 -1
  87. package/dist-server/activities/index.d.ts +0 -49
  88. package/dist-server/activities/index.js +0 -21
  89. package/dist-server/activities/index.js.map +0 -1
  90. package/dist-server/index.d.ts +0 -3
  91. package/dist-server/index.js +0 -8
  92. package/dist-server/index.js.map +0 -1
  93. package/dist-server/service/index.d.ts +0 -22
  94. package/dist-server/service/index.js +0 -40
  95. package/dist-server/service/index.js.map +0 -1
  96. package/dist-server/service/station/index.d.ts +0 -13
  97. package/dist-server/service/station/index.js +0 -25
  98. package/dist-server/service/station/index.js.map +0 -1
  99. package/dist-server/service/station/screen-model-digest.d.ts +0 -60
  100. package/dist-server/service/station/screen-model-digest.js +0 -97
  101. package/dist-server/service/station/screen-model-digest.js.map +0 -1
  102. package/dist-server/service/station/staged-material.d.ts +0 -62
  103. package/dist-server/service/station/staged-material.js +0 -44
  104. package/dist-server/service/station/staged-material.js.map +0 -1
  105. package/dist-server/service/station/station-argument-sources.d.ts +0 -31
  106. package/dist-server/service/station/station-argument-sources.js +0 -66
  107. package/dist-server/service/station/station-argument-sources.js.map +0 -1
  108. package/dist-server/service/station/station-assignment-choice.d.ts +0 -81
  109. package/dist-server/service/station/station-assignment-choice.js +0 -105
  110. package/dist-server/service/station/station-assignment-choice.js.map +0 -1
  111. package/dist-server/service/station/station-assignment-resolver.d.ts +0 -104
  112. package/dist-server/service/station/station-assignment-resolver.js +0 -426
  113. package/dist-server/service/station/station-assignment-resolver.js.map +0 -1
  114. package/dist-server/service/station/station-assignment-service.d.ts +0 -33
  115. package/dist-server/service/station/station-assignment-service.js +0 -64
  116. package/dist-server/service/station/station-assignment-service.js.map +0 -1
  117. package/dist-server/service/station/station-axes.d.ts +0 -9
  118. package/dist-server/service/station/station-axes.js +0 -18
  119. package/dist-server/service/station/station-axes.js.map +0 -1
  120. package/dist-server/service/station/station-flow.d.ts +0 -158
  121. package/dist-server/service/station/station-flow.js +0 -257
  122. package/dist-server/service/station/station-flow.js.map +0 -1
  123. package/dist-server/service/station/station-generate.d.ts +0 -83
  124. package/dist-server/service/station/station-generate.js +0 -181
  125. package/dist-server/service/station/station-generate.js.map +0 -1
  126. package/dist-server/service/station/station-product.d.ts +0 -113
  127. package/dist-server/service/station/station-product.js +0 -124
  128. package/dist-server/service/station/station-product.js.map +0 -1
  129. package/dist-server/service/station/station-runtime-binding.d.ts +0 -6
  130. package/dist-server/service/station/station-runtime-binding.js +0 -46
  131. package/dist-server/service/station/station-runtime-binding.js.map +0 -1
  132. package/dist-server/service/station/station-screen-assignment.d.ts +0 -94
  133. package/dist-server/service/station/station-screen-assignment.js +0 -154
  134. package/dist-server/service/station/station-screen-assignment.js.map +0 -1
  135. package/dist-server/service/station/station-screen-control-withdrawal-resolver.d.ts +0 -37
  136. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js +0 -130
  137. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js.map +0 -1
  138. package/dist-server/service/station/station-screen-control-withdrawal.d.ts +0 -104
  139. package/dist-server/service/station/station-screen-control-withdrawal.js +0 -128
  140. package/dist-server/service/station/station-screen-control-withdrawal.js.map +0 -1
  141. package/dist-server/service/station/station-screen-draft-resolver.d.ts +0 -47
  142. package/dist-server/service/station/station-screen-draft-resolver.js +0 -155
  143. package/dist-server/service/station/station-screen-draft-resolver.js.map +0 -1
  144. package/dist-server/service/station/station-screen-layout.d.ts +0 -121
  145. package/dist-server/service/station/station-screen-layout.js +0 -202
  146. package/dist-server/service/station/station-screen-layout.js.map +0 -1
  147. package/dist-server/service/station/station-screen-list-resolver.d.ts +0 -69
  148. package/dist-server/service/station/station-screen-list-resolver.js +0 -215
  149. package/dist-server/service/station/station-screen-list-resolver.js.map +0 -1
  150. package/dist-server/service/station/station-screen-publish-subscriber.d.ts +0 -22
  151. package/dist-server/service/station/station-screen-publish-subscriber.js +0 -90
  152. package/dist-server/service/station/station-screen-publish-subscriber.js.map +0 -1
  153. package/dist-server/service/station/station-screen-release-gate.d.ts +0 -66
  154. package/dist-server/service/station/station-screen-release-gate.js +0 -67
  155. package/dist-server/service/station/station-screen-release-gate.js.map +0 -1
  156. package/dist-server/service/station/station-screen-release-record.d.ts +0 -62
  157. package/dist-server/service/station/station-screen-release-record.js +0 -139
  158. package/dist-server/service/station/station-screen-release-record.js.map +0 -1
  159. package/dist-server/service/station/station-screen-release-signature.d.ts +0 -23
  160. package/dist-server/service/station/station-screen-release-signature.js +0 -32
  161. package/dist-server/service/station/station-screen-release-signature.js.map +0 -1
  162. package/dist-server/service/station/station-screen-scorecard.d.ts +0 -15
  163. package/dist-server/service/station/station-screen-scorecard.js +0 -35
  164. package/dist-server/service/station/station-screen-scorecard.js.map +0 -1
  165. package/dist-server/service/station/station-screen-spec-resolver.d.ts +0 -36
  166. package/dist-server/service/station/station-screen-spec-resolver.js +0 -202
  167. package/dist-server/service/station/station-screen-spec-resolver.js.map +0 -1
  168. package/dist-server/service/station/station-screen-spec.d.ts +0 -104
  169. package/dist-server/service/station/station-screen-spec.js +0 -114
  170. package/dist-server/service/station/station-screen-spec.js.map +0 -1
  171. package/dist-server/service/station/station-screen-thumbnail.d.ts +0 -11
  172. package/dist-server/service/station/station-screen-thumbnail.js +0 -36
  173. package/dist-server/service/station/station-screen-thumbnail.js.map +0 -1
  174. package/dist-server/service/station/station-service-resolver.d.ts +0 -36
  175. package/dist-server/service/station/station-service-resolver.js +0 -113
  176. package/dist-server/service/station/station-service-resolver.js.map +0 -1
  177. package/dist-server/tsconfig.tsbuildinfo +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"station-assignment-choice.js","sourceRoot":"","sources":["../../../server/service/station/station-assignment-choice.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which drawn screen a station gets — **the picking rule, and nothing else.**\n *\n * Pure on purpose: the rule is the part that will be argued about, and arguing about it needs to be\n * possible without a database and a shift calendar.\n *\n * ── The rule ───────────────────────────────────────────────────────────────\n * A screen declares constraints on the axes it cares about and leaves the rest open. It is a\n * candidate when **every constraint it declared** matches the station. Among candidates, the more\n * specific one wins — the same rule the kernel already uses where a recipe line beats a general\n * material spec.\n *\n * ── Specificity is a partial order, not a ranking ──────────────────────────\n * \"More specific\" means **constraining a strict superset of the axes**: a screen for\n * `{workCenter, operation}` beats one for `{workCenter}`, because it says everything the other says and more.\n *\n * What it deliberately does not do is weigh axes against each other. `{equipment}` versus\n * `{workCenter, operation}` — one names a single machine, the other two broader things, and any ordering\n * between them would be a number somebody made up. Inventing that weight is how a rule becomes\n * folklore that nobody can predict. So they are a **tie**, and a tie is decided by a priority\n * somebody wrote down.\n *\n * ── A tie nobody resolved is reported, not hidden ──────────────────────────\n * If two screens tie and no priority separates them, one still has to be drawn — but the answer says\n * it was ambiguous and names the others. Quietly dropping one is the worst outcome: the factory sees\n * a screen, cannot tell why that one, and changing the other has no effect.\n */\n\n/** The axes a screen can constrain. Everything is optional — absent means \"any\". */\nexport interface StationAssignmentSelector {\n equipmentName?: string\n equipmentClassId?: string\n workCenterName?: string\n operationKey?: string\n routeKey?: string\n materialClassId?: string\n personnelClassId?: string\n}\n\n/** One assignment: a board, and when to show it. */\nexport interface StationAssignmentRule extends StationAssignmentSelector {\n name: string\n /** 그릴 화면의 이름. 버전은 배정이 못 박았을 때만 있다. */\n boardId: string\n /** Breaks a tie between equally specific screens. Higher wins. */\n priority?: number\n}\n\n/**\n * What the station actually is right now — the same five axes, plus the classes it belongs to.\n *\n * The axis names are the contract's, not this factory's (ADR-0083 decision 5): `workCenterName`\n * covers a production line, a storage zone and a yard area, which is what lets the warehouse select\n * on the same axis without a word of ours in it.\n */\nexport interface StationAssignmentSituation {\n equipmentName?: string\n equipmentClassIds?: string[]\n workCenterName?: string\n operationKey?: string\n routeKey?: string\n materialClassIds?: string[]\n personnelClassIds?: string[]\n}\n\nexport interface StationAssignmentChoice {\n name: string\n boardId: string\n /** Which axes this screen constrained. The admin screen says \"chosen because …\" from this. */\n matchedOn: (keyof StationAssignmentSelector)[]\n /**\n * Equally specific screens that nothing separated.\n *\n * Empty is the normal case. Non-empty means the factory has two answers to the same question and\n * somebody has to write a priority — the screen drawn is deterministic but arbitrary.\n */\n ambiguousWith: string[]\n}\n\nconst SINGLE_AXES = ['equipmentName', 'workCenterName', 'operationKey', 'routeKey'] as const\n\nconst CLASS_AXES = [\n ['equipmentClassId', 'equipmentClassIds'],\n ['materialClassId', 'materialClassIds'],\n ['personnelClassId', 'personnelClassIds']\n] as const\n\n/** Which axes a rule constrains. Its specificity is this set, not its size. */\nfunction constrainedAxes(rule: StationAssignmentRule): (keyof StationAssignmentSelector)[] {\n const axes: (keyof StationAssignmentSelector)[] = []\n\n for (const axis of SINGLE_AXES) {\n if (rule[axis]) {\n axes.push(axis)\n }\n }\n\n for (const [axis] of CLASS_AXES) {\n if (rule[axis]) {\n axes.push(axis)\n }\n }\n\n return axes\n}\n\n/**\n * Does every constraint this rule declared hold here.\n *\n * An axis the situation does not know is **not a match.** A screen asking for welders should not be\n * drawn before anyone has scanned in — that is the moment it would matter most that we do not guess.\n */\nfunction fits(rule: StationAssignmentRule, situation: StationAssignmentSituation): boolean {\n for (const axis of SINGLE_AXES) {\n if (rule[axis] && rule[axis] !== situation[axis]) {\n return false\n }\n }\n\n for (const [axis, membership] of CLASS_AXES) {\n const wanted = rule[axis]\n\n if (wanted && !(situation[membership] ?? []).includes(wanted)) {\n return false\n }\n }\n\n return true\n}\n\nfunction isSuperset(of: (keyof StationAssignmentSelector)[], than: (keyof StationAssignmentSelector)[]): boolean {\n return than.every(axis => of.includes(axis)) && of.length > than.length\n}\n\n/**\n * Pick one. Nothing fits → nothing is drawn, and the caller says so rather than showing a default.\n *\n * A screen that appears when nothing was assigned teaches the factory that assignment does not\n * matter, and then nobody trusts what they see.\n */\nexport function chooseStationAssignment(rules: readonly StationAssignmentRule[], situation: StationAssignmentSituation): StationAssignmentChoice | undefined {\n const candidates = rules.filter(rule => fits(rule, situation))\n\n if (!candidates.length) {\n return undefined\n }\n\n const withAxes = candidates.map(rule => ({ rule, axes: constrainedAxes(rule) }))\n\n /* Drop anything another candidate says strictly more than. */\n const finalists = withAxes.filter(one => !withAxes.some(other => isSuperset(other.axes, one.axes)))\n\n const best = finalists.reduce((winner, one) => {\n const byPriority = (one.rule.priority ?? 0) - (winner.rule.priority ?? 0)\n\n if (byPriority !== 0) {\n return byPriority > 0 ? one : winner\n }\n\n /* Same priority, neither more specific. Pick by name so the same factory sees the same screen. */\n return one.rule.name < winner.rule.name ? one : winner\n })\n\n const tied = finalists.filter(one => one.rule !== best.rule && (one.rule.priority ?? 0) === (best.rule.priority ?? 0))\n\n return {\n name: best.rule.name,\n boardId: best.rule.boardId,\n matchedOn: best.axes,\n ambiguousWith: tied.map(one => one.rule.name).sort()\n }\n}\n"]}
@@ -1,104 +0,0 @@
1
- import { StationScreenAssignment } from './station-screen-assignment';
2
- export declare class StationAssignmentChoiceType {
3
- name: string;
4
- boardId: string;
5
- /**
6
- * 무엇으로 골랐나.
7
- *
8
- * 「왜 이 화면인가」에 답하려고 함께 낸다. 이것이 없으면 배정이 여러 줄일 때 관리자가 자기가 고친 줄이
9
- * 이 태블릿에 걸리는 줄인지 확인할 방법이 없다.
10
- */
11
- matchedOn: string[];
12
- /**
13
- * 같은 만큼 구체적인데 아무것도 가려 주지 못한 다른 배정들.
14
- *
15
- * 비어 있는 것이 정상이다. 비어 있지 않으면 **공장이 같은 물음에 답을 둘 갖고 있다**는 뜻이고, 그리는
16
- * 화면은 정해져 있지만 임의로 정해진 것이다 — 누군가 우선순위를 적어야 한다. 조용히 한쪽을 버리면
17
- * 관리자는 다른 줄을 고쳐도 아무 일이 안 일어나는 것을 겪는다.
18
- */
19
- ambiguousWith: string[];
20
- }
21
- export declare class StationAssignmentRowType {
22
- name: string;
23
- description?: string;
24
- boardId: string;
25
- /**
26
- * 그 화면이 지금 현장에 나가 있는 버전.
27
- *
28
- * 없으면 **나간 버전이 없다**는 뜻이고, 그러면 이 배정은 아무것도 못 그린다 — 아래 `usable` 이 그것을
29
- * 말한다. 초안이 있어도 대신 내밀지 않는다: 작업대가 그리다 만 화면 앞에서 실적을 남기게 된다.
30
- */
31
- boardName?: string;
32
- equipmentName?: string;
33
- equipmentClassId?: string;
34
- workCenterName?: string;
35
- operationKey?: string;
36
- routeKey?: string;
37
- materialClassId?: string;
38
- personnelClassId?: string;
39
- priority?: number;
40
- effectiveStart?: Date;
41
- effectiveEnd?: Date;
42
- /**
43
- * 지금 이 줄이 화면을 그릴 수 있나.
44
- *
45
- * ── 왜 선언 옆에 현실을 함께 적나 ────────────────────────────────────────
46
- * 없어진 보드를 가리키는 배정은 **목록에는 멀쩡히 서 있고 현장에는 아무것도 안 뜬다.** 유효 기간이
47
- * 지난 줄도 같다. 화면이 그것을 말하지 않으면 관리자는 자기가 적은 것이 왜 안 먹는지 알 길이 없다.
48
- */
49
- usable: boolean;
50
- /** 못 쓰면 왜 못 쓰나 — 사용자가 읽을 문장. 쓸 수 있으면 없다. */
51
- because?: string;
52
- }
53
- export declare class StationAssignmentSetupType {
54
- screens: StationAssignmentRowType[];
55
- /**
56
- * 아무 축도 걸지 않은 배정의 이름들.
57
- *
58
- * 하나면 「그 밖의 모든 작업대」라는 뜻이라 쓸모가 있다. **둘 이상이면 둘 다 모든 작업대에 들어맞고**
59
- * 언제나 동점이다 — 우선순위 없이는 이름순으로 갈린다. 화면이 짚어 준다.
60
- */
61
- unconstrained: string[];
62
- }
63
- export declare class StationAssignmentInput {
64
- name: string;
65
- description?: string;
66
- boardId: string;
67
- equipmentName?: string;
68
- equipmentClassId?: string;
69
- workCenterName?: string;
70
- operationKey?: string;
71
- routeKey?: string;
72
- materialClassId?: string;
73
- personnelClassId?: string;
74
- priority?: number;
75
- effectiveStart?: Date;
76
- effectiveEnd?: Date;
77
- }
78
- export declare class StationAssignmentResolver {
79
- /**
80
- * 이 작업대가 지금 그릴 보드.
81
- *
82
- * **배정이 없으면 아무것도 돌려주지 않는다.** 기본 화면을 내놓지 않는 이유는, 배정하지 않아도 뭔가
83
- * 뜨면 공장이 「배정은 안 해도 된다」를 배우고, 그 뒤로는 벽에 걸린 것을 아무도 믿지 않게 되기 때문이다.
84
- */
85
- stationAssignment(equipmentName: string, context: any, operationKey?: string, operatorName?: string, jobOrderName?: string): Promise<StationAssignmentChoiceType | undefined>;
86
- /**
87
- * 배정 전부 — **선언 옆에 현실을 함께.**
88
- *
89
- * ── 목록의 순서는 승부가 아니다 ─────────────────────────────────────────
90
- * 이기는 것은 얼마나 구체적인가이고 우선순위는 동점일 때만 쓴다. 목록이 승부를 말하는 척하면 관리자가
91
- * 맨 위 줄이 이긴다고 읽는다. 그래서 이름순으로만 낸다 — 어느 줄이 이기는지는 **물어봐야** 안다
92
- * (`stationScreen`).
93
- */
94
- stationAssignmentSetup(context: any): Promise<StationAssignmentSetupType>;
95
- /**
96
- * 배정을 적거나 고친다.
97
- *
98
- * ── 왜 만드는 문이 함께 있어야 하나 ──────────────────────────────────────
99
- * 읽는 질의만 두면 배정을 만들 길이 SQL 뿐이다. 이 제품에서 가장 잦은 결함이 **선언은 있고 길이 없는**
100
- * 것이고, 관리 화면이 붙기 전이라도 문은 열려 있어야 한다.
101
- */
102
- saveStationAssignment(context: any, patch: StationAssignmentInput): Promise<StationScreenAssignment>;
103
- deleteStationAssignment(context: any, name: string): Promise<boolean>;
104
- }
@@ -1,426 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StationAssignmentResolver = exports.StationAssignmentInput = exports.StationAssignmentSetupType = exports.StationAssignmentRowType = exports.StationAssignmentChoiceType = void 0;
4
- const tslib_1 = require("tslib");
5
- const type_graphql_1 = require("type-graphql");
6
- const shell_1 = require("@things-factory/shell");
7
- const typeorm_1 = require("typeorm");
8
- const ops_master_1 = require("@things-factory/ops-master");
9
- const board_service_1 = require("@things-factory/board-service");
10
- const station_screen_assignment_1 = require("./station-screen-assignment");
11
- const station_assignment_service_1 = require("./station-assignment-service");
12
- let StationAssignmentChoiceType = class StationAssignmentChoiceType {
13
- };
14
- exports.StationAssignmentChoiceType = StationAssignmentChoiceType;
15
- tslib_1.__decorate([
16
- (0, type_graphql_1.Field)({ description: 'the assignment that won' }),
17
- tslib_1.__metadata("design:type", String)
18
- ], StationAssignmentChoiceType.prototype, "name", void 0);
19
- tslib_1.__decorate([
20
- (0, type_graphql_1.Field)({ description: 'the station screen to draw, by name' }),
21
- tslib_1.__metadata("design:type", String)
22
- ], StationAssignmentChoiceType.prototype, "boardId", void 0);
23
- tslib_1.__decorate([
24
- (0, type_graphql_1.Field)(type => [String], { description: 'the axes this assignment constrained — why it was chosen' }),
25
- tslib_1.__metadata("design:type", Array)
26
- ], StationAssignmentChoiceType.prototype, "matchedOn", void 0);
27
- tslib_1.__decorate([
28
- (0, type_graphql_1.Field)(type => [String], { description: 'equally specific assignments nothing separated — write a priority' }),
29
- tslib_1.__metadata("design:type", Array)
30
- ], StationAssignmentChoiceType.prototype, "ambiguousWith", void 0);
31
- exports.StationAssignmentChoiceType = StationAssignmentChoiceType = tslib_1.__decorate([
32
- (0, type_graphql_1.ObjectType)({ description: 'Which board this station shows, and why that one' })
33
- ], StationAssignmentChoiceType);
34
- /**
35
- * Which axis values actually exist in the masters — **one pass, however many assignments.**
36
- *
37
- * ── Why the screen has to say this ─────────────────────────────────────────
38
- * Every axis is typed. A mistyped one is stored, listed, and **matches nothing** — the assignment
39
- * looks fine and the tablet shows nothing, with no way to tell which of the seven fields is wrong.
40
- * The board was already checked this way; the axes are the ones people get wrong more often, since
41
- * there are seven of them and they are keys, not names people remember.
42
- *
43
- * ── Checked here, not by narrowing the input ───────────────────────────────
44
- * Dropdowns were the alternative. This catches values that arrived any other way too — a seed, an
45
- * import, a hand-edited row — and does not need the whole equipment list in a select on a plant with
46
- * a thousand machines.
47
- *
48
- * ── A class that exists but has no members is still valid ──────────────────
49
- * So classes are checked against the class masters, not against membership. "Every welder gets this
50
- * screen" is a fair thing to declare before the first welder is hired.
51
- *
52
- * ── `kind` counts as a class ───────────────────────────────────────────────
53
- * Equipment membership is `equipmentClassIds ∪ {kind}` (the contract's own rule), so a value used as
54
- * some machine's `kind` is a working selector even when no `EquipmentClass` row declares it.
55
- * Complaining about it would be a false alarm, and false alarms kill the true ones.
56
- */
57
- async function axesThatExist(domainId, rows) {
58
- const values = (pick) => [...new Set(rows.map(pick).filter((one) => Boolean(one)))];
59
- const namesOf = async (entity, names) => names.length
60
- ? new Set((await (0, shell_1.getRepository)(entity).find({
61
- where: { domain: { id: domainId }, name: (0, typeorm_1.In)(names) },
62
- select: ['name']
63
- })).map((one) => one.name))
64
- : new Set();
65
- const equipmentClassNames = values(row => row.equipmentClassId);
66
- const [equipment, equipmentClasses, kinds, lines, operations, routes, materialClasses, personnelClasses] = await Promise.all([
67
- namesOf(ops_master_1.Equipment, values(row => row.equipmentName)),
68
- namesOf(ops_master_1.EquipmentClass, equipmentClassNames),
69
- /* `kind` is not a name column, so it is asked for separately. */
70
- equipmentClassNames.length
71
- ? (0, shell_1.getRepository)(ops_master_1.Equipment)
72
- .find({ where: { domain: { id: domainId }, kind: (0, typeorm_1.In)(equipmentClassNames) }, select: ['kind'] })
73
- .then(found => new Set(found.map(one => one.kind)))
74
- : Promise.resolve(new Set()),
75
- namesOf(ops_master_1.OperationalLocation, values(row => row.workCenterName)),
76
- namesOf(ops_master_1.OperationDefinition, values(row => row.operationKey)),
77
- namesOf(ops_master_1.RouteDefinition, values(row => row.routeKey)),
78
- namesOf(ops_master_1.MaterialClass, values(row => row.materialClassId)),
79
- namesOf(ops_master_1.PersonnelClass, values(row => row.personnelClassId))
80
- ]);
81
- return {
82
- equipmentName: equipment,
83
- equipmentClassId: new Set([...equipmentClasses, ...kinds]),
84
- workCenterName: lines,
85
- operationKey: operations,
86
- routeKey: routes,
87
- materialClassId: materialClasses,
88
- personnelClassId: personnelClasses
89
- };
90
- }
91
- /** 어느 축이 어긋났는지 사용자의 말로. 축 이름을 그대로 보이지 않는다 — 화면이 번역한다. */
92
- const AXIS_COMPLAINT = {
93
- equipmentName: 'no equipment by that name',
94
- equipmentClassId: 'no equipment class or kind by that name',
95
- workCenterName: 'no location by that name',
96
- operationKey: 'no operation by that name',
97
- routeKey: 'no route by that name',
98
- materialClassId: 'no material class by that name',
99
- personnelClassId: 'no personnel class by that name'
100
- };
101
- let StationAssignmentRowType = class StationAssignmentRowType {
102
- };
103
- exports.StationAssignmentRowType = StationAssignmentRowType;
104
- tslib_1.__decorate([
105
- (0, type_graphql_1.Field)(),
106
- tslib_1.__metadata("design:type", String)
107
- ], StationAssignmentRowType.prototype, "name", void 0);
108
- tslib_1.__decorate([
109
- (0, type_graphql_1.Field)({ nullable: true }),
110
- tslib_1.__metadata("design:type", String)
111
- ], StationAssignmentRowType.prototype, "description", void 0);
112
- tslib_1.__decorate([
113
- (0, type_graphql_1.Field)({ description: 'the station screen to draw, by name' }),
114
- tslib_1.__metadata("design:type", String)
115
- ], StationAssignmentRowType.prototype, "boardId", void 0);
116
- tslib_1.__decorate([
117
- (0, type_graphql_1.Field)({ nullable: true, description: 'the version currently released — absent when none is' }),
118
- (0, type_graphql_1.Field)({ nullable: true, description: "the board's name, so the list reads as words not ids" }),
119
- tslib_1.__metadata("design:type", String)
120
- ], StationAssignmentRowType.prototype, "boardName", void 0);
121
- tslib_1.__decorate([
122
- (0, type_graphql_1.Field)({ nullable: true }),
123
- tslib_1.__metadata("design:type", String)
124
- ], StationAssignmentRowType.prototype, "equipmentName", void 0);
125
- tslib_1.__decorate([
126
- (0, type_graphql_1.Field)({ nullable: true }),
127
- tslib_1.__metadata("design:type", String)
128
- ], StationAssignmentRowType.prototype, "equipmentClassId", void 0);
129
- tslib_1.__decorate([
130
- (0, type_graphql_1.Field)({ nullable: true }),
131
- tslib_1.__metadata("design:type", String)
132
- ], StationAssignmentRowType.prototype, "workCenterName", void 0);
133
- tslib_1.__decorate([
134
- (0, type_graphql_1.Field)({ nullable: true }),
135
- tslib_1.__metadata("design:type", String)
136
- ], StationAssignmentRowType.prototype, "operationKey", void 0);
137
- tslib_1.__decorate([
138
- (0, type_graphql_1.Field)({ nullable: true }),
139
- tslib_1.__metadata("design:type", String)
140
- ], StationAssignmentRowType.prototype, "routeKey", void 0);
141
- tslib_1.__decorate([
142
- (0, type_graphql_1.Field)({ nullable: true }),
143
- tslib_1.__metadata("design:type", String)
144
- ], StationAssignmentRowType.prototype, "materialClassId", void 0);
145
- tslib_1.__decorate([
146
- (0, type_graphql_1.Field)({ nullable: true }),
147
- tslib_1.__metadata("design:type", String)
148
- ], StationAssignmentRowType.prototype, "personnelClassId", void 0);
149
- tslib_1.__decorate([
150
- (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true }),
151
- tslib_1.__metadata("design:type", Number)
152
- ], StationAssignmentRowType.prototype, "priority", void 0);
153
- tslib_1.__decorate([
154
- (0, type_graphql_1.Field)({ nullable: true }),
155
- tslib_1.__metadata("design:type", Date)
156
- ], StationAssignmentRowType.prototype, "effectiveStart", void 0);
157
- tslib_1.__decorate([
158
- (0, type_graphql_1.Field)({ nullable: true }),
159
- tslib_1.__metadata("design:type", Date
160
- /**
161
- * 지금 이 줄이 화면을 그릴 수 있나.
162
- *
163
- * ── 왜 선언 옆에 현실을 함께 적나 ────────────────────────────────────────
164
- * 없어진 보드를 가리키는 배정은 **목록에는 멀쩡히 서 있고 현장에는 아무것도 안 뜬다.** 유효 기간이
165
- * 지난 줄도 같다. 화면이 그것을 말하지 않으면 관리자는 자기가 적은 것이 왜 안 먹는지 알 길이 없다.
166
- */
167
- )
168
- ], StationAssignmentRowType.prototype, "effectiveEnd", void 0);
169
- tslib_1.__decorate([
170
- (0, type_graphql_1.Field)({ description: 'can this row draw anything right now' }),
171
- tslib_1.__metadata("design:type", Boolean)
172
- ], StationAssignmentRowType.prototype, "usable", void 0);
173
- tslib_1.__decorate([
174
- (0, type_graphql_1.Field)({ nullable: true, description: 'why not, in words the reader can act on' }),
175
- tslib_1.__metadata("design:type", String)
176
- ], StationAssignmentRowType.prototype, "because", void 0);
177
- exports.StationAssignmentRowType = StationAssignmentRowType = tslib_1.__decorate([
178
- (0, type_graphql_1.ObjectType)({ description: 'one screen assignment, with whether it can actually draw anything' })
179
- ], StationAssignmentRowType);
180
- let StationAssignmentSetupType = class StationAssignmentSetupType {
181
- };
182
- exports.StationAssignmentSetupType = StationAssignmentSetupType;
183
- tslib_1.__decorate([
184
- (0, type_graphql_1.Field)(type => [StationAssignmentRowType]),
185
- tslib_1.__metadata("design:type", Array)
186
- ], StationAssignmentSetupType.prototype, "screens", void 0);
187
- tslib_1.__decorate([
188
- (0, type_graphql_1.Field)(type => [String], { description: 'assignments that constrain nothing — more than one always ties' }),
189
- tslib_1.__metadata("design:type", Array)
190
- ], StationAssignmentSetupType.prototype, "unconstrained", void 0);
191
- exports.StationAssignmentSetupType = StationAssignmentSetupType = tslib_1.__decorate([
192
- (0, type_graphql_1.ObjectType)({ description: 'every screen assignment, and what a person has to settle' })
193
- ], StationAssignmentSetupType);
194
- let StationAssignmentInput = class StationAssignmentInput {
195
- };
196
- exports.StationAssignmentInput = StationAssignmentInput;
197
- tslib_1.__decorate([
198
- (0, type_graphql_1.Field)(),
199
- tslib_1.__metadata("design:type", String)
200
- ], StationAssignmentInput.prototype, "name", void 0);
201
- tslib_1.__decorate([
202
- (0, type_graphql_1.Field)({ nullable: true }),
203
- tslib_1.__metadata("design:type", String)
204
- ], StationAssignmentInput.prototype, "description", void 0);
205
- tslib_1.__decorate([
206
- (0, type_graphql_1.Field)({ description: 'the station screen to draw, by name' }),
207
- tslib_1.__metadata("design:type", String)
208
- ], StationAssignmentInput.prototype, "boardId", void 0);
209
- tslib_1.__decorate([
210
- (0, type_graphql_1.Field)({ nullable: true }),
211
- tslib_1.__metadata("design:type", String)
212
- ], StationAssignmentInput.prototype, "equipmentName", void 0);
213
- tslib_1.__decorate([
214
- (0, type_graphql_1.Field)({ nullable: true }),
215
- tslib_1.__metadata("design:type", String)
216
- ], StationAssignmentInput.prototype, "equipmentClassId", void 0);
217
- tslib_1.__decorate([
218
- (0, type_graphql_1.Field)({ nullable: true }),
219
- tslib_1.__metadata("design:type", String)
220
- ], StationAssignmentInput.prototype, "workCenterName", void 0);
221
- tslib_1.__decorate([
222
- (0, type_graphql_1.Field)({ nullable: true }),
223
- tslib_1.__metadata("design:type", String)
224
- ], StationAssignmentInput.prototype, "operationKey", void 0);
225
- tslib_1.__decorate([
226
- (0, type_graphql_1.Field)({ nullable: true }),
227
- tslib_1.__metadata("design:type", String)
228
- ], StationAssignmentInput.prototype, "routeKey", void 0);
229
- tslib_1.__decorate([
230
- (0, type_graphql_1.Field)({ nullable: true }),
231
- tslib_1.__metadata("design:type", String)
232
- ], StationAssignmentInput.prototype, "materialClassId", void 0);
233
- tslib_1.__decorate([
234
- (0, type_graphql_1.Field)({ nullable: true }),
235
- tslib_1.__metadata("design:type", String)
236
- ], StationAssignmentInput.prototype, "personnelClassId", void 0);
237
- tslib_1.__decorate([
238
- (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'higher wins when neither screen is more specific' }),
239
- tslib_1.__metadata("design:type", Number)
240
- ], StationAssignmentInput.prototype, "priority", void 0);
241
- tslib_1.__decorate([
242
- (0, type_graphql_1.Field)({ nullable: true }),
243
- tslib_1.__metadata("design:type", Date)
244
- ], StationAssignmentInput.prototype, "effectiveStart", void 0);
245
- tslib_1.__decorate([
246
- (0, type_graphql_1.Field)({ nullable: true }),
247
- tslib_1.__metadata("design:type", Date)
248
- ], StationAssignmentInput.prototype, "effectiveEnd", void 0);
249
- exports.StationAssignmentInput = StationAssignmentInput = tslib_1.__decorate([
250
- (0, type_graphql_1.InputType)({ description: 'which board a station shows, and when' })
251
- ], StationAssignmentInput);
252
- let StationAssignmentResolver = class StationAssignmentResolver {
253
- /**
254
- * 이 작업대가 지금 그릴 보드.
255
- *
256
- * **배정이 없으면 아무것도 돌려주지 않는다.** 기본 화면을 내놓지 않는 이유는, 배정하지 않아도 뭔가
257
- * 뜨면 공장이 「배정은 안 해도 된다」를 배우고, 그 뒤로는 벽에 걸린 것을 아무도 믿지 않게 되기 때문이다.
258
- */
259
- async stationAssignment(equipmentName, context, operationKey, operatorName, jobOrderName) {
260
- const { domain } = context.state;
261
- const chosen = await (0, station_assignment_service_1.stationAssignmentFor)((0, shell_1.getRepository)(station_screen_assignment_1.StationScreenAssignment).manager, domain, equipmentName, {
262
- operationKey,
263
- operatorName,
264
- jobOrderName
265
- });
266
- return chosen;
267
- }
268
- /**
269
- * 배정 전부 — **선언 옆에 현실을 함께.**
270
- *
271
- * ── 목록의 순서는 승부가 아니다 ─────────────────────────────────────────
272
- * 이기는 것은 얼마나 구체적인가이고 우선순위는 동점일 때만 쓴다. 목록이 승부를 말하는 척하면 관리자가
273
- * 맨 위 줄이 이긴다고 읽는다. 그래서 이름순으로만 낸다 — 어느 줄이 이기는지는 **물어봐야** 안다
274
- * (`stationScreen`).
275
- */
276
- async stationAssignmentSetup(context) {
277
- const { domain } = context.state;
278
- const now = new Date();
279
- const rows = await (0, shell_1.getRepository)(station_screen_assignment_1.StationScreenAssignment).find({
280
- where: { domain: { id: domain.id } },
281
- order: { name: 'ASC' }
282
- });
283
- /*
284
- * Does the board this row points at still exist?
285
- *
286
- * ── What is missing here, and why it is missing rather than guessed ──────────────────────
287
- * The question that matters more is "has it ever been released", because a station draws the
288
- * published snapshot and never the working copy — a board nobody released draws nothing while
289
- * this row looks fine. Answering it needs `BoardHistory`, which board-service registers as an
290
- * entity but does not export by name (`service/index.ts` exports `./board/board.js` only).
291
- *
292
- * `Board.state` cannot stand in: a released board that has since been edited reads 'draft'
293
- * while its published version is still on the floor, and `Board.version` defaults to 1 whether
294
- * or not a release ever happened. So the check is absent rather than approximated — a listing
295
- * that paints a row green on a guess is worse than one that stays quiet.
296
- */
297
- const boardIds = [...new Set(rows.map(row => row.boardId))];
298
- const boards = boardIds.length ? await (0, shell_1.getRepository)(board_service_1.Board).find({ where: { domain: { id: domain.id }, id: (0, typeorm_1.In)(boardIds) } }) : [];
299
- const nameOf = new Map(boards.map(one => [one.id, one.name]));
300
- const exists = await axesThatExist(domain.id, rows);
301
- const screens = rows.map(row => {
302
- const boardName = nameOf.get(row.boardId);
303
- const notYet = row.effectiveStart && row.effectiveStart.getTime() > now.getTime();
304
- const over = row.effectiveEnd && row.effectiveEnd.getTime() <= now.getTime();
305
- /*
306
- * 어긋난 축을 **모두** 모은다. 하나만 말하면 고치고 저장한 뒤에 또 다른 것이 나온다 — 일곱 칸이
307
- * 있는 화면에서 그것은 일곱 번의 왕복이다.
308
- */
309
- const wrongAxes = Object.keys(AXIS_COMPLAINT)
310
- .filter(axis => row[axis] && !exists[axis].has(row[axis]))
311
- .map(axis => `${row[axis]}: ${AXIS_COMPLAINT[axis]}`);
312
- return {
313
- ...row,
314
- boardName,
315
- /*
316
- * 축이 어긋난 줄은 **쓸 수 없다.** 저장되고 목록에 서지만 아무 작업대에도 안 걸린다 — 「보드가
317
- * 없어졌다」와 같은 부류이고, 말하지 않으면 관리자가 일곱 칸 중 어느 것이 틀렸는지 알 길이 없다.
318
- */
319
- usable: Boolean(boardName) && !notYet && !over && !wrongAxes.length,
320
- because: !boardName
321
- ? 'that board is gone — nothing will be drawn here'
322
- : wrongAxes.length
323
- ? wrongAxes.join(' · ')
324
- : notYet
325
- ? 'not in effect yet'
326
- : over
327
- ? 'no longer in effect'
328
- : undefined
329
- };
330
- });
331
- return {
332
- screens,
333
- /*
334
- * 아무 축도 안 건 배정들. 하나면 「그 밖의 모든 작업대」라 쓸모가 있고, **둘 이상이면 언제나 동점**
335
- * 이다 — 우선순위 없이는 이름순으로 갈리고 아무도 그것을 예상하지 못한다.
336
- */
337
- unconstrained: screens
338
- .filter(row => !row.equipmentName && !row.equipmentClassId && !row.workCenterName && !row.operationKey && !row.routeKey && !row.materialClassId && !row.personnelClassId)
339
- .map(row => row.name)
340
- };
341
- }
342
- /**
343
- * 배정을 적거나 고친다.
344
- *
345
- * ── 왜 만드는 문이 함께 있어야 하나 ──────────────────────────────────────
346
- * 읽는 질의만 두면 배정을 만들 길이 SQL 뿐이다. 이 제품에서 가장 잦은 결함이 **선언은 있고 길이 없는**
347
- * 것이고, 관리 화면이 붙기 전이라도 문은 열려 있어야 한다.
348
- */
349
- async saveStationAssignment(context, patch) {
350
- const { domain, user, tx } = context.state;
351
- const repository = tx.getRepository(station_screen_assignment_1.StationScreenAssignment);
352
- /*
353
- * 끝이 시작보다 앞서면 이 배정은 **아무 때도 걸리지 않는다.** 그런데 목록에는 서 있어서, 관리자는
354
- * 적어 두었는데 화면이 안 바뀌는 것을 겪고 원인을 찾을 데가 없다.
355
- */
356
- if (patch.effectiveStart && patch.effectiveEnd && patch.effectiveEnd.getTime() <= patch.effectiveStart.getTime()) {
357
- throw new shell_1.Refusal('time-range-end-not-after-start', {}, 'the end time is not after the start time. enter a later end time.');
358
- }
359
- const found = await repository.findOne({ where: { domain: { id: domain.id }, name: patch.name } });
360
- return await repository.save({
361
- ...(found ?? {}),
362
- ...patch,
363
- domain,
364
- ...(found ? { updater: user } : { creator: user, updater: user })
365
- });
366
- }
367
- async deleteStationAssignment(context, name) {
368
- const { domain, tx } = context.state;
369
- const repository = tx.getRepository(station_screen_assignment_1.StationScreenAssignment);
370
- const found = await repository.findOne({ where: { domain: { id: domain.id }, name } });
371
- if (!found) {
372
- return false;
373
- }
374
- await repository.remove(found);
375
- return true;
376
- }
377
- };
378
- exports.StationAssignmentResolver = StationAssignmentResolver;
379
- tslib_1.__decorate([
380
- (0, type_graphql_1.Directive)('@privilege(category: "station", privilege: "query", domainOwnerGranted: true)'),
381
- (0, type_graphql_1.Query)(returns => StationAssignmentChoiceType, {
382
- nullable: true,
383
- description: 'the board assigned to this station right now — null when nothing is assigned'
384
- }),
385
- tslib_1.__param(0, (0, type_graphql_1.Arg)('equipmentName')),
386
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
387
- tslib_1.__param(2, (0, type_graphql_1.Arg)('operationKey', { nullable: true, description: 'the operation this station performs' })),
388
- tslib_1.__param(3, (0, type_graphql_1.Arg)('operatorName', { nullable: true, description: 'who is scanned in — personnel class axes need it' })),
389
- tslib_1.__param(4, (0, type_graphql_1.Arg)('jobOrderName', { nullable: true, description: 'the order picked but not yet started' })),
390
- tslib_1.__metadata("design:type", Function),
391
- tslib_1.__metadata("design:paramtypes", [String, Object, String, String, String]),
392
- tslib_1.__metadata("design:returntype", Promise)
393
- ], StationAssignmentResolver.prototype, "stationAssignment", null);
394
- tslib_1.__decorate([
395
- (0, type_graphql_1.Directive)('@privilege(category: "station", privilege: "query", domainOwnerGranted: true)'),
396
- (0, type_graphql_1.Query)(returns => StationAssignmentSetupType, { description: 'every screen assignment, with what a person must settle' }),
397
- tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
398
- tslib_1.__metadata("design:type", Function),
399
- tslib_1.__metadata("design:paramtypes", [Object]),
400
- tslib_1.__metadata("design:returntype", Promise)
401
- ], StationAssignmentResolver.prototype, "stationAssignmentSetup", null);
402
- tslib_1.__decorate([
403
- (0, type_graphql_1.Directive)('@transaction'),
404
- (0, type_graphql_1.Directive)('@privilege(category: "station", privilege: "mutation", domainOwnerGranted: true)'),
405
- (0, type_graphql_1.Mutation)(returns => station_screen_assignment_1.StationScreenAssignment, { description: 'declare or change which board a station shows' }),
406
- tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
407
- tslib_1.__param(1, (0, type_graphql_1.Arg)('patch', type => StationAssignmentInput)),
408
- tslib_1.__metadata("design:type", Function),
409
- tslib_1.__metadata("design:paramtypes", [Object, StationAssignmentInput]),
410
- tslib_1.__metadata("design:returntype", Promise)
411
- ], StationAssignmentResolver.prototype, "saveStationAssignment", null);
412
- tslib_1.__decorate([
413
- (0, type_graphql_1.Directive)('@transaction'),
414
- (0, type_graphql_1.Directive)('@privilege(category: "station", privilege: "mutation", domainOwnerGranted: true)'),
415
- (0, type_graphql_1.Mutation)(returns => Boolean, { description: 'remove a screen assignment' }),
416
- tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
417
- tslib_1.__param(1, (0, type_graphql_1.Arg)('name')),
418
- tslib_1.__metadata("design:type", Function),
419
- tslib_1.__metadata("design:paramtypes", [Object, String]),
420
- tslib_1.__metadata("design:returntype", Promise)
421
- ], StationAssignmentResolver.prototype, "deleteStationAssignment", null);
422
- exports.StationAssignmentResolver = StationAssignmentResolver = tslib_1.__decorate([
423
- (0, type_graphql_1.Resolver)(station_screen_assignment_1.StationScreenAssignment),
424
- (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
425
- ], StationAssignmentResolver);
426
- //# sourceMappingURL=station-assignment-resolver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"station-assignment-resolver.js","sourceRoot":"","sources":["../../../server/service/station/station-assignment-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Field, InputType, Int, Mutation, ObjectType, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { getRepository, Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { In } from 'typeorm'\n\nimport { Equipment, EquipmentClass, MaterialClass, OperationDefinition, OperationalLocation, PersonnelClass, RouteDefinition } from '@things-factory/ops-master'\n\nimport { Board } from '@things-factory/board-service'\n\nimport { StationScreenAssignment } from './station-screen-assignment'\nimport { stationAssignmentFor } from './station-assignment-service'\n\n@ObjectType({ description: 'Which board this station shows, and why that one' })\nexport class StationAssignmentChoiceType {\n @Field({ description: 'the assignment that won' })\n name: string\n\n @Field({ description: 'the station screen to draw, by name' })\n boardId: string\n\n /**\n * 무엇으로 골랐나.\n *\n * 「왜 이 화면인가」에 답하려고 함께 낸다. 이것이 없으면 배정이 여러 줄일 때 관리자가 자기가 고친 줄이\n * 이 태블릿에 걸리는 줄인지 확인할 방법이 없다.\n */\n @Field(type => [String], { description: 'the axes this assignment constrained — why it was chosen' })\n matchedOn: string[]\n\n /**\n * 같은 만큼 구체적인데 아무것도 가려 주지 못한 다른 배정들.\n *\n * 비어 있는 것이 정상이다. 비어 있지 않으면 **공장이 같은 물음에 답을 둘 갖고 있다**는 뜻이고, 그리는\n * 화면은 정해져 있지만 임의로 정해진 것이다 — 누군가 우선순위를 적어야 한다. 조용히 한쪽을 버리면\n * 관리자는 다른 줄을 고쳐도 아무 일이 안 일어나는 것을 겪는다.\n */\n @Field(type => [String], { description: 'equally specific assignments nothing separated — write a priority' })\n ambiguousWith: string[]\n}\n\n/**\n * Which axis values actually exist in the masters — **one pass, however many assignments.**\n *\n * ── Why the screen has to say this ─────────────────────────────────────────\n * Every axis is typed. A mistyped one is stored, listed, and **matches nothing** — the assignment\n * looks fine and the tablet shows nothing, with no way to tell which of the seven fields is wrong.\n * The board was already checked this way; the axes are the ones people get wrong more often, since\n * there are seven of them and they are keys, not names people remember.\n *\n * ── Checked here, not by narrowing the input ───────────────────────────────\n * Dropdowns were the alternative. This catches values that arrived any other way too — a seed, an\n * import, a hand-edited row — and does not need the whole equipment list in a select on a plant with\n * a thousand machines.\n *\n * ── A class that exists but has no members is still valid ──────────────────\n * So classes are checked against the class masters, not against membership. \"Every welder gets this\n * screen\" is a fair thing to declare before the first welder is hired.\n *\n * ── `kind` counts as a class ───────────────────────────────────────────────\n * Equipment membership is `equipmentClassIds ∪ {kind}` (the contract's own rule), so a value used as\n * some machine's `kind` is a working selector even when no `EquipmentClass` row declares it.\n * Complaining about it would be a false alarm, and false alarms kill the true ones.\n */\nasync function axesThatExist(domainId: string, rows: readonly StationScreenAssignment[]) {\n const values = (pick: (row: StationScreenAssignment) => string | undefined) => [...new Set(rows.map(pick).filter((one): one is string => Boolean(one)))]\n\n const namesOf = async (entity: any, names: string[]) =>\n names.length\n ? new Set(\n (\n await getRepository(entity).find({\n where: { domain: { id: domainId }, name: In(names) },\n select: ['name']\n })\n ).map((one: any) => one.name)\n )\n : new Set<string>()\n\n const equipmentClassNames = values(row => row.equipmentClassId)\n\n const [equipment, equipmentClasses, kinds, lines, operations, routes, materialClasses, personnelClasses] = await Promise.all([\n namesOf(\n Equipment,\n values(row => row.equipmentName)\n ),\n namesOf(EquipmentClass, equipmentClassNames),\n /* `kind` is not a name column, so it is asked for separately. */\n equipmentClassNames.length\n ? getRepository(Equipment)\n .find({ where: { domain: { id: domainId }, kind: In(equipmentClassNames) }, select: ['kind'] })\n .then(found => new Set(found.map(one => one.kind)))\n : Promise.resolve(new Set<string>()),\n namesOf(\n OperationalLocation,\n values(row => row.workCenterName)\n ),\n namesOf(\n OperationDefinition,\n values(row => row.operationKey)\n ),\n namesOf(\n RouteDefinition,\n values(row => row.routeKey)\n ),\n namesOf(\n MaterialClass,\n values(row => row.materialClassId)\n ),\n namesOf(\n PersonnelClass,\n values(row => row.personnelClassId)\n )\n ])\n\n return {\n equipmentName: equipment,\n equipmentClassId: new Set([...equipmentClasses, ...kinds]),\n workCenterName: lines,\n operationKey: operations,\n routeKey: routes,\n materialClassId: materialClasses,\n personnelClassId: personnelClasses\n }\n}\n\n/** 어느 축이 어긋났는지 사용자의 말로. 축 이름을 그대로 보이지 않는다 — 화면이 번역한다. */\nconst AXIS_COMPLAINT: Record<string, string> = {\n equipmentName: 'no equipment by that name',\n equipmentClassId: 'no equipment class or kind by that name',\n workCenterName: 'no location by that name',\n operationKey: 'no operation by that name',\n routeKey: 'no route by that name',\n materialClassId: 'no material class by that name',\n personnelClassId: 'no personnel class by that name'\n}\n\n@ObjectType({ description: 'one screen assignment, with whether it can actually draw anything' })\nexport class StationAssignmentRowType {\n @Field()\n name: string\n\n @Field({ nullable: true })\n description?: string\n\n @Field({ description: 'the station screen to draw, by name' })\n boardId: string\n\n /**\n * 그 화면이 지금 현장에 나가 있는 버전.\n *\n * 없으면 **나간 버전이 없다**는 뜻이고, 그러면 이 배정은 아무것도 못 그린다 — 아래 `usable` 이 그것을\n * 말한다. 초안이 있어도 대신 내밀지 않는다: 작업대가 그리다 만 화면 앞에서 실적을 남기게 된다.\n */\n @Field({ nullable: true, description: 'the version currently released — absent when none is' })\n @Field({ nullable: true, description: \"the board's name, so the list reads as words not ids\" })\n boardName?: string\n\n @Field({ nullable: true })\n equipmentName?: string\n\n @Field({ nullable: true })\n equipmentClassId?: string\n\n @Field({ nullable: true })\n workCenterName?: string\n\n @Field({ nullable: true })\n operationKey?: string\n\n @Field({ nullable: true })\n routeKey?: string\n\n @Field({ nullable: true })\n materialClassId?: string\n\n @Field({ nullable: true })\n personnelClassId?: string\n\n @Field(type => Int, { nullable: true })\n priority?: number\n\n @Field({ nullable: true })\n effectiveStart?: Date\n\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n /**\n * 지금 이 줄이 화면을 그릴 수 있나.\n *\n * ── 왜 선언 옆에 현실을 함께 적나 ────────────────────────────────────────\n * 없어진 보드를 가리키는 배정은 **목록에는 멀쩡히 서 있고 현장에는 아무것도 안 뜬다.** 유효 기간이\n * 지난 줄도 같다. 화면이 그것을 말하지 않으면 관리자는 자기가 적은 것이 왜 안 먹는지 알 길이 없다.\n */\n @Field({ description: 'can this row draw anything right now' })\n usable: boolean\n\n /** 못 쓰면 왜 못 쓰나 — 사용자가 읽을 문장. 쓸 수 있으면 없다. */\n @Field({ nullable: true, description: 'why not, in words the reader can act on' })\n because?: string\n}\n\n@ObjectType({ description: 'every screen assignment, and what a person has to settle' })\nexport class StationAssignmentSetupType {\n @Field(type => [StationAssignmentRowType])\n screens: StationAssignmentRowType[]\n\n /**\n * 아무 축도 걸지 않은 배정의 이름들.\n *\n * 하나면 「그 밖의 모든 작업대」라는 뜻이라 쓸모가 있다. **둘 이상이면 둘 다 모든 작업대에 들어맞고**\n * 언제나 동점이다 — 우선순위 없이는 이름순으로 갈린다. 화면이 짚어 준다.\n */\n @Field(type => [String], { description: 'assignments that constrain nothing — more than one always ties' })\n unconstrained: string[]\n}\n\n@InputType({ description: 'which board a station shows, and when' })\nexport class StationAssignmentInput {\n @Field()\n name: string\n\n @Field({ nullable: true })\n description?: string\n\n @Field({ description: 'the station screen to draw, by name' })\n boardId: string\n\n @Field({ nullable: true })\n equipmentName?: string\n\n @Field({ nullable: true })\n equipmentClassId?: string\n\n @Field({ nullable: true })\n workCenterName?: string\n\n @Field({ nullable: true })\n operationKey?: string\n\n @Field({ nullable: true })\n routeKey?: string\n\n @Field({ nullable: true })\n materialClassId?: string\n\n @Field({ nullable: true })\n personnelClassId?: string\n\n @Field(type => Int, { nullable: true, description: 'higher wins when neither screen is more specific' })\n priority?: number\n\n @Field({ nullable: true })\n effectiveStart?: Date\n\n @Field({ nullable: true })\n effectiveEnd?: Date\n}\n\n@Resolver(StationScreenAssignment)\n@UseMiddleware(SpeakRefusals)\nexport class StationAssignmentResolver {\n /**\n * 이 작업대가 지금 그릴 보드.\n *\n * **배정이 없으면 아무것도 돌려주지 않는다.** 기본 화면을 내놓지 않는 이유는, 배정하지 않아도 뭔가\n * 뜨면 공장이 「배정은 안 해도 된다」를 배우고, 그 뒤로는 벽에 걸린 것을 아무도 믿지 않게 되기 때문이다.\n */\n @Directive('@privilege(category: \"station\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => StationAssignmentChoiceType, {\n nullable: true,\n description: 'the board assigned to this station right now — null when nothing is assigned'\n })\n async stationAssignment(\n @Arg('equipmentName') equipmentName: string,\n @Ctx() context: any,\n @Arg('operationKey', { nullable: true, description: 'the operation this station performs' })\n operationKey?: string,\n @Arg('operatorName', { nullable: true, description: 'who is scanned in — personnel class axes need it' })\n operatorName?: string,\n @Arg('jobOrderName', { nullable: true, description: 'the order picked but not yet started' })\n jobOrderName?: string\n ): Promise<StationAssignmentChoiceType | undefined> {\n const { domain } = context.state\n\n const chosen = await stationAssignmentFor(getRepository(StationScreenAssignment).manager, domain, equipmentName, {\n operationKey,\n operatorName,\n jobOrderName\n })\n\n return chosen as StationAssignmentChoiceType | undefined\n }\n\n /**\n * 배정 전부 — **선언 옆에 현실을 함께.**\n *\n * ── 목록의 순서는 승부가 아니다 ─────────────────────────────────────────\n * 이기는 것은 얼마나 구체적인가이고 우선순위는 동점일 때만 쓴다. 목록이 승부를 말하는 척하면 관리자가\n * 맨 위 줄이 이긴다고 읽는다. 그래서 이름순으로만 낸다 — 어느 줄이 이기는지는 **물어봐야** 안다\n * (`stationScreen`).\n */\n @Directive('@privilege(category: \"station\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => StationAssignmentSetupType, { description: 'every screen assignment, with what a person must settle' })\n async stationAssignmentSetup(@Ctx() context: any): Promise<StationAssignmentSetupType> {\n const { domain } = context.state\n const now = new Date()\n\n const rows = await getRepository(StationScreenAssignment).find({\n where: { domain: { id: domain.id } },\n order: { name: 'ASC' }\n })\n\n /*\n * Does the board this row points at still exist?\n *\n * ── What is missing here, and why it is missing rather than guessed ──────────────────────\n * The question that matters more is \"has it ever been released\", because a station draws the\n * published snapshot and never the working copy — a board nobody released draws nothing while\n * this row looks fine. Answering it needs `BoardHistory`, which board-service registers as an\n * entity but does not export by name (`service/index.ts` exports `./board/board.js` only).\n *\n * `Board.state` cannot stand in: a released board that has since been edited reads 'draft'\n * while its published version is still on the floor, and `Board.version` defaults to 1 whether\n * or not a release ever happened. So the check is absent rather than approximated — a listing\n * that paints a row green on a guess is worse than one that stays quiet.\n */\n const boardIds = [...new Set(rows.map(row => row.boardId))]\n\n const boards = boardIds.length ? await getRepository(Board).find({ where: { domain: { id: domain.id }, id: In(boardIds) } }) : []\n const nameOf = new Map(boards.map(one => [one.id, one.name]))\n\n const exists = await axesThatExist(domain.id, rows)\n\n const screens = rows.map(row => {\n const boardName = nameOf.get(row.boardId)\n const notYet = row.effectiveStart && row.effectiveStart.getTime() > now.getTime()\n const over = row.effectiveEnd && row.effectiveEnd.getTime() <= now.getTime()\n\n /*\n * 어긋난 축을 **모두** 모은다. 하나만 말하면 고치고 저장한 뒤에 또 다른 것이 나온다 — 일곱 칸이\n * 있는 화면에서 그것은 일곱 번의 왕복이다.\n */\n const wrongAxes = (Object.keys(AXIS_COMPLAINT) as (keyof typeof exists)[])\n .filter(axis => row[axis] && !exists[axis].has(row[axis] as string))\n .map(axis => `${row[axis]}: ${AXIS_COMPLAINT[axis]}`)\n\n return {\n ...row,\n boardName,\n /*\n * 축이 어긋난 줄은 **쓸 수 없다.** 저장되고 목록에 서지만 아무 작업대에도 안 걸린다 — 「보드가\n * 없어졌다」와 같은 부류이고, 말하지 않으면 관리자가 일곱 칸 중 어느 것이 틀렸는지 알 길이 없다.\n */\n usable: Boolean(boardName) && !notYet && !over && !wrongAxes.length,\n because: !boardName\n ? 'that board is gone — nothing will be drawn here'\n : wrongAxes.length\n ? wrongAxes.join(' · ')\n : notYet\n ? 'not in effect yet'\n : over\n ? 'no longer in effect'\n : undefined\n } as StationAssignmentRowType\n })\n\n return {\n screens,\n /*\n * 아무 축도 안 건 배정들. 하나면 「그 밖의 모든 작업대」라 쓸모가 있고, **둘 이상이면 언제나 동점**\n * 이다 — 우선순위 없이는 이름순으로 갈리고 아무도 그것을 예상하지 못한다.\n */\n unconstrained: screens\n .filter(\n row =>\n !row.equipmentName && !row.equipmentClassId && !row.workCenterName && !row.operationKey && !row.routeKey && !row.materialClassId && !row.personnelClassId\n )\n .map(row => row.name)\n }\n }\n\n /**\n * 배정을 적거나 고친다.\n *\n * ── 왜 만드는 문이 함께 있어야 하나 ──────────────────────────────────────\n * 읽는 질의만 두면 배정을 만들 길이 SQL 뿐이다. 이 제품에서 가장 잦은 결함이 **선언은 있고 길이 없는**\n * 것이고, 관리 화면이 붙기 전이라도 문은 열려 있어야 한다.\n */\n @Directive('@transaction')\n @Directive('@privilege(category: \"station\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => StationScreenAssignment, { description: 'declare or change which board a station shows' })\n async saveStationAssignment(@Ctx() context: any, @Arg('patch', type => StationAssignmentInput) patch: StationAssignmentInput): Promise<StationScreenAssignment> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(StationScreenAssignment)\n\n /*\n * 끝이 시작보다 앞서면 이 배정은 **아무 때도 걸리지 않는다.** 그런데 목록에는 서 있어서, 관리자는\n * 적어 두었는데 화면이 안 바뀌는 것을 겪고 원인을 찾을 데가 없다.\n */\n if (patch.effectiveStart && patch.effectiveEnd && patch.effectiveEnd.getTime() <= patch.effectiveStart.getTime()) {\n throw new Refusal('time-range-end-not-after-start', {}, 'the end time is not after the start time. enter a later end time.')\n }\n\n const found = await repository.findOne({ where: { domain: { id: domain.id }, name: patch.name } })\n\n return await repository.save({\n ...(found ?? {}),\n ...patch,\n domain,\n ...(found ? { updater: user } : { creator: user, updater: user })\n } as any)\n }\n\n @Directive('@transaction')\n @Directive('@privilege(category: \"station\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => Boolean, { description: 'remove a screen assignment' })\n async deleteStationAssignment(@Ctx() context: any, @Arg('name') name: string): Promise<boolean> {\n const { domain, tx } = context.state\n const repository = tx.getRepository(StationScreenAssignment)\n\n const found = await repository.findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!found) {\n return false\n }\n\n await repository.remove(found)\n\n return true\n }\n}\n"]}