@things-factory/station 10.1.223 → 10.1.227
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/activities/activity-station-screen-release.d.ts +3 -3
- package/dist-server/activities/activity-station-screen-release.js +7 -7
- package/dist-server/activities/activity-station-screen-release.js.map +1 -1
- package/dist-server/service/station/screen-model-digest.d.ts +3 -3
- package/dist-server/service/station/screen-model-digest.js +3 -3
- package/dist-server/service/station/screen-model-digest.js.map +1 -1
- package/dist-server/service/station/station-assignment-choice.d.ts +1 -1
- package/dist-server/service/station/station-assignment-choice.js.map +1 -1
- package/dist-server/service/station/station-assignment-resolver.d.ts +2 -2
- package/dist-server/service/station/station-assignment-resolver.js.map +1 -1
- package/dist-server/service/station/station-screen-assignment.js.map +1 -1
- package/dist-server/service/station/station-screen-control-withdrawal-resolver.d.ts +1 -1
- package/dist-server/service/station/station-screen-control-withdrawal-resolver.js +2 -2
- package/dist-server/service/station/station-screen-control-withdrawal-resolver.js.map +1 -1
- package/dist-server/service/station/station-screen-control-withdrawal.d.ts +1 -1
- package/dist-server/service/station/station-screen-control-withdrawal.js +1 -1
- package/dist-server/service/station/station-screen-control-withdrawal.js.map +1 -1
- package/dist-server/service/station/station-screen-release-record.d.ts +5 -5
- package/dist-server/service/station/station-screen-release-record.js +3 -3
- package/dist-server/service/station/station-screen-release-record.js.map +1 -1
- package/dist-server/service/station/station-screen-spec-resolver.js +2 -2
- package/dist-server/service/station/station-screen-spec-resolver.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
|
@@ -3,12 +3,12 @@ export declare const STATION_SCREEN_RELEASE_ACTIVITY = "StationScreenRelease";
|
|
|
3
3
|
* 결재자가 보는 화면의 요소 이름.
|
|
4
4
|
*
|
|
5
5
|
* 안 붙어 있으면 결재함이 「승인 대상 내용이 없습니다」만 그리고 **승인 단추는 그대로 눌린다.**
|
|
6
|
-
* 통제 문서에서 그것은 「검토했다」가 거짓이 되는 자리다 — 무엇을 바꾸는
|
|
6
|
+
* 통제 문서에서 그것은 「검토했다」가 거짓이 되는 자리다 — 무엇을 바꾸는 버전인지 안 보이는데 서명이
|
|
7
7
|
* 남는다. 출하 승인에서 실제로 그랬다(2026-09-16).
|
|
8
8
|
*/
|
|
9
9
|
export declare const STATION_SCREEN_RELEASE_VIEW = "activity-station-screen-release-view";
|
|
10
10
|
/**
|
|
11
|
-
* 화면 발행 승인 — **통제 문서 한
|
|
11
|
+
* 화면 발행 승인 — **통제 문서 한 버전을 내보내도 되는지 정하는 자리.**
|
|
12
12
|
*
|
|
13
13
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
14
14
|
* ── 왜 이 문이 있어야 하나 ────────────────────────────────────────────────
|
|
@@ -27,7 +27,7 @@ export declare const STATION_SCREEN_RELEASE_VIEW = "activity-station-screen-rele
|
|
|
27
27
|
*
|
|
28
28
|
* ── 이 문이 하는 일과 안 하는 일 ──────────────────────────────────────────
|
|
29
29
|
* ```
|
|
30
|
-
* 한다 양식 · 결재선 · 결재 화면 · 승인되면 그
|
|
30
|
+
* 한다 양식 · 결재선 · 결재 화면 · 승인되면 그 버전의 서명을 남긴다
|
|
31
31
|
* 안 한다 발행 자체를 막는 관문 — 그것은 station 레인이 들고, 이 서명을 읽어 판단한다
|
|
32
32
|
* ```
|
|
33
33
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -10,12 +10,12 @@ exports.STATION_SCREEN_RELEASE_ACTIVITY = 'StationScreenRelease';
|
|
|
10
10
|
* 결재자가 보는 화면의 요소 이름.
|
|
11
11
|
*
|
|
12
12
|
* 안 붙어 있으면 결재함이 「승인 대상 내용이 없습니다」만 그리고 **승인 단추는 그대로 눌린다.**
|
|
13
|
-
* 통제 문서에서 그것은 「검토했다」가 거짓이 되는 자리다 — 무엇을 바꾸는
|
|
13
|
+
* 통제 문서에서 그것은 「검토했다」가 거짓이 되는 자리다 — 무엇을 바꾸는 버전인지 안 보이는데 서명이
|
|
14
14
|
* 남는다. 출하 승인에서 실제로 그랬다(2026-09-16).
|
|
15
15
|
*/
|
|
16
16
|
exports.STATION_SCREEN_RELEASE_VIEW = 'activity-station-screen-release-view';
|
|
17
17
|
/**
|
|
18
|
-
* 승인이 끝나면 **그
|
|
18
|
+
* 승인이 끝나면 **그 버전에 대한 서명**을 남긴다.
|
|
19
19
|
*
|
|
20
20
|
* 상신할 때 굳힌 값(화면 이름 · 작업대 · 변경 요지 · 결재자에게 보인 문장)을 그대로 적는다. 여기서
|
|
21
21
|
* 다시 읽지 않는다 — 상신과 승인 사이에 화면이 또 바뀔 수 있고, 그러면 **결재자가 본 것과 남는 것이
|
|
@@ -31,8 +31,8 @@ async function callback(instance, _addendum, context) {
|
|
|
31
31
|
const { domain, user, tx } = context.state;
|
|
32
32
|
const repository = tx.getRepository(station_screen_release_record_1.StationScreenRelease);
|
|
33
33
|
/*
|
|
34
|
-
* 같은
|
|
35
|
-
* 같은 화면의 다른
|
|
34
|
+
* 같은 버전을 두 번 승인해도 앞의 서명을 덮지 않는다 — 서명은 쌓이는 기록이다. 정체는 **버전의 지문**이다:
|
|
35
|
+
* 같은 화면의 다른 버전은 새 서명이고, 앞 버전의 서명은 그대로 남는다(ADR-0047 ④).
|
|
36
36
|
*/
|
|
37
37
|
const already = await repository.findOne({ where: (0, station_screen_release_signature_1.signatureOf)(domain.id, release.boardId, release.modelDigest) });
|
|
38
38
|
if (already) {
|
|
@@ -44,7 +44,7 @@ async function callback(instance, _addendum, context) {
|
|
|
44
44
|
boardId: release.boardId,
|
|
45
45
|
screenName: release.screenName,
|
|
46
46
|
stationName: release.stationName,
|
|
47
|
-
/* 어느
|
|
47
|
+
/* 어느 버전에 서명했나. 상신 때 만든 지문을 그대로 적는다 — 여기서 다시 읽으면 그 사이의 버전이 된다. */
|
|
48
48
|
modelDigest: release.modelDigest,
|
|
49
49
|
/* 정한 시각과 정한 사람은 같은 사건이다. 둘 다 여기서 적는다. */
|
|
50
50
|
decidedAt: new Date(),
|
|
@@ -57,7 +57,7 @@ async function callback(instance, _addendum, context) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* 화면 발행 승인 — **통제 문서 한
|
|
60
|
+
* 화면 발행 승인 — **통제 문서 한 버전을 내보내도 되는지 정하는 자리.**
|
|
61
61
|
*
|
|
62
62
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
63
63
|
* ── 왜 이 문이 있어야 하나 ────────────────────────────────────────────────
|
|
@@ -76,7 +76,7 @@ async function callback(instance, _addendum, context) {
|
|
|
76
76
|
*
|
|
77
77
|
* ── 이 문이 하는 일과 안 하는 일 ──────────────────────────────────────────
|
|
78
78
|
* ```
|
|
79
|
-
* 한다 양식 · 결재선 · 결재 화면 · 승인되면 그
|
|
79
|
+
* 한다 양식 · 결재선 · 결재 화면 · 승인되면 그 버전의 서명을 남긴다
|
|
80
80
|
* 안 한다 발행 자체를 막는 관문 — 그것은 station 레인이 들고, 이 서명을 읽어 판단한다
|
|
81
81
|
* ```
|
|
82
82
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-station-screen-release.js","sourceRoot":"","sources":["../../server/activities/activity-station-screen-release.ts"],"names":[],"mappings":";;;AAAA,uDAAwH;AAExH,oGAAuF;AACvF,iDAA+D;AAC/D,0GAAiF;AAEpE,QAAA,+BAA+B,GAAG,sBAAsB,CAAA;AAErE;;;;;;GAMG;AACU,QAAA,2BAA2B,GAAG,sCAAsC,CAAA;AAEjF;;;;;;GAMG;AACH,KAAK,UAAU,QAAQ,CACrB,QAA0B,EAC1B,SAA8C,EAC9C,OAAwB;IAExB,IAAI,QAAQ,CAAC,KAAK,KAAK,iCAAsB,CAAC,KAAK;QAAE,OAAM;IAE3D,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IAEvC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,eAAO,CACf,iCAAiC,EACjC,EAAE,EACF,0EAA0E,CAC3E,CAAA;IACH,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,oDAAoB,CAAC,CAAA;IAEzD;;;OAGG;IACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAA,8CAAW,EAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IAEjH,IAAI,OAAO,EAAE,CAAC;QACZ,OAAM;IACR,CAAC;IAED,MAAM,UAAU,CAAC,IAAI,CAAC;QACpB,MAAM;QACN,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC
|
|
1
|
+
{"version":3,"file":"activity-station-screen-release.js","sourceRoot":"","sources":["../../server/activities/activity-station-screen-release.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActivityInstance, ActivityInstanceStatus, UpdateActivityInstanceStateAddendum } from '@things-factory/worklist'\n\nimport { StationScreenRelease } from '../service/station/station-screen-release-record'\nimport { Refusal, speaksRefusals } from '@things-factory/shell'\nimport { signatureOf } from '../service/station/station-screen-release-signature'\n\nexport const STATION_SCREEN_RELEASE_ACTIVITY = 'StationScreenRelease'\n\n/**\n * 결재자가 보는 화면의 요소 이름.\n *\n * 안 붙어 있으면 결재함이 「승인 대상 내용이 없습니다」만 그리고 **승인 단추는 그대로 눌린다.**\n * 통제 문서에서 그것은 「검토했다」가 거짓이 되는 자리다 — 무엇을 바꾸는 버전인지 안 보이는데 서명이\n * 남는다. 출하 승인에서 실제로 그랬다(2026-09-16).\n */\nexport const STATION_SCREEN_RELEASE_VIEW = 'activity-station-screen-release-view'\n\n/**\n * 승인이 끝나면 **그 버전에 대한 서명**을 남긴다.\n *\n * 상신할 때 굳힌 값(화면 이름 · 작업대 · 변경 요지 · 결재자에게 보인 문장)을 그대로 적는다. 여기서\n * 다시 읽지 않는다 — 상신과 승인 사이에 화면이 또 바뀔 수 있고, 그러면 **결재자가 본 것과 남는 것이\n * 달라진다.** 서명은 본 것에 대한 것이다.\n */\nasync function callback(\n instance: ActivityInstance,\n _addendum: UpdateActivityInstanceStateAddendum,\n context: ResolverContext\n) {\n if (instance.state !== ActivityInstanceStatus.Ended) return\n\n const release = instance.input?.release\n\n if (!release?.boardId) {\n throw new Refusal(\n 'station-release-carried-missing',\n {},\n '[screen release] the approved item has no screen, so it was not applied.'\n )\n }\n\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(StationScreenRelease)\n\n /*\n * 같은 버전을 두 번 승인해도 앞의 서명을 덮지 않는다 — 서명은 쌓이는 기록이다. 정체는 **버전의 지문**이다:\n * 같은 화면의 다른 버전은 새 서명이고, 앞 버전의 서명은 그대로 남는다(ADR-0047 ④).\n */\n const already = await repository.findOne({ where: signatureOf(domain.id, release.boardId, release.modelDigest) })\n\n if (already) {\n return\n }\n\n await repository.save({\n domain,\n name: release.name,\n boardId: release.boardId,\n screenName: release.screenName,\n stationName: release.stationName,\n /* 어느 버전에 서명했나. 상신 때 만든 지문을 그대로 적는다 — 여기서 다시 읽으면 그 사이의 버전이 된다. */\n modelDigest: release.modelDigest,\n /* 정한 시각과 정한 사람은 같은 사건이다. 둘 다 여기서 적는다. */\n decidedAt: new Date(),\n decidedBy: user?.name ?? user?.email,\n requestedAt: release.requestedAt ? new Date(release.requestedAt) : undefined,\n reason: release.reason,\n shownAtSigning: release.shownAtSigning ?? [],\n creator: user,\n updater: user\n } as any)\n}\n\n/**\n * 화면 발행 승인 — **통제 문서 한 버전을 내보내도 되는지 정하는 자리.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── 왜 이 문이 있어야 하나 ────────────────────────────────────────────────\n * `StationScreenSpec.controlled` 는 저장·조회·전달까지 되는데 **분기하는 자리가 0 이었다.** 그리고\n * 통제 문서 규칙(결재선 없이는 `controlled` 를 못 켠다)은 **문이 없으면 「영영 못 켜는 칸」**이 된다 —\n * 자리는 있고 길이 없는 결함이다. 그 길이 이 문이다.\n *\n * ── 작성자와 승인자를 가른다 ──────────────────────────────────────────────\n * ISO 9001 7.5.3 의 뜻이 그것이다. 화면을 만드는 권한(`station:mutation`)을 든 자리가 올리고,\n * **문서 관리를 맡은 품질**이 승인한다. 그래서 이 문의 결재선에는 `station:mutation` 을 든 자리가\n * 들어가지 않는다 — 들어가면 한 사람이 만들고 자기가 승인한다.\n *\n * 현장이 공장장을 마지막 서명자로 두고 싶으면 **공장장이 `station:mutation` 을 내려놓는** 쪽이다.\n * 역할 템플릿은 그 테넌트의 것이고, 그 선택은 현장이 한다.\n * [합의했다] operato-application/adr/ADR-0047.md ⑦\n *\n * ── 이 문이 하는 일과 안 하는 일 ──────────────────────────────────────────\n * ```\n * 한다 양식 · 결재선 · 결재 화면 · 승인되면 그 버전의 서명을 남긴다\n * 안 한다 발행 자체를 막는 관문 — 그것은 station 레인이 들고, 이 서명을 읽어 판단한다\n * ```\n * ═══════════════════════════════════════════════════════════════════════════\n */\nexport const ActivityStationScreenRelease = {\n name: STATION_SCREEN_RELEASE_ACTIVITY,\n /* label and guide are drawn from the translations (activity.<door>.label · .guide.<n>); change them together. */\n label: 'screen publication approval',\n description: 'Approve releasing one controlled station screen',\n guide: [\n 'what the operator reads on the screen changes. one torque value, one tolerance becomes the work on the floor.',\n 'read what changes and how, then decide — approving sends that edition to the floor and records who approved it.',\n 'with an empty approval line this screen cannot be put under document control. there is no way to publish under control without approval.'\n ],\n release: '1.0.0',\n provider: 'hatiolab.com',\n category: 'quality',\n activityType: 'user',\n priority: 1,\n startingType: 'post',\n multiple: 'single',\n standardTime: 8 * 60 * 60,\n searchKeys: [\n { name: 'board', description: 'Which screen this release is about', inputKey: 'boardId', tKey: 'field.board' }\n ],\n /* 결재자가 볼 화면. 없으면 빈 화면에 승인 단추만 남는다(§`STATION_SCREEN_RELEASE_VIEW`). */\n viewType: 'custom-element',\n viewSource: STATION_SCREEN_RELEASE_VIEW,\n decisionKeys: [\n { role: 'headline', inputPath: 'headline', name: 'which screen is going out' },\n { role: 'subject', inputPath: 'release.stationName', tKey: 'field.station', name: 'which station reads it' },\n { role: 'reason', inputPath: 'release.reason', tKey: 'field.reason', name: 'what this version changes' },\n { role: 'consequence', inputPath: 'consequences.reads', name: 'what an operator will read off it' },\n { role: 'evidence', inputPath: 'evidence.changes', name: 'the changes themselves, line by line' },\n { role: 'reversibility', inputPath: 'reversibility', name: 'whether this can be taken back' }\n ],\n model: [\n {\n /* 찾기 칸이 읽는 자리 — 맨 위에 있어야 채워진다. 이름이 아니라 식별자로 건다. */\n name: 'boardId',\n tag: 'boardId',\n description: 'Which screen, by the identifier that survives renaming',\n hidden: true,\n mandatory: true,\n inout: 'in',\n type: 'string',\n options: {},\n unit: null,\n quantifier: [1],\n spec: {}\n },\n {\n name: 'release',\n tag: 'release',\n description: 'The screen version as it stood when this was raised',\n hidden: true,\n mandatory: true,\n inout: 'in',\n type: 'object',\n options: {},\n unit: null,\n quantifier: [1],\n spec: {}\n }\n ],\n callback: speaksRefusals(callback)\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 화면 도안 한
|
|
2
|
+
* 화면 도안 한 버전의 **지문** — 서명이 어느 버전에 걸린 것인지 가리는 값.
|
|
3
3
|
*
|
|
4
4
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
5
5
|
* ── 왜 필요한가 ───────────────────────────────────────────────────────────
|
|
6
|
-
* 통제 문서는
|
|
6
|
+
* 통제 문서는 **버전마다** 승인받는다(ISO 9001 7.5.3). 그런데 서명이 화면(`boardId`)에만 걸려 있으면
|
|
7
7
|
* v1 을 승인한 서명 위로 **v2 가 그대로 나간다** — 통제가 이름만 남는 자리다.
|
|
8
8
|
*
|
|
9
9
|
* 번호로는 못 묶는다. `Board.version` 은 발행할 때 정해지고, 상신하는 시점에는 그 번호가 없다.
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* 지문이 한 번 바뀐다. 부품마다 붙으므로 **재귀로** 뺀다
|
|
33
33
|
* stationScenario 뺀다 어느 시나리오가 만들었나 — **출처**다. 'rework' 'packing' 같은 값이고
|
|
34
34
|
* 작업자는 이것을 안 읽는다(실측: spec 붙은 화면 11개에 있다)
|
|
35
|
-
* stationScenarioVersion 뺀다 그 출처의
|
|
35
|
+
* stationScenarioVersion 뺀다 그 출처의 버전. 지금 전부 1이고, **시드를 다시 돌려 2가 되면 그려진 것은
|
|
36
36
|
* 하나도 안 바뀌는데 서명이 전부 낡는다**(실측: 10개)
|
|
37
37
|
* type · view · counts 남긴다 구조 · 묶음 타일 종류 · 양품/불량 — 작업자가 읽거나 만지는 내용이다
|
|
38
38
|
* removable 남긴다 줄을 뺄 수 있나 — 작업자가 만지는 것이다
|
|
@@ -3,11 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.screenModelDigest = screenModelDigest;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
/**
|
|
6
|
-
* 화면 도안 한
|
|
6
|
+
* 화면 도안 한 버전의 **지문** — 서명이 어느 버전에 걸린 것인지 가리는 값.
|
|
7
7
|
*
|
|
8
8
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
9
9
|
* ── 왜 필요한가 ───────────────────────────────────────────────────────────
|
|
10
|
-
* 통제 문서는
|
|
10
|
+
* 통제 문서는 **버전마다** 승인받는다(ISO 9001 7.5.3). 그런데 서명이 화면(`boardId`)에만 걸려 있으면
|
|
11
11
|
* v1 을 승인한 서명 위로 **v2 가 그대로 나간다** — 통제가 이름만 남는 자리다.
|
|
12
12
|
*
|
|
13
13
|
* 번호로는 못 묶는다. `Board.version` 은 발행할 때 정해지고, 상신하는 시점에는 그 번호가 없다.
|
|
@@ -36,7 +36,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
36
36
|
* 지문이 한 번 바뀐다. 부품마다 붙으므로 **재귀로** 뺀다
|
|
37
37
|
* stationScenario 뺀다 어느 시나리오가 만들었나 — **출처**다. 'rework' 'packing' 같은 값이고
|
|
38
38
|
* 작업자는 이것을 안 읽는다(실측: spec 붙은 화면 11개에 있다)
|
|
39
|
-
* stationScenarioVersion 뺀다 그 출처의
|
|
39
|
+
* stationScenarioVersion 뺀다 그 출처의 버전. 지금 전부 1이고, **시드를 다시 돌려 2가 되면 그려진 것은
|
|
40
40
|
* 하나도 안 바뀌는데 서명이 전부 낡는다**(실측: 10개)
|
|
41
41
|
* type · view · counts 남긴다 구조 · 묶음 타일 종류 · 양품/불량 — 작업자가 읽거나 만지는 내용이다
|
|
42
42
|
* removable 남긴다 줄을 뺄 수 있나 — 작업자가 만지는 것이다
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-model-digest.js","sourceRoot":"","sources":["../../../server/service/station/screen-model-digest.ts"],"names":[],"mappings":";;AA6DA,8CAEC;AA/DD,6CAAwC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,SAAgB,iBAAiB,CAAC,KAAc;IAC9C,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACzE,CAAC;AAED,gDAAgD;AAChD,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,CAAC,CAAC,CAAA;AAE7G,uCAAuC;AACvC,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;IACtC,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IAC3C,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;QAC9D,8DAA8D;SAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC;QACvC,+DAA+D;SAC9D,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,2BAA2B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACxD,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEnD,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;AAC9F,CAAC","sourcesContent":["import { createHash } from 'node:crypto'\n\n/**\n * 화면 도안 한
|
|
1
|
+
{"version":3,"file":"screen-model-digest.js","sourceRoot":"","sources":["../../../server/service/station/screen-model-digest.ts"],"names":[],"mappings":";;AA6DA,8CAEC;AA/DD,6CAAwC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,SAAgB,iBAAiB,CAAC,KAAc;IAC9C,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACzE,CAAC;AAED,gDAAgD;AAChD,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,CAAC,CAAC,CAAA;AAE7G,uCAAuC;AACvC,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;IACtC,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IAC3C,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;QAC9D,8DAA8D;SAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC;QACvC,+DAA+D;SAC9D,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,2BAA2B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACxD,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEnD,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;AAC9F,CAAC","sourcesContent":["import { createHash } from 'node:crypto'\n\n/**\n * 화면 도안 한 버전의 **지문** — 서명이 어느 버전에 걸린 것인지 가리는 값.\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── 왜 필요한가 ───────────────────────────────────────────────────────────\n * 통제 문서는 **버전마다** 승인받는다(ISO 9001 7.5.3). 그런데 서명이 화면(`boardId`)에만 걸려 있으면\n * v1 을 승인한 서명 위로 **v2 가 그대로 나간다** — 통제가 이름만 남는 자리다.\n *\n * 번호로는 못 묶는다. `Board.version` 은 발행할 때 정해지고, 상신하는 시점에는 그 번호가 없다.\n * 그래서 **그때 도안 자체**를 굳힌다 — `shownAtSigning` 이 결재자에게 보인 문장을 굳히는 것과 같다.\n *\n * ── 정규화가 이 값의 전부다 ───────────────────────────────────────────────\n * 키 순서만 달라져도 지문이 바뀌면 **저장할 때마다 재결재**가 되고, 관문은 곧 소음이 되어 사람이\n * 끄게 된다. 그래서 키를 정렬해 직렬화한 뒤 해시한다.\n *\n * **배열 순서는 안 건드린다.** 도안에서 순서는 뜻이다 — 겹치는 순서가 바뀌면 작업자가 보는 것이\n * 달라진다. 정렬하면 그 변화가 지문에서 사라진다.\n *\n * ── 한 자리에서만 만든다 ─────────────────────────────────────────────────\n * 상신하는 쪽과 발행 관문이 **같은 함수**를 부른다. 두 벌로 두면 한쪽이 정규화를 조금 다르게 하는 날\n * 모든 서명이 낡은 것으로 읽히고, 아무도 이유를 모른다.\n *\n * ── 빼는 칸: **작업자가 화면에서 읽는 것이 아닌 칸** ──────────────────────\n * 도안 안에 저장할 때마다(또는 한 번) 바뀌는 칸이 있으면 이 값이 달라지고, **내용이 안 바뀐 화면의\n * 서명이 낡는다.** 기준은 하나다 — 그 칸이 작업자가 화면에서 읽는 것을 바꾸는가.\n *\n * ```\n * threed 뺀다 2D/3D 토글의 보기 상태. 3D 로 보다가 저장하면 읽는 것은 그대로인데 지문이 바뀐다\n * refid 뺀다 things-scene 이 없을 때 한 번 매겨 저장하는 식별 색인\n * (RootContainer.addRefidIndex → set('refid'); 있으면 그대로 둔다)\n * 세션마다 바뀌진 않지만 **저작면 도안이 편집기를 처음 지날 때** 붙어\n * 지문이 한 번 바뀐다. 부품마다 붙으므로 **재귀로** 뺀다\n * stationScenario 뺀다 어느 시나리오가 만들었나 — **출처**다. 'rework' 'packing' 같은 값이고\n * 작업자는 이것을 안 읽는다(실측: spec 붙은 화면 11개에 있다)\n * stationScenarioVersion 뺀다 그 출처의 버전. 지금 전부 1이고, **시드를 다시 돌려 2가 되면 그려진 것은\n * 하나도 안 바뀌는데 서명이 전부 낡는다**(실측: 10개)\n * type · view · counts 남긴다 구조 · 묶음 타일 종류 · 양품/불량 — 작업자가 읽거나 만지는 내용이다\n * removable 남긴다 줄을 뺄 수 있나 — 작업자가 만지는 것이다\n * ```\n *\n * **값어치로 예외를 두지 않는다.** 「이건 중요하니까 세자」가 한 번 들어오면 기준이 사라진다.\n *\n * ⚠ 이름으로 빼므로 **부품 자료 안에 같은 이름의 칸이 있으면 그것도 빠진다.** 지금 도안에는 없고,\n * 그런 칸이 생기면 그때는 경로로 빼는 수밖에 없다.\n *\n * [합의했다] ADR-0047 ④ — Shop Floor 가 저장된 도안 둘(저작면 것 · 편집기를 지난 것)을 견줘 찾았다.\n *\n * ── 글자로 와도 받는다. **그것이 실제 경로다** ────────────────────────────\n * `Board.model` 은 **문자열 칸**이다(text). 그래서 상신하는 쪽이 읽어 넘기는 값도 글자이고, 그것을\n * 그대로 해시하면 **정규화가 한 번도 안 돈다** — 같은 도안이라도 저장된 글자의 키 순서나 들여쓰기가\n * 달라지면 지문이 달라지고, 그 순간 모든 서명이 낡는다. 막으려고 만든 결함을 막는 코드가 그대로\n * 만들어 내는 자리였다(2026-09-16에 실제로 그랬다).\n *\n * 그래서 글자면 **먼저 읽어서** 정규화한다. 못 읽는 글자는 지어내지 않고 글자 그대로 해시한다 —\n * 여기서 던지면 발행 관문이 그 화면에서 멈춘다.\n *\n * [합의했다] operato-application/adr/ADR-0047.md ④\n * ═══════════════════════════════════════════════════════════════════════════\n */\nexport function screenModelDigest(model: unknown): string {\n return createHash('sha256').update(stable(parsed(model))).digest('hex')\n}\n\n/** 글자로 온 도안은 읽어서 본다. 못 읽으면 글자 그대로 — 던지지 않는다. */\nfunction parsed(model: unknown): unknown {\n if (typeof model !== 'string') {\n return model\n }\n\n try {\n return JSON.parse(model)\n } catch {\n return model\n }\n}\n\n/** 보기 상태 · 색인 · 출처 — 읽는 것을 안 바꾸면서 지문을 흔드는 칸들. */\nconst NOT_WHAT_THE_OPERATOR_READS = new Set(['threed', 'refid', 'stationScenario', 'stationScenarioVersion'])\n\n/** 키를 정렬해 직렬화한다. 배열은 그대로 — 순서가 뜻이다. */\nfunction stable(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n return JSON.stringify(value ?? null)\n }\n\n if (Array.isArray(value)) {\n return `[${value.map(stable).join(',')}]`\n }\n\n const entries = Object.entries(value as Record<string, unknown>)\n /* `undefined` 는 JSON 에서 사라진다 — 여기서도 없는 것으로 읽어 둘이 안 갈리게 한다. */\n .filter(([, one]) => one !== undefined)\n /* 작업자가 읽는 것이 아닌 칸은 빼고 센다(위 표). 부품 안에도 붙으므로 여기서 걸러야 재귀로 빠진다. */\n .filter(([key]) => !NOT_WHAT_THE_OPERATOR_READS.has(key))\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\n return `{${entries.map(([key, one]) => `${JSON.stringify(key)}:${stable(one)}`).join(',')}}`\n}\n"]}
|
|
@@ -38,7 +38,7 @@ export interface StationAssignmentSelector {
|
|
|
38
38
|
/** One assignment: a board, and when to show it. */
|
|
39
39
|
export interface StationAssignmentRule extends StationAssignmentSelector {
|
|
40
40
|
name: string;
|
|
41
|
-
/** 그릴 화면의 이름.
|
|
41
|
+
/** 그릴 화면의 이름. 버전은 배정이 못 박았을 때만 있다. */
|
|
42
42
|
boardId: string;
|
|
43
43
|
/** Breaks a tie between equally specific screens. Higher wins. */
|
|
44
44
|
priority?: number;
|
|
@@ -1 +1 @@
|
|
|
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 /** 그릴 화면의 이름.
|
|
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"]}
|
|
@@ -23,9 +23,9 @@ export declare class StationAssignmentRowType {
|
|
|
23
23
|
description?: string;
|
|
24
24
|
boardId: string;
|
|
25
25
|
/**
|
|
26
|
-
* 그 화면이 지금 현장에 나가 있는
|
|
26
|
+
* 그 화면이 지금 현장에 나가 있는 버전.
|
|
27
27
|
*
|
|
28
|
-
* 없으면 **나간
|
|
28
|
+
* 없으면 **나간 버전이 없다**는 뜻이고, 그러면 이 배정은 아무것도 못 그린다 — 아래 `usable` 이 그것을
|
|
29
29
|
* 말한다. 초안이 있어도 대신 내밀지 않는다: 작업대가 그리다 만 화면 앞에서 실적을 남기게 된다.
|
|
30
30
|
*/
|
|
31
31
|
boardName?: string;
|
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"station-screen-assignment.js","sourceRoot":"","sources":["../../../server/service/station/station-screen-assignment.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * Which screen a station gets — **the assignment, not the drawing.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── What could not be said ─────────────────────────────────────────────────\n * A board reached a station only through its address: someone typed `?board=<id>` into a tablet and\n * that tablet showed that board until someone typed a different one. So \"the press stations get the\n * new layout\" was a walk around the factory, and \"welders see the qualification screen\" could not be\n * expressed at all.\n *\n * ── Why it lives here and not in board-service ─────────────────────────────\n * The axes are this product's words — operation, route, material class, personnel class. Teaching\n * the board service to know them would put manufacturing vocabulary in a package that draws\n * rectangles, and every other consumer of boards would inherit it.\n *\n * ── The axes are the ones the context already carries ──────────────────────\n * Deliberately the same five. One vocabulary answers both \"what does this screen show\" and \"which\n * screen shows\" — if they drifted apart, a factory could select on something a screen cannot display\n * and never understand why the board is blank.\n *\n * ── Absent means any ───────────────────────────────────────────────────────\n * Every axis is nullable and an empty one is not a constraint. That is what makes one row able to\n * say \"every press\" and another \"this machine, this operation\" without two kinds of assignment.\n * How they compete is `station-screen-choice.ts`, kept pure so the rule can be argued about without\n * a database.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n@Entity()\n@Index('ix_mes_station_screen_assignment_0', (assignment: StationScreenAssignment) => [assignment.domain, assignment.name], { unique: true })\n@Index('ix_mes_station_screen_assignment_1', (assignment: StationScreenAssignment) => [assignment.domain, assignment.equipmentName])\n@ObjectType({ description: 'Which screen a station shows, and when' })\nexport class StationScreenAssignment {\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((assignment: StationScreenAssignment) => assignment.domain)\n domainId?: string\n\n /** What this assignment is called. It is what the answer names when it says which screen won. */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /**\n * The drawing to put on this station — a board, by id.\n *\n * ── This was briefly a name pointing at our own `StationScreen` table ────────────────────\n * That table was `Board` with different words: same columns, same release/revert/history, same\n * verbs. What is actually ours is not the drawing but what surrounds it — which station sees\n * which one (this table) and what the drawing needs in order to work (`StationScreenSpec`).\n *\n * An id and not a name, because that is what board-service is keyed by, and because a name is\n * reusable there: `ix_board_1` is scoped to `deleted_at IS NULL`, so deleting a board frees its\n * name for a new one. An assignment holding a name would silently follow that new board.\n */\n @Column()\n @Field({ description: \"the board to draw — the board's id in board-service\" })\n boardId: string\n\n /*\n * ── 판은 배정이 고르지 않는다 ────────────────────────────────────────────\n * `screenVersion` 으로 특정 판에 못 박을 수 있게 두었다가 걷었다.\n *\n * 현장 관리자가 축 일곱 개를 채우고 거기에 「어느 판」까지 고르는 일은 실제로 안 일어나고, 한 번\n * 박으면 **아무도 안 푼다** — 그 라인만 몇 달째 옛 화면을 보는데 관리 화면은 초록이다. 실제로 그\n * 결함이 이미 있었다: 없는 판에 박아도 `usable` 로 섰다.\n *\n * 새 판을 한 라인에서 먼저 돌려 보는 것도 이걸로 할 일이 아니다. 깨지면 되돌리기는 화면 쪽에서\n * `RELEASED` 를 뒤집는 것이고, 라인마다 다른 것을 그려야 한다면 그것은 다른 화면이다(이름을 따로\n * 준다). 같은 이름이 자리마다 다른 것을 그리면 나중에 「그때 무엇을 봤나」에 아무도 답 못 한다.\n *\n * **어느 화면인가는 배정이, 어느 판인가는 화면이 정한다.**\n */\n\n /* ── The axes. Empty is not a constraint. ─────────────────────────────── */\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'this one machine' })\n equipmentName?: string\n\n /**\n * Any machine of this class — `Equipment.equipmentClassIds` or its `kind`.\n *\n * This is the axis that makes assignment worth having. \"Every press gets the new layout\" is one\n * row; naming the machines is a row per machine and a walk around the factory when one is added.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'any machine belonging to this EquipmentClassID' })\n equipmentClassId?: string\n\n /** Any station on this line — the ISA-95 `ProductionLine` the station's location sits under. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n workCenterName?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'stations performing this operation' })\n operationKey?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'orders running this route' })\n routeKey?: string\n\n /**\n * Orders making something of this class — standard `MaterialClassID`.\n *\n * By class and not by item on purpose: a plant has thousands of items and nobody assigns a screen\n * to each. The class is the unit a person actually thinks in.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'orders for material of this MaterialClassID' })\n materialClassId?: string\n\n /**\n * Whoever is scanned in belongs to this class — standard `PersonnelClassID`.\n *\n * This is how a trainee gets a different screen from a qualified operator. It only matches once\n * somebody has scanned in — before that the station does not know who is there, and guessing would\n * put the wrong screen in front of the wrong person.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'whoever is scanned in belongs to this PersonnelClassID' })\n personnelClassId?: string\n\n /**\n * Breaks a tie between screens neither of which is more specific. Higher wins.\n *\n * Needed because specificity is a partial order: `{machine}` and `{line, operation}` cannot be\n * ranked without inventing a weight, so a person writes down which one they meant.\n */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'higher wins when neither screen is more specific' })\n priority?: number\n\n /** From when this assignment applies — standard `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /** Until when. Lets a changeover be prepared in advance instead of switched by hand on the day. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((assignment: StationScreenAssignment) => assignment.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((assignment: StationScreenAssignment) => assignment.updater)\n updaterId?: string\n}\n"]}
|
|
1
|
+
{"version":3,"file":"station-screen-assignment.js","sourceRoot":"","sources":["../../../server/service/station/station-screen-assignment.ts"],"names":[],"mappings":"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","sourcesContent":["import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\n\n/**\n * Which screen a station gets — **the assignment, not the drawing.**\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── What could not be said ─────────────────────────────────────────────────\n * A board reached a station only through its address: someone typed `?board=<id>` into a tablet and\n * that tablet showed that board until someone typed a different one. So \"the press stations get the\n * new layout\" was a walk around the factory, and \"welders see the qualification screen\" could not be\n * expressed at all.\n *\n * ── Why it lives here and not in board-service ─────────────────────────────\n * The axes are this product's words — operation, route, material class, personnel class. Teaching\n * the board service to know them would put manufacturing vocabulary in a package that draws\n * rectangles, and every other consumer of boards would inherit it.\n *\n * ── The axes are the ones the context already carries ──────────────────────\n * Deliberately the same five. One vocabulary answers both \"what does this screen show\" and \"which\n * screen shows\" — if they drifted apart, a factory could select on something a screen cannot display\n * and never understand why the board is blank.\n *\n * ── Absent means any ───────────────────────────────────────────────────────\n * Every axis is nullable and an empty one is not a constraint. That is what makes one row able to\n * say \"every press\" and another \"this machine, this operation\" without two kinds of assignment.\n * How they compete is `station-screen-choice.ts`, kept pure so the rule can be argued about without\n * a database.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n@Entity()\n@Index('ix_mes_station_screen_assignment_0', (assignment: StationScreenAssignment) => [assignment.domain, assignment.name], { unique: true })\n@Index('ix_mes_station_screen_assignment_1', (assignment: StationScreenAssignment) => [assignment.domain, assignment.equipmentName])\n@ObjectType({ description: 'Which screen a station shows, and when' })\nexport class StationScreenAssignment {\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((assignment: StationScreenAssignment) => assignment.domain)\n domainId?: string\n\n /** What this assignment is called. It is what the answer names when it says which screen won. */\n @Column()\n @Field()\n name: string\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n description?: string\n\n /**\n * The drawing to put on this station — a board, by id.\n *\n * ── This was briefly a name pointing at our own `StationScreen` table ────────────────────\n * That table was `Board` with different words: same columns, same release/revert/history, same\n * verbs. What is actually ours is not the drawing but what surrounds it — which station sees\n * which one (this table) and what the drawing needs in order to work (`StationScreenSpec`).\n *\n * An id and not a name, because that is what board-service is keyed by, and because a name is\n * reusable there: `ix_board_1` is scoped to `deleted_at IS NULL`, so deleting a board frees its\n * name for a new one. An assignment holding a name would silently follow that new board.\n */\n @Column()\n @Field({ description: \"the board to draw — the board's id in board-service\" })\n boardId: string\n\n /*\n * ── 버전은 배정이 고르지 않는다 ────────────────────────────────────────────\n * `screenVersion` 으로 특정 버전에 못 박을 수 있게 두었다가 걷었다.\n *\n * 현장 관리자가 축 일곱 개를 채우고 거기에 「어느 버전」까지 고르는 일은 실제로 안 일어나고, 한 번\n * 박으면 **아무도 안 푼다** — 그 라인만 몇 달째 옛 화면을 보는데 관리 화면은 초록이다. 실제로 그\n * 결함이 이미 있었다: 없는 버전에 박아도 `usable` 로 섰다.\n *\n * 새 버전을 한 라인에서 먼저 돌려 보는 것도 이걸로 할 일이 아니다. 깨지면 되돌리기는 화면 쪽에서\n * `RELEASED` 를 뒤집는 것이고, 라인마다 다른 것을 그려야 한다면 그것은 다른 화면이다(이름을 따로\n * 준다). 같은 이름이 자리마다 다른 것을 그리면 나중에 「그때 무엇을 봤나」에 아무도 답 못 한다.\n *\n * **어느 화면인가는 배정이, 어느 버전인가는 화면이 정한다.**\n */\n\n /* ── The axes. Empty is not a constraint. ─────────────────────────────── */\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'this one machine' })\n equipmentName?: string\n\n /**\n * Any machine of this class — `Equipment.equipmentClassIds` or its `kind`.\n *\n * This is the axis that makes assignment worth having. \"Every press gets the new layout\" is one\n * row; naming the machines is a row per machine and a walk around the factory when one is added.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'any machine belonging to this EquipmentClassID' })\n equipmentClassId?: string\n\n /** Any station on this line — the ISA-95 `ProductionLine` the station's location sits under. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n workCenterName?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'stations performing this operation' })\n operationKey?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'orders running this route' })\n routeKey?: string\n\n /**\n * Orders making something of this class — standard `MaterialClassID`.\n *\n * By class and not by item on purpose: a plant has thousands of items and nobody assigns a screen\n * to each. The class is the unit a person actually thinks in.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'orders for material of this MaterialClassID' })\n materialClassId?: string\n\n /**\n * Whoever is scanned in belongs to this class — standard `PersonnelClassID`.\n *\n * This is how a trainee gets a different screen from a qualified operator. It only matches once\n * somebody has scanned in — before that the station does not know who is there, and guessing would\n * put the wrong screen in front of the wrong person.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'whoever is scanned in belongs to this PersonnelClassID' })\n personnelClassId?: string\n\n /**\n * Breaks a tie between screens neither of which is more specific. Higher wins.\n *\n * Needed because specificity is a partial order: `{machine}` and `{line, operation}` cannot be\n * ranked without inventing a weight, so a person writes down which one they meant.\n */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'higher wins when neither screen is more specific' })\n priority?: number\n\n /** From when this assignment applies — standard `EffectiveStartDate`. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveStart?: Date\n\n /** Until when. Lets a changeover be prepared in advance instead of switched by hand on the day. */\n @Column({ nullable: true })\n @Field({ nullable: true })\n effectiveEnd?: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n creator?: User\n\n @RelationId((assignment: StationScreenAssignment) => assignment.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true })\n updater?: User\n\n @RelationId((assignment: StationScreenAssignment) => assignment.updater)\n updaterId?: string\n}\n"]}
|
|
@@ -17,7 +17,7 @@ export declare function controlLineStanding(context: ResolverContext): Promise<{
|
|
|
17
17
|
*
|
|
18
18
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
19
19
|
* ── 올리기만 한다. spec 은 그대로다 ────────────────────────────────────────
|
|
20
|
-
* 승인 전까지 이 화면은 여전히 통제 문서이고, 서명 없는
|
|
20
|
+
* 승인 전까지 이 화면은 여전히 통제 문서이고, 서명 없는 버전은 그동안에도 발행 관문에서 거절된다
|
|
21
21
|
* (아키텍트 조건 ①). 여기서 spec 을 쓰면 「올렸다」가 곧 「껐다」가 된다 — 이 파일에는 spec 을
|
|
22
22
|
* 쓰는 줄이 없어야 한다(`station-screen-control-withdrawal.test.ts` 가 센다).
|
|
23
23
|
*
|
|
@@ -37,7 +37,7 @@ async function controlLineStanding(context) {
|
|
|
37
37
|
*
|
|
38
38
|
* ═══════════════════════════════════════════════════════════════════════════
|
|
39
39
|
* ── 올리기만 한다. spec 은 그대로다 ────────────────────────────────────────
|
|
40
|
-
* 승인 전까지 이 화면은 여전히 통제 문서이고, 서명 없는
|
|
40
|
+
* 승인 전까지 이 화면은 여전히 통제 문서이고, 서명 없는 버전은 그동안에도 발행 관문에서 거절된다
|
|
41
41
|
* (아키텍트 조건 ①). 여기서 spec 을 쓰면 「올렸다」가 곧 「껐다」가 된다 — 이 파일에는 spec 을
|
|
42
42
|
* 쓰는 줄이 없어야 한다(`station-screen-control-withdrawal.test.ts` 가 센다).
|
|
43
43
|
*
|
|
@@ -89,7 +89,7 @@ let StationScreenControlWithdrawalResolver = class StationScreenControlWithdrawa
|
|
|
89
89
|
? `${screenName} 의 화면 설정을 지우고 통제에서 뺍니다`
|
|
90
90
|
: `${screenName} 를 통제 문서에서 뺍니다`,
|
|
91
91
|
consequences: {
|
|
92
|
-
after: '승인하면 이 화면은 그 뒤로 결재 없이 발행됩니다. 작업자가 보는
|
|
92
|
+
after: '승인하면 이 화면은 그 뒤로 결재 없이 발행됩니다. 작업자가 보는 버전이 서명 없이 바뀔 수 있습니다.'
|
|
93
93
|
},
|
|
94
94
|
/* 방식마다 무게가 다르다 — 끄는 것은 다시 켤 수 있고, 지우는 것은 못 되돌린다. 사유(caveat)와 함께 싣는다. */
|
|
95
95
|
reversibility: (0, station_screen_control_withdrawal_1.withdrawalReversibility)(action),
|