@things-factory/ops-master 10.1.36 → 10.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +12 -0
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/master-change-policy-axes.d.ts +43 -0
- package/dist-client/master-change-policy-axes.js +35 -0
- package/dist-client/master-change-policy-axes.js.map +1 -0
- package/dist-client/master-change-policy-panel.d.ts +59 -0
- package/dist-client/master-change-policy-panel.js +452 -0
- package/dist-client/master-change-policy-panel.js.map +1 -0
- package/dist-client/pages/master/applied-when-dialog.d.ts +2 -2
- package/dist-client/pages/master/applied-when-dialog.js +2 -2
- package/dist-client/pages/master/applied-when-dialog.js.map +1 -1
- package/dist-client/pages/master/assignment-fields.d.ts +8 -9
- package/dist-client/pages/master/assignment-fields.js +25 -18
- package/dist-client/pages/master/assignment-fields.js.map +1 -1
- package/dist-client/pages/master/master-page-base.d.ts +0 -2
- package/dist-client/pages/master/master-page-base.js +1 -3
- package/dist-client/pages/master/master-page-base.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.js +10 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/service/approval-route.d.ts +37 -0
- package/dist-server/service/approval-route.js +34 -0
- package/dist-server/service/approval-route.js.map +1 -0
- package/dist-server/service/assignment/assignment-history.d.ts +21 -16
- package/dist-server/service/assignment/assignment-history.js +91 -52
- package/dist-server/service/assignment/assignment-history.js.map +1 -1
- package/dist-server/service/assignment/assignment-write.d.ts +21 -4
- package/dist-server/service/assignment/assignment-write.js +41 -9
- package/dist-server/service/assignment/assignment-write.js.map +1 -1
- package/dist-server/service/assignment/{assignment-claim.d.ts → master-value-claim.d.ts} +31 -7
- package/dist-server/service/assignment/{assignment-claim.js → master-value-claim.js} +45 -31
- package/dist-server/service/assignment/master-value-claim.js.map +1 -0
- package/dist-server/service/index.d.ts +5 -2
- package/dist-server/service/index.js +9 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/master-axis.d.ts +14 -3
- package/dist-server/service/master-axis.js +16 -15
- package/dist-server/service/master-axis.js.map +1 -1
- package/dist-server/service/master-change-policy.d.ts +74 -2
- package/dist-server/service/master-change-policy.js +187 -21
- package/dist-server/service/master-change-policy.js.map +1 -1
- package/dist-server/service/material/material-revision-resolver.d.ts +7 -2
- package/dist-server/service/material/material-revision-resolver.js +18 -0
- package/dist-server/service/material/material-revision-resolver.js.map +1 -1
- package/dist-server/service/material/material-revision-service.d.ts +32 -2
- package/dist-server/service/material/material-revision-service.js +129 -0
- package/dist-server/service/material/material-revision-service.js.map +1 -1
- package/dist-server/service/material/material-revision.d.ts +7 -5
- package/dist-server/service/material/material-revision.js +2 -2
- package/dist-server/service/material/material-revision.js.map +1 -1
- package/dist-server/service/recipe/index.d.ts +1 -1
- package/dist-server/service/recipe/recipe-revision-resolver.d.ts +7 -2
- package/dist-server/service/recipe/recipe-revision-resolver.js +18 -0
- package/dist-server/service/recipe/recipe-revision-resolver.js.map +1 -1
- package/dist-server/service/recipe/recipe-revision-service.d.ts +33 -2
- package/dist-server/service/recipe/recipe-revision-service.js +139 -0
- package/dist-server/service/recipe/recipe-revision-service.js.map +1 -1
- package/dist-server/service/recipe/recipe-revision.d.ts +8 -6
- package/dist-server/service/recipe/recipe-revision.js +2 -2
- package/dist-server/service/recipe/recipe-revision.js.map +1 -1
- package/dist-server/service/revision-baseline.d.ts +63 -0
- package/dist-server/service/revision-baseline.js +72 -0
- package/dist-server/service/revision-baseline.js.map +1 -0
- package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +7 -2
- package/dist-server/service/test-spec/test-spec-revision-resolver.js +18 -0
- package/dist-server/service/test-spec/test-spec-revision-resolver.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-revision-service.d.ts +37 -2
- package/dist-server/service/test-spec/test-spec-revision-service.js +122 -0
- package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -1
- package/dist-server/service/test-spec/test-spec-revision.d.ts +7 -5
- package/dist-server/service/test-spec/test-spec-revision.js +2 -2
- package/dist-server/service/test-spec/test-spec-revision.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/translations/en.json +63 -2
- package/translations/ja.json +62 -1
- package/translations/ko.json +63 -2
- package/translations/ms.json +62 -1
- package/translations/zh.json +64 -1
- package/dist-server/service/assignment/assignment-claim.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-axis.js","sourceRoot":"","sources":["../../server/service/master-axis.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"master-axis.js","sourceRoot":"","sources":["../../server/service/master-axis.ts"],"names":[],"mappings":"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","sourcesContent":["import { ObjectLiteral } from 'typeorm'\n\nimport { Equipment } from './equipment/equipment'\nimport { EquipmentClass } from './equipment/equipment-class'\nimport { StopReason } from './equipment/stop-reason'\nimport { NonconformanceCause } from './quality/nonconformance-cause'\nimport { MaterialClass } from './material/material-class'\nimport { MaterialDefinition } from './material/material-definition'\nimport { OperationDefinition } from './operation/operation-definition'\nimport { OperationalLocation } from './location/operational-location'\nimport { Person } from './personnel/person'\nimport { PersonnelClass } from './personnel/personnel-class'\nimport { PhysicalAsset } from './asset/physical-asset'\nimport { PhysicalAssetClass } from './asset/physical-asset-class'\nimport { RecipeDefinition } from './recipe/recipe-definition'\nimport { RouteDefinition } from './recipe/route-definition'\nimport { TestSpecification } from './test-spec/test-specification'\n\n/**\n * 마스터 축의 **한 줄짜리 선언** — 축마다 다른 것만 적는다.\n *\n * ── 왜 표로 두나 ───────────────────────────────────────────────────────────\n * 축이 열다섯인데 조회와 저장은 모양이 같다. 축마다 리졸버를 통째로 베끼면 같은 코드가 열다섯 벌이 되고,\n * 규칙 하나를 고칠 때 열다섯 곳을 고쳐야 한다. 그러다 한 곳을 빠뜨리면 그 축만 다르게 동작하는데\n * 오류는 나지 않는다.\n *\n * 그래서 **다른 것만** 적는다 — 어느 표인가, 무엇으로 찾는가, 없앨 때 지우는가 끝을 채우는가.\n *\n * ── 왜 유효 기간 여부가 여기 있나 ──────────────────────────────────────────\n * 지난 기록이 가리키는 축은 지우면 안 된다. 그것이 축마다 다르므로 축의 성질로 적는다. 정지 사유는\n * 유효 기간이 있고, 등급도 있다. 지금 열다섯 모두 유효 기간을 갖는다 — 그래도 칸을 둔다. 없는 축이\n * 생기면 그때 이 표만 고치면 된다.\n */\n\nexport interface MasterAxis {\n /** GraphQL 이름의 바탕. `person` → `persons` · `updateMultiplePerson`. */\n key: string\n entity: { new (): ObjectLiteral }\n /** 검색어가 걸리는 칸들. */\n searchables: string[]\n /** 없앨 때 행을 지우지 않고 `effectiveEnd` 를 채우나. */\n hasEffectivePeriod: boolean\n /**\n * 이 축의 바뀜을 **개정본으로 결재에 보낼 수 있나.**\n *\n * 승인으로 켜면 `assertDirectMasterChange` 가 이 축의 직접 쓰기를 전부 거절한다. 그 뒤로 고치는 길은\n * 개정본을 결재로 보내는 것뿐이라, 그 길이 없는 축을 켜면 **그 축이 잠긴다**(2026-09-24). 그래서\n * 정책은 이 칸이 참인 축만 승인으로 켜게 한다(`declareMasterChangePolicy`).\n *\n * 앱이 정하지 않는다 — 개정본 문은 이 꾸러미가 지었고, 어느 앱에서도 없는 문으로는 보낼 수 없다.\n * 축마다 적게 한 것은, 새 축이 이 물음에 답하지 않고 들어오지 못하게 하려는 것이다.\n */\n revisable: boolean\n}\n\nexport const MASTER_AXES: readonly MasterAxis[] = [\n { key: 'personnelClass', entity: PersonnelClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'person', entity: Person, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'equipmentClass', entity: EquipmentClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'equipment', entity: Equipment, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'stopReason', entity: StopReason, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'nonconformanceCause', entity: NonconformanceCause, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'physicalAssetClass', entity: PhysicalAssetClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'physicalAsset', entity: PhysicalAsset, searchables: ['name', 'description', 'identity'], hasEffectivePeriod: true, revisable: false },\n { key: 'materialClass', entity: MaterialClass, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'materialDefinition', entity: MaterialDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: true },\n { key: 'operationalLocation', entity: OperationalLocation, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'operationDefinition', entity: OperationDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n { key: 'testSpecification', entity: TestSpecification, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: true },\n /*\n * 라우팅과 레시피 (2026-08-31) — 커널에 `RouteDef`·`RecipeDef` 가 있고 `JobOrder.routeKey` 가 그것을\n * 가리키는데 **그 값이 가리킬 표가 없었다.** 사람이 문자열을 손으로 적는 상태였고, 그래서 MES 가 받은\n * 작업지시에 라우팅을 자동으로 붙일 수 없었다.\n */\n { key: 'routeDefinition', entity: RouteDefinition, searchables: ['name', 'description'], hasEffectivePeriod: true, revisable: false },\n {\n key: 'recipeDefinition',\n entity: RecipeDefinition,\n /* 만드는 품목으로도 찾는다 — 「이 품목을 만드는 레시피」가 MES 가 지시마다 묻는 물음이다. */\n searchables: ['name', 'description', 'materialDefinitionName'],\n hasEffectivePeriod: true,\n revisable: true\n }\n]\n\nexport function masterAxisOf(key: string): MasterAxis {\n const axis = MASTER_AXES.find(candidate => candidate.key === key)\n\n if (!axis) {\n /* 없는 축을 조용히 지나치지 않는다 — 지나치면 그 축의 화면이 빈 목록을 보이고 아무도 모른다. */\n throw new Error(`masterAxisOf: unknown master axis \"${key}\"`)\n }\n\n return axis\n}\n"]}
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { EntityManager } from 'typeorm';
|
|
2
2
|
import { Domain } from '@things-factory/shell';
|
|
3
3
|
import { User } from '@things-factory/auth-base';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* 정책을 정할 수 있는 축들 — 마스터 축 열다섯에 `workShift` 를 더한다.
|
|
6
|
+
*
|
|
7
|
+
* `workShift` 는 별도 모듈이라 `MASTER_AXES` 에 없는데 정책은 받는다(§`policyAxis`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const POLICY_AXES: string[];
|
|
10
|
+
/**
|
|
11
|
+
* 이 축의 바뀜을 **개정본으로 결재에 보낼 수 있나** — 승인으로 켤 수 있는 축인가.
|
|
12
|
+
*
|
|
13
|
+
* 마스터 축은 자기 선언(`MasterAxis.revisable`)이 답한다. `workShift` 는 그 표 밖에서 자기 꾸러미가
|
|
14
|
+
* 개정 체계를 지었으므로 참이다(`work-shift-revision-service`). 앱이 답하지 않는다 — 문이 없으면
|
|
15
|
+
* 어느 앱에서도 보낼 곳이 없다.
|
|
16
|
+
*/
|
|
17
|
+
export declare function axisHasRevision(axis: string): boolean;
|
|
18
|
+
/** 도메인 × 축의 기준정보 변경 정책 — 정하는 것도 바꾸는 것도 사업장 소유자의 몫이다. */
|
|
5
19
|
export declare class MasterChangePolicy {
|
|
6
20
|
id: string;
|
|
7
21
|
domain: Domain;
|
|
@@ -9,15 +23,73 @@ export declare class MasterChangePolicy {
|
|
|
9
23
|
axis: string;
|
|
10
24
|
approvalRequired: boolean;
|
|
11
25
|
reason: string;
|
|
26
|
+
/**
|
|
27
|
+
* 이 줄이 물러난 시각 — 비어 있는 줄이 **지금 정책**이다.
|
|
28
|
+
*
|
|
29
|
+
* 차례를 `createdAt` 으로 세우지 않는다. sqlite 의 `datetime('now')` 는 초까지라, 한 사람이 같은
|
|
30
|
+
* 초에 두 번 바꾸면 어느 줄이 나중인지 저장소가 못 가린다. 물러난 줄에 표를 하면 그 물음이 사라진다.
|
|
31
|
+
*/
|
|
32
|
+
supersededAt: Date;
|
|
33
|
+
/**
|
|
34
|
+
* 누가 정했나 — **비어 있으면 아무도 안 정한 것**이고, 그 줄은 기본값으로 선 줄이다.
|
|
35
|
+
*
|
|
36
|
+
* 사람이 안 정한 줄에 사람을 적으면 그 사람이 정한 것이 된다. 사업장을 만든 사람도 이 축을 보고
|
|
37
|
+
* 고른 적이 없다.
|
|
38
|
+
*/
|
|
12
39
|
creator: User;
|
|
13
40
|
creatorId: string;
|
|
14
41
|
createdAt: Date;
|
|
15
42
|
}
|
|
43
|
+
/** 아무도 안 정한 축이 도는 방식 — 바꾸려면 여기 하나만 고치면 된다. */
|
|
44
|
+
export declare const DEFAULT_APPROVAL_REQUIRED = false;
|
|
45
|
+
/**
|
|
46
|
+
* 이 축이 지금 어떻게 도는가. **아무도 안 정했으면 기본값으로 한 줄을 세우고 그것을 답한다.**
|
|
47
|
+
*
|
|
48
|
+
* 전에는 거절했다(`MASTER_POLICY_REQUIRED`). 그래서 새 사업장은 축 열여섯이 전부 막힌 채로 시작했고,
|
|
49
|
+
* 기준정보를 만들려던 사람이 「정책을 먼저 정하라」는 말만 받았다 — 어디서 정하는지는 안 적혀 있었다.
|
|
50
|
+
*
|
|
51
|
+
* 값을 그때그때 지어내지 않고 **줄을 세우는** 까닭은, 개정본이 자기가 어느 정책 아래 만들어졌는지를
|
|
52
|
+
* 그 줄로 가리키기 때문이다(ADR-0090). 가리킬 줄이 없으면 그 물음에 나중에 답할 수 없다.
|
|
53
|
+
*
|
|
54
|
+
* 그 줄의 `creator` 는 비어 있다. 아무도 고른 적이 없다는 것이 사실이고, 사업장을 만든 사람을 적으면
|
|
55
|
+
* 그 사람이 정한 것이 된다.
|
|
56
|
+
*/
|
|
16
57
|
export declare function masterChangePolicy(manager: EntityManager, domainId: string, axis: string): Promise<MasterChangePolicy>;
|
|
58
|
+
/**
|
|
59
|
+
* 지금 서 있는 줄.
|
|
60
|
+
*
|
|
61
|
+
* 둘 이상이 서 있으면 **가장 늦게 만들어진 것**을 답한다. 그 상태는 이 코드가 못 만들지만, 물러남
|
|
62
|
+
* 표를 두기 전에 쌓인 줄들이 그렇게 남아 있다. 다음 선언이 전부 물러나게 하고 하나로 정리한다.
|
|
63
|
+
*/
|
|
64
|
+
export declare function standingPolicy(manager: EntityManager, domainId: string, axis: string): Promise<MasterChangePolicy>;
|
|
65
|
+
/** 이 도메인에서 지금 서 있는 줄 전부 — 축을 열쇠로. */
|
|
66
|
+
export declare function standingPolicies(manager: EntityManager, domainId: string): Promise<Map<string, MasterChangePolicy>>;
|
|
17
67
|
export declare function declareMasterChangePolicy(manager: EntityManager, domainId: string, actorId: string, axis: string, approvalRequired: boolean, reason: string): Promise<MasterChangePolicy>;
|
|
18
|
-
/** The common mutation boundary also protects not-yet-revisioned axes when declared regulated. */
|
|
19
68
|
export declare function assertDirectMasterChange(manager: EntityManager, domainId: string, axis: string): Promise<void>;
|
|
20
69
|
export declare class MasterChangePolicyResolver {
|
|
21
70
|
masterChangePolicy(axis: string, context: ResolverContext): Promise<MasterChangePolicy>;
|
|
71
|
+
/**
|
|
72
|
+
* 축 전부의 정책 상태 — **안 정한 것도 답한다.**
|
|
73
|
+
*
|
|
74
|
+
* `masterChangePolicy` 는 안 정했으면 거절한다. 그것은 「고치기 전에 정해라」를 말하는 자리라 맞지만,
|
|
75
|
+
* **정하러 들어가는 화면까지 막는다.** 그래서 목록은 따로 둔다 — 안 정한 축은 안 정했다고 답한다.
|
|
76
|
+
*
|
|
77
|
+
* `canDeclare` 는 **이 사람이 소유자인가**다. 화면이 스스로 판단하지 않게 서버가 말한다 —
|
|
78
|
+
* 눌러 보고 거절당하는 화면이 되지 않도록.
|
|
79
|
+
*/
|
|
80
|
+
masterChangePolicies(context: ResolverContext): Promise<{
|
|
81
|
+
canDeclare: boolean;
|
|
82
|
+
axes: {
|
|
83
|
+
axis: string;
|
|
84
|
+
declared: boolean;
|
|
85
|
+
byDefault: boolean;
|
|
86
|
+
approvalRequired: boolean;
|
|
87
|
+
reason: string;
|
|
88
|
+
declaredAt: Date;
|
|
89
|
+
revisable: boolean;
|
|
90
|
+
approvalReady: boolean;
|
|
91
|
+
approvalActivity: string;
|
|
92
|
+
}[];
|
|
93
|
+
}>;
|
|
22
94
|
declareMasterChangePolicy(axis: string, approvalRequired: boolean, reason: string, context: ResolverContext): Promise<MasterChangePolicy>;
|
|
23
95
|
}
|
|
@@ -1,21 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MasterChangePolicyResolver = exports.MasterChangePolicy = void 0;
|
|
3
|
+
exports.MasterChangePolicyResolver = exports.DEFAULT_APPROVAL_REQUIRED = exports.MasterChangePolicy = exports.POLICY_AXES = void 0;
|
|
4
|
+
exports.axisHasRevision = axisHasRevision;
|
|
4
5
|
exports.masterChangePolicy = masterChangePolicy;
|
|
6
|
+
exports.standingPolicy = standingPolicy;
|
|
7
|
+
exports.standingPolicies = standingPolicies;
|
|
5
8
|
exports.declareMasterChangePolicy = declareMasterChangePolicy;
|
|
6
9
|
exports.assertDirectMasterChange = assertDirectMasterChange;
|
|
7
10
|
const tslib_1 = require("tslib");
|
|
8
11
|
const typeorm_1 = require("typeorm");
|
|
12
|
+
const typeorm_2 = require("typeorm");
|
|
9
13
|
const type_graphql_1 = require("type-graphql");
|
|
10
14
|
const shell_1 = require("@things-factory/shell");
|
|
11
15
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
12
16
|
const master_axis_1 = require("./master-axis");
|
|
17
|
+
const approval_route_1 = require("./approval-route");
|
|
13
18
|
// WorkShift owns a domain-wide arrangement, not a row in the generic master CRUD registry.
|
|
14
19
|
function policyAxis(axis) {
|
|
15
20
|
if (axis !== 'workShift')
|
|
16
21
|
(0, master_axis_1.masterAxisOf)(axis);
|
|
17
22
|
}
|
|
18
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* 정책을 정할 수 있는 축들 — 마스터 축 열다섯에 `workShift` 를 더한다.
|
|
25
|
+
*
|
|
26
|
+
* `workShift` 는 별도 모듈이라 `MASTER_AXES` 에 없는데 정책은 받는다(§`policyAxis`).
|
|
27
|
+
*/
|
|
28
|
+
exports.POLICY_AXES = [...master_axis_1.MASTER_AXES.map(one => one.key), 'workShift'];
|
|
29
|
+
/**
|
|
30
|
+
* 이 축의 바뀜을 **개정본으로 결재에 보낼 수 있나** — 승인으로 켤 수 있는 축인가.
|
|
31
|
+
*
|
|
32
|
+
* 마스터 축은 자기 선언(`MasterAxis.revisable`)이 답한다. `workShift` 는 그 표 밖에서 자기 꾸러미가
|
|
33
|
+
* 개정 체계를 지었으므로 참이다(`work-shift-revision-service`). 앱이 답하지 않는다 — 문이 없으면
|
|
34
|
+
* 어느 앱에서도 보낼 곳이 없다.
|
|
35
|
+
*/
|
|
36
|
+
function axisHasRevision(axis) {
|
|
37
|
+
if (axis === 'workShift')
|
|
38
|
+
return true;
|
|
39
|
+
return (0, master_axis_1.masterAxisOf)(axis).revisable;
|
|
40
|
+
}
|
|
41
|
+
/** 도메인 × 축의 기준정보 변경 정책 — 정하는 것도 바꾸는 것도 사업장 소유자의 몫이다. */
|
|
19
42
|
let MasterChangePolicy = class MasterChangePolicy {
|
|
20
43
|
};
|
|
21
44
|
exports.MasterChangePolicy = MasterChangePolicy;
|
|
@@ -44,7 +67,18 @@ tslib_1.__decorate([
|
|
|
44
67
|
tslib_1.__metadata("design:type", String)
|
|
45
68
|
], MasterChangePolicy.prototype, "reason", void 0);
|
|
46
69
|
tslib_1.__decorate([
|
|
47
|
-
(0, typeorm_1.
|
|
70
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
71
|
+
tslib_1.__metadata("design:type", Date
|
|
72
|
+
/**
|
|
73
|
+
* 누가 정했나 — **비어 있으면 아무도 안 정한 것**이고, 그 줄은 기본값으로 선 줄이다.
|
|
74
|
+
*
|
|
75
|
+
* 사람이 안 정한 줄에 사람을 적으면 그 사람이 정한 것이 된다. 사업장을 만든 사람도 이 축을 보고
|
|
76
|
+
* 고른 적이 없다.
|
|
77
|
+
*/
|
|
78
|
+
)
|
|
79
|
+
], MasterChangePolicy.prototype, "supersededAt", void 0);
|
|
80
|
+
tslib_1.__decorate([
|
|
81
|
+
(0, typeorm_1.ManyToOne)(() => auth_base_1.User, { nullable: true }),
|
|
48
82
|
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
49
83
|
], MasterChangePolicy.prototype, "creator", void 0);
|
|
50
84
|
tslib_1.__decorate([
|
|
@@ -56,16 +90,71 @@ tslib_1.__decorate([
|
|
|
56
90
|
tslib_1.__metadata("design:type", Date)
|
|
57
91
|
], MasterChangePolicy.prototype, "createdAt", void 0);
|
|
58
92
|
exports.MasterChangePolicy = MasterChangePolicy = tslib_1.__decorate([
|
|
59
|
-
(0, typeorm_1.Entity)('ops_master_change_policy')
|
|
60
|
-
|
|
93
|
+
(0, typeorm_1.Entity)('ops_master_change_policy')
|
|
94
|
+
/*
|
|
95
|
+
* 도메인 × 축에 **줄이 여럿 선다.** 정책은 바뀌고, 바뀌면 새 줄이 서고, 지난 개정본은 자기가 만들어질
|
|
96
|
+
* 때의 줄을 그대로 가리킨다 — 그래야 「그때는 무슨 규칙이었나」가 안 흔들린다.
|
|
97
|
+
*
|
|
98
|
+
* 전에는 유니크였다. 그래서 바꾸려면 그 줄을 덮어야 했고, 덮으면 승인받고 만든 개정본이 소급해서
|
|
99
|
+
* 승인 없이 만든 것이 됐다. 그래서 코드가 바꾸기를 거절했고, **정책을 영영 못 바꿨다.**
|
|
100
|
+
*
|
|
101
|
+
* ⚠ **이 정책을 바꾸는 일 자체는 결재에 안 태운다**(2026-09-22). 사업장 소유자가 정하는 것이라
|
|
102
|
+
* 소유자 확인만 받고 바로 선다. 태울 필요가 생기면 그때 결재 활동을 붙인다 — 줄이 여럿 서는
|
|
103
|
+
* 구조라 붙이는 자리는 이미 있다.
|
|
104
|
+
*/
|
|
105
|
+
,
|
|
106
|
+
(0, typeorm_1.Index)('ix_master_change_policy_axis', ['domain', 'axis']),
|
|
61
107
|
(0, typeorm_1.Index)('ix_master_change_policy_identity', ['domain', 'id'], { unique: true })
|
|
62
108
|
], MasterChangePolicy);
|
|
109
|
+
/** 아무도 안 정한 축이 도는 방식 — 바꾸려면 여기 하나만 고치면 된다. */
|
|
110
|
+
exports.DEFAULT_APPROVAL_REQUIRED = false;
|
|
111
|
+
/**
|
|
112
|
+
* 이 축이 지금 어떻게 도는가. **아무도 안 정했으면 기본값으로 한 줄을 세우고 그것을 답한다.**
|
|
113
|
+
*
|
|
114
|
+
* 전에는 거절했다(`MASTER_POLICY_REQUIRED`). 그래서 새 사업장은 축 열여섯이 전부 막힌 채로 시작했고,
|
|
115
|
+
* 기준정보를 만들려던 사람이 「정책을 먼저 정하라」는 말만 받았다 — 어디서 정하는지는 안 적혀 있었다.
|
|
116
|
+
*
|
|
117
|
+
* 값을 그때그때 지어내지 않고 **줄을 세우는** 까닭은, 개정본이 자기가 어느 정책 아래 만들어졌는지를
|
|
118
|
+
* 그 줄로 가리키기 때문이다(ADR-0090). 가리킬 줄이 없으면 그 물음에 나중에 답할 수 없다.
|
|
119
|
+
*
|
|
120
|
+
* 그 줄의 `creator` 는 비어 있다. 아무도 고른 적이 없다는 것이 사실이고, 사업장을 만든 사람을 적으면
|
|
121
|
+
* 그 사람이 정한 것이 된다.
|
|
122
|
+
*/
|
|
63
123
|
async function masterChangePolicy(manager, domainId, axis) {
|
|
64
124
|
policyAxis(axis);
|
|
65
|
-
const policy = await manager
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
125
|
+
const policy = await standingPolicy(manager, domainId, axis);
|
|
126
|
+
if (policy) {
|
|
127
|
+
return policy;
|
|
128
|
+
}
|
|
129
|
+
if (!manager.queryRunner?.isTransactionActive)
|
|
130
|
+
throw new shell_1.Refusal('TRANSACTION_REQUIRED', {}, 'A transaction is required.');
|
|
131
|
+
return manager.save(MasterChangePolicy, manager.create(MasterChangePolicy, {
|
|
132
|
+
domain: { id: domainId },
|
|
133
|
+
axis,
|
|
134
|
+
approvalRequired: exports.DEFAULT_APPROVAL_REQUIRED,
|
|
135
|
+
reason: 'nobody set this axis; it runs on the default'
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/** 이 축에서 물러나지 않은 줄 전부 — 보통 하나다. */
|
|
139
|
+
function standingRows(manager, domainId, axis) {
|
|
140
|
+
return manager.findBy(MasterChangePolicy, { domain: { id: domainId }, axis, supersededAt: (0, typeorm_2.IsNull)() });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 지금 서 있는 줄.
|
|
144
|
+
*
|
|
145
|
+
* 둘 이상이 서 있으면 **가장 늦게 만들어진 것**을 답한다. 그 상태는 이 코드가 못 만들지만, 물러남
|
|
146
|
+
* 표를 두기 전에 쌓인 줄들이 그렇게 남아 있다. 다음 선언이 전부 물러나게 하고 하나로 정리한다.
|
|
147
|
+
*/
|
|
148
|
+
async function standingPolicy(manager, domainId, axis) {
|
|
149
|
+
const rows = await standingRows(manager, domainId, axis);
|
|
150
|
+
return rows.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).pop() ?? null;
|
|
151
|
+
}
|
|
152
|
+
/** 이 도메인에서 지금 서 있는 줄 전부 — 축을 열쇠로. */
|
|
153
|
+
async function standingPolicies(manager, domainId) {
|
|
154
|
+
const rows = await manager.findBy(MasterChangePolicy, { domain: { id: domainId }, supersededAt: (0, typeorm_2.IsNull)() });
|
|
155
|
+
/* 이른 것부터 담아서 같은 축은 늦은 것이 이긴다 — `standingPolicy` 와 같은 답이 되도록. */
|
|
156
|
+
rows.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
157
|
+
return new Map(rows.map(one => [one.axis, one]));
|
|
69
158
|
}
|
|
70
159
|
async function declareMasterChangePolicy(manager, domainId, actorId, axis, approvalRequired, reason) {
|
|
71
160
|
if (!manager.queryRunner?.isTransactionActive)
|
|
@@ -73,32 +162,101 @@ async function declareMasterChangePolicy(manager, domainId, actorId, axis, appro
|
|
|
73
162
|
policyAxis(axis);
|
|
74
163
|
const domain = await manager.findOneByOrFail(shell_1.Domain, { id: domainId });
|
|
75
164
|
if (domain.owner !== actorId)
|
|
76
|
-
throw new shell_1.Refusal('MASTER_POLICY_OWNER_REQUIRED', {}, 'Only the domain owner may
|
|
165
|
+
throw new shell_1.Refusal('MASTER_POLICY_OWNER_REQUIRED', {}, 'Only the domain owner may declare or change the master change policy.');
|
|
77
166
|
if (!reason?.trim())
|
|
78
|
-
throw new shell_1.Refusal('MASTER_POLICY_REASON_REQUIRED', {}, 'Explain the
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
167
|
+
throw new shell_1.Refusal('MASTER_POLICY_REASON_REQUIRED', {}, 'Explain the policy declaration.');
|
|
168
|
+
/*
|
|
169
|
+
* 규제를 **거는** 쪽만 본다. 푸는 쪽은 결재선이 비어 있어도 되고, 오히려 비어서 멈춘 축을 푸는 것이
|
|
170
|
+
* 이 화면으로 할 수 있는 일이어야 한다.
|
|
171
|
+
*/
|
|
172
|
+
if (approvalRequired) {
|
|
173
|
+
/*
|
|
174
|
+
* 개정본 문이 없는 축은 **켜는 것부터 막는다**(2026-09-24). 켜면 직접 쓰기가 전부 거절되는데 결재로
|
|
175
|
+
* 보낼 곳이 없어서 그 축이 통째로 잠긴다. 결재선을 묻기 전에 본다 — 결재선이 있어도 보낼 문이 없다.
|
|
176
|
+
*/
|
|
177
|
+
if (!axisHasRevision(axis))
|
|
178
|
+
throw new shell_1.Refusal('MASTER_AXIS_HAS_NO_REVISION', { axis }, 'This master cannot be sent for approval yet, so it cannot be set to change only after approval.');
|
|
179
|
+
await (0, approval_route_1.assertApprovalRouteReady)(manager, domainId, axis);
|
|
180
|
+
}
|
|
181
|
+
const standing = await standingRows(manager, domainId, axis);
|
|
182
|
+
/*
|
|
183
|
+
* 같은 값을 다시 정하는 것은 아무 일도 아니다 — **하나만 서 있을 때** 그렇다. 여럿이 서 있으면
|
|
184
|
+
* 어느 것이 지금 것인지가 안 정해진 상태이므로, 값이 같아 보여도 전부 물리고 하나로 다시 세운다.
|
|
185
|
+
*/
|
|
186
|
+
if (standing.length === 1 && standing[0].approvalRequired === approvalRequired) {
|
|
187
|
+
return standing[0];
|
|
188
|
+
}
|
|
189
|
+
const supersededAt = new Date();
|
|
190
|
+
for (const row of standing) {
|
|
191
|
+
row.supersededAt = supersededAt;
|
|
192
|
+
await manager.save(MasterChangePolicy, row);
|
|
84
193
|
}
|
|
194
|
+
/*
|
|
195
|
+
* 바뀌면 **새 줄을 세운다. 있던 줄은 그대로 둔다.**
|
|
196
|
+
*
|
|
197
|
+
* 지난 개정본들이 그 줄을 가리키고 있다(`RecipeRevision.policy`, RESTRICT). 덮으면 그것들이
|
|
198
|
+
* 소급해서 다른 규칙 아래 만들어진 것이 된다 — 그래서 덮지 않고 새로 세운다. 오늘부터는 새 줄이
|
|
199
|
+
* 현재 정책이고, 지난 것은 자기 때의 줄을 계속 가리킨다.
|
|
200
|
+
*/
|
|
85
201
|
return manager.save(MasterChangePolicy, manager.create(MasterChangePolicy, {
|
|
86
|
-
domain,
|
|
202
|
+
domain: { id: domainId },
|
|
87
203
|
axis,
|
|
88
204
|
approvalRequired,
|
|
89
|
-
reason
|
|
205
|
+
reason,
|
|
90
206
|
creator: { id: actorId }
|
|
91
207
|
}));
|
|
92
208
|
}
|
|
93
|
-
/** The common mutation boundary also protects not-yet-revisioned axes when declared regulated. */
|
|
94
209
|
async function assertDirectMasterChange(manager, domainId, axis) {
|
|
95
|
-
const policy = await manager
|
|
210
|
+
const policy = await standingPolicy(manager, domainId, axis);
|
|
96
211
|
if (policy?.approvalRequired)
|
|
97
212
|
throw new shell_1.Refusal('MASTER_APPROVAL_REQUIRED', { axis }, 'This master axis requires approval in this domain.');
|
|
98
213
|
}
|
|
99
214
|
let MasterChangePolicyResolver = class MasterChangePolicyResolver {
|
|
100
215
|
async masterChangePolicy(axis, context) {
|
|
101
|
-
|
|
216
|
+
/* 읽기만 하는 자리다. 안 정한 축에 줄을 세우지 않는다 — 그것은 고치러 들어간 쪽이 할 일이다. */
|
|
217
|
+
return standingPolicy((0, shell_1.getRepository)(MasterChangePolicy).manager, context.state.domain.id, axis);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 축 전부의 정책 상태 — **안 정한 것도 답한다.**
|
|
221
|
+
*
|
|
222
|
+
* `masterChangePolicy` 는 안 정했으면 거절한다. 그것은 「고치기 전에 정해라」를 말하는 자리라 맞지만,
|
|
223
|
+
* **정하러 들어가는 화면까지 막는다.** 그래서 목록은 따로 둔다 — 안 정한 축은 안 정했다고 답한다.
|
|
224
|
+
*
|
|
225
|
+
* `canDeclare` 는 **이 사람이 소유자인가**다. 화면이 스스로 판단하지 않게 서버가 말한다 —
|
|
226
|
+
* 눌러 보고 거절당하는 화면이 되지 않도록.
|
|
227
|
+
*/
|
|
228
|
+
async masterChangePolicies(context) {
|
|
229
|
+
const { domain, user } = context.state;
|
|
230
|
+
const manager = (0, shell_1.getRepository)(MasterChangePolicy).manager;
|
|
231
|
+
/*
|
|
232
|
+
* **물러나지 않은 줄만** 읽는다. 축마다 줄이 여럿이라, 전부 담으면 저장소가 주는 차례대로 덮여서
|
|
233
|
+
* 바꾼 뒤에도 화면에 옛 값이 떴다(2026-09-22 화면에서 확인).
|
|
234
|
+
*/
|
|
235
|
+
const byAxis = await standingPolicies(manager, domain.id);
|
|
236
|
+
return {
|
|
237
|
+
canDeclare: domain.owner === user?.id,
|
|
238
|
+
axes: await Promise.all(exports.POLICY_AXES.map(async (axis) => {
|
|
239
|
+
const policy = byAxis.get(axis);
|
|
240
|
+
/*
|
|
241
|
+
* 결재 길이 준비됐나 — **화면이 누르기 전에 알아야 한다.** 답이 없는 축은 이 앱이 아직 이
|
|
242
|
+
* 물음에 답할 길이 없다는 뜻이라 막지 않는다(`inspectApprovalRoute`).
|
|
243
|
+
*/
|
|
244
|
+
const route = await (0, approval_route_1.inspectApprovalRoute)(manager, domain.id, axis);
|
|
245
|
+
return {
|
|
246
|
+
axis,
|
|
247
|
+
declared: !!policy,
|
|
248
|
+
/* 줄은 섰지만 사람이 고른 적은 없다 — 화면이 그것을 다르게 보인다. */
|
|
249
|
+
byDefault: !!policy && !policy.creatorId,
|
|
250
|
+
approvalRequired: policy?.approvalRequired ?? null,
|
|
251
|
+
reason: policy?.reason ?? null,
|
|
252
|
+
declaredAt: policy?.createdAt ?? null,
|
|
253
|
+
/* 승인으로 켤 수 있는 축인가 — 아니면 화면은 누르기 전에 그 까닭을 말한다. */
|
|
254
|
+
revisable: axisHasRevision(axis),
|
|
255
|
+
approvalReady: route ? route.ready : true,
|
|
256
|
+
approvalActivity: route?.activity ?? null
|
|
257
|
+
};
|
|
258
|
+
}))
|
|
259
|
+
};
|
|
102
260
|
}
|
|
103
261
|
async declareMasterChangePolicy(axis, approvalRequired, reason, context) {
|
|
104
262
|
const { tx, domain, user } = context.state;
|
|
@@ -115,6 +273,14 @@ tslib_1.__decorate([
|
|
|
115
273
|
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
116
274
|
tslib_1.__metadata("design:returntype", Promise)
|
|
117
275
|
], MasterChangePolicyResolver.prototype, "masterChangePolicy", null);
|
|
276
|
+
tslib_1.__decorate([
|
|
277
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
|
|
278
|
+
(0, type_graphql_1.Query)(() => shell_1.ScalarObject),
|
|
279
|
+
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
280
|
+
tslib_1.__metadata("design:type", Function),
|
|
281
|
+
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
282
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
283
|
+
], MasterChangePolicyResolver.prototype, "masterChangePolicies", null);
|
|
118
284
|
tslib_1.__decorate([
|
|
119
285
|
(0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
|
|
120
286
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-change-policy.js","sourceRoot":"","sources":["../../server/service/master-change-policy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n EntityManager,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId\n} from 'typeorm'\nimport { Arg, Ctx, Directive, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { Domain, getRepository, Refusal, ScalarObject, SpeakRefusals } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { masterAxisOf } from './master-axis'\n\n// WorkShift owns a domain-wide arrangement, not a row in the generic master CRUD registry.\nfunction policyAxis(axis: string) {\n if (axis !== 'workShift') masterAxisOf(axis)\n}\n\n/** Initial domain×axis declaration. Changing an established policy needs its own approved decision. */\n@Entity('ops_master_change_policy')\n@Index('ix_master_change_policy_axis', ['domain', 'axis'], { unique: true })\n@Index('ix_master_change_policy_identity', ['domain', 'id'], { unique: true })\nexport class MasterChangePolicy {\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false }) domain: Domain\n @RelationId((row: MasterChangePolicy) => row.domain) domainId: string\n @Column() axis: string\n @Column() approvalRequired: boolean\n @Column('text') reason: string\n @ManyToOne(() => User, { nullable: false }) creator: User\n @RelationId((row: MasterChangePolicy) => row.creator) creatorId: string\n @CreateDateColumn() createdAt: Date\n}\n\nexport async function masterChangePolicy(manager: EntityManager, domainId: string, axis: string) {\n policyAxis(axis)\n const policy = await manager.findOneBy(MasterChangePolicy, { domain: { id: domainId }, axis })\n if (!policy)\n throw new Refusal(\n 'MASTER_POLICY_REQUIRED',\n { axis },\n 'Declare the domain policy for this master axis before publishing changes.'\n )\n return policy\n}\n\nexport async function declareMasterChangePolicy(\n manager: EntityManager,\n domainId: string,\n actorId: string,\n axis: string,\n approvalRequired: boolean,\n reason: string\n) {\n if (!manager.queryRunner?.isTransactionActive)\n throw new Refusal('TRANSACTION_REQUIRED', {}, 'A transaction is required.')\n policyAxis(axis)\n const domain = await manager.findOneByOrFail(Domain, { id: domainId })\n if (domain.owner !== actorId)\n throw new Refusal(\n 'MASTER_POLICY_OWNER_REQUIRED',\n {},\n 'Only the domain owner may make the initial policy declaration.'\n )\n if (!reason?.trim()) throw new Refusal('MASTER_POLICY_REASON_REQUIRED', {}, 'Explain the initial policy declaration.')\n const existing = await manager.findOneBy(MasterChangePolicy, { domain: { id: domainId }, axis })\n if (existing) {\n if (existing.approvalRequired === approvalRequired) return existing\n throw new Refusal(\n 'MASTER_POLICY_CHANGE_REQUIRES_APPROVAL',\n {},\n 'An established regulation policy cannot be changed through initial declaration.'\n )\n }\n return manager.save(\n MasterChangePolicy,\n manager.create(MasterChangePolicy, {\n domain,\n axis,\n approvalRequired,\n reason: reason.trim(),\n creator: { id: actorId }\n })\n )\n}\n\n/** The common mutation boundary also protects not-yet-revisioned axes when declared regulated. */\nexport async function assertDirectMasterChange(manager: EntityManager, domainId: string, axis: string) {\n const policy = await manager.findOneBy(MasterChangePolicy, { domain: { id: domainId }, axis })\n if (policy?.approvalRequired)\n throw new Refusal('MASTER_APPROVAL_REQUIRED', { axis }, 'This master axis requires approval in this domain.')\n}\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class MasterChangePolicyResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async masterChangePolicy(@Arg('axis') axis: string, @Ctx() context: ResolverContext) {\n return masterChangePolicy(getRepository(MasterChangePolicy).manager, context.state.domain.id, axis)\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async declareMasterChangePolicy(\n @Arg('axis') axis: string,\n @Arg('approvalRequired') approvalRequired: boolean,\n @Arg('reason') reason: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return declareMasterChangePolicy(tx, domain.id, user.id, axis, approvalRequired, reason)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"master-change-policy.js","sourceRoot":"","sources":["../../server/service/master-change-policy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n EntityManager,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId\n} from 'typeorm'\nimport { IsNull } from 'typeorm'\nimport { Arg, Ctx, Directive, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { Domain, getRepository, Refusal, ScalarObject, SpeakRefusals } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { MASTER_AXES, masterAxisOf } from './master-axis'\nimport { assertApprovalRouteReady, inspectApprovalRoute } from './approval-route'\n\n// WorkShift owns a domain-wide arrangement, not a row in the generic master CRUD registry.\nfunction policyAxis(axis: string) {\n if (axis !== 'workShift') masterAxisOf(axis)\n}\n\n/**\n * 정책을 정할 수 있는 축들 — 마스터 축 열다섯에 `workShift` 를 더한다.\n *\n * `workShift` 는 별도 모듈이라 `MASTER_AXES` 에 없는데 정책은 받는다(§`policyAxis`).\n */\nexport const POLICY_AXES = [...MASTER_AXES.map(one => one.key), 'workShift']\n\n/**\n * 이 축의 바뀜을 **개정본으로 결재에 보낼 수 있나** — 승인으로 켤 수 있는 축인가.\n *\n * 마스터 축은 자기 선언(`MasterAxis.revisable`)이 답한다. `workShift` 는 그 표 밖에서 자기 꾸러미가\n * 개정 체계를 지었으므로 참이다(`work-shift-revision-service`). 앱이 답하지 않는다 — 문이 없으면\n * 어느 앱에서도 보낼 곳이 없다.\n */\nexport function axisHasRevision(axis: string): boolean {\n if (axis === 'workShift') return true\n return masterAxisOf(axis).revisable\n}\n\n/** 도메인 × 축의 기준정보 변경 정책 — 정하는 것도 바꾸는 것도 사업장 소유자의 몫이다. */\n@Entity('ops_master_change_policy')\n/*\n * 도메인 × 축에 **줄이 여럿 선다.** 정책은 바뀌고, 바뀌면 새 줄이 서고, 지난 개정본은 자기가 만들어질\n * 때의 줄을 그대로 가리킨다 — 그래야 「그때는 무슨 규칙이었나」가 안 흔들린다.\n *\n * 전에는 유니크였다. 그래서 바꾸려면 그 줄을 덮어야 했고, 덮으면 승인받고 만든 개정본이 소급해서\n * 승인 없이 만든 것이 됐다. 그래서 코드가 바꾸기를 거절했고, **정책을 영영 못 바꿨다.**\n *\n * ⚠ **이 정책을 바꾸는 일 자체는 결재에 안 태운다**(2026-09-22). 사업장 소유자가 정하는 것이라\n * 소유자 확인만 받고 바로 선다. 태울 필요가 생기면 그때 결재 활동을 붙인다 — 줄이 여럿 서는\n * 구조라 붙이는 자리는 이미 있다.\n */\n@Index('ix_master_change_policy_axis', ['domain', 'axis'])\n@Index('ix_master_change_policy_identity', ['domain', 'id'], { unique: true })\nexport class MasterChangePolicy {\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false }) domain: Domain\n @RelationId((row: MasterChangePolicy) => row.domain) domainId: string\n @Column() axis: string\n @Column() approvalRequired: boolean\n @Column('text') reason: string\n /**\n * 이 줄이 물러난 시각 — 비어 있는 줄이 **지금 정책**이다.\n *\n * 차례를 `createdAt` 으로 세우지 않는다. sqlite 의 `datetime('now')` 는 초까지라, 한 사람이 같은\n * 초에 두 번 바꾸면 어느 줄이 나중인지 저장소가 못 가린다. 물러난 줄에 표를 하면 그 물음이 사라진다.\n */\n @Column({ nullable: true }) supersededAt: Date\n /**\n * 누가 정했나 — **비어 있으면 아무도 안 정한 것**이고, 그 줄은 기본값으로 선 줄이다.\n *\n * 사람이 안 정한 줄에 사람을 적으면 그 사람이 정한 것이 된다. 사업장을 만든 사람도 이 축을 보고\n * 고른 적이 없다.\n */\n @ManyToOne(() => User, { nullable: true }) creator: User\n @RelationId((row: MasterChangePolicy) => row.creator) creatorId: string\n @CreateDateColumn() createdAt: Date\n}\n\n/** 아무도 안 정한 축이 도는 방식 — 바꾸려면 여기 하나만 고치면 된다. */\nexport const DEFAULT_APPROVAL_REQUIRED = false\n\n/**\n * 이 축이 지금 어떻게 도는가. **아무도 안 정했으면 기본값으로 한 줄을 세우고 그것을 답한다.**\n *\n * 전에는 거절했다(`MASTER_POLICY_REQUIRED`). 그래서 새 사업장은 축 열여섯이 전부 막힌 채로 시작했고,\n * 기준정보를 만들려던 사람이 「정책을 먼저 정하라」는 말만 받았다 — 어디서 정하는지는 안 적혀 있었다.\n *\n * 값을 그때그때 지어내지 않고 **줄을 세우는** 까닭은, 개정본이 자기가 어느 정책 아래 만들어졌는지를\n * 그 줄로 가리키기 때문이다(ADR-0090). 가리킬 줄이 없으면 그 물음에 나중에 답할 수 없다.\n *\n * 그 줄의 `creator` 는 비어 있다. 아무도 고른 적이 없다는 것이 사실이고, 사업장을 만든 사람을 적으면\n * 그 사람이 정한 것이 된다.\n */\nexport async function masterChangePolicy(manager: EntityManager, domainId: string, axis: string) {\n policyAxis(axis)\n const policy = await standingPolicy(manager, domainId, axis)\n if (policy) {\n return policy\n }\n\n if (!manager.queryRunner?.isTransactionActive)\n throw new Refusal('TRANSACTION_REQUIRED', {}, 'A transaction is required.')\n\n return manager.save(\n MasterChangePolicy,\n manager.create(MasterChangePolicy, {\n domain: { id: domainId },\n axis,\n approvalRequired: DEFAULT_APPROVAL_REQUIRED,\n reason: 'nobody set this axis; it runs on the default'\n })\n )\n}\n\n/** 이 축에서 물러나지 않은 줄 전부 — 보통 하나다. */\nfunction standingRows(manager: EntityManager, domainId: string, axis: string) {\n return manager.findBy(MasterChangePolicy, { domain: { id: domainId }, axis, supersededAt: IsNull() })\n}\n\n/**\n * 지금 서 있는 줄.\n *\n * 둘 이상이 서 있으면 **가장 늦게 만들어진 것**을 답한다. 그 상태는 이 코드가 못 만들지만, 물러남\n * 표를 두기 전에 쌓인 줄들이 그렇게 남아 있다. 다음 선언이 전부 물러나게 하고 하나로 정리한다.\n */\nexport async function standingPolicy(manager: EntityManager, domainId: string, axis: string) {\n const rows = await standingRows(manager, domainId, axis)\n\n return rows.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).pop() ?? null\n}\n\n/** 이 도메인에서 지금 서 있는 줄 전부 — 축을 열쇠로. */\nexport async function standingPolicies(manager: EntityManager, domainId: string) {\n const rows = await manager.findBy(MasterChangePolicy, { domain: { id: domainId }, supersededAt: IsNull() })\n\n /* 이른 것부터 담아서 같은 축은 늦은 것이 이긴다 — `standingPolicy` 와 같은 답이 되도록. */\n rows.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())\n\n return new Map(rows.map(one => [one.axis, one]))\n}\n\nexport async function declareMasterChangePolicy(\n manager: EntityManager,\n domainId: string,\n actorId: string,\n axis: string,\n approvalRequired: boolean,\n reason: string\n) {\n if (!manager.queryRunner?.isTransactionActive)\n throw new Refusal('TRANSACTION_REQUIRED', {}, 'A transaction is required.')\n policyAxis(axis)\n const domain = await manager.findOneByOrFail(Domain, { id: domainId })\n if (domain.owner !== actorId)\n throw new Refusal(\n 'MASTER_POLICY_OWNER_REQUIRED',\n {},\n 'Only the domain owner may declare or change the master change policy.'\n )\n if (!reason?.trim()) throw new Refusal('MASTER_POLICY_REASON_REQUIRED', {}, 'Explain the policy declaration.')\n\n /*\n * 규제를 **거는** 쪽만 본다. 푸는 쪽은 결재선이 비어 있어도 되고, 오히려 비어서 멈춘 축을 푸는 것이\n * 이 화면으로 할 수 있는 일이어야 한다.\n */\n if (approvalRequired) {\n /*\n * 개정본 문이 없는 축은 **켜는 것부터 막는다**(2026-09-24). 켜면 직접 쓰기가 전부 거절되는데 결재로\n * 보낼 곳이 없어서 그 축이 통째로 잠긴다. 결재선을 묻기 전에 본다 — 결재선이 있어도 보낼 문이 없다.\n */\n if (!axisHasRevision(axis))\n throw new Refusal(\n 'MASTER_AXIS_HAS_NO_REVISION',\n { axis },\n 'This master cannot be sent for approval yet, so it cannot be set to change only after approval.'\n )\n await assertApprovalRouteReady(manager, domainId, axis)\n }\n\n const standing = await standingRows(manager, domainId, axis)\n\n /*\n * 같은 값을 다시 정하는 것은 아무 일도 아니다 — **하나만 서 있을 때** 그렇다. 여럿이 서 있으면\n * 어느 것이 지금 것인지가 안 정해진 상태이므로, 값이 같아 보여도 전부 물리고 하나로 다시 세운다.\n */\n if (standing.length === 1 && standing[0].approvalRequired === approvalRequired) {\n return standing[0]\n }\n\n const supersededAt = new Date()\n for (const row of standing) {\n row.supersededAt = supersededAt\n await manager.save(MasterChangePolicy, row)\n }\n\n /*\n * 바뀌면 **새 줄을 세운다. 있던 줄은 그대로 둔다.**\n *\n * 지난 개정본들이 그 줄을 가리키고 있다(`RecipeRevision.policy`, RESTRICT). 덮으면 그것들이\n * 소급해서 다른 규칙 아래 만들어진 것이 된다 — 그래서 덮지 않고 새로 세운다. 오늘부터는 새 줄이\n * 현재 정책이고, 지난 것은 자기 때의 줄을 계속 가리킨다.\n */\n return manager.save(\n MasterChangePolicy,\n manager.create(MasterChangePolicy, {\n domain: { id: domainId },\n axis,\n approvalRequired,\n reason,\n creator: { id: actorId }\n })\n )\n}\n\nexport async function assertDirectMasterChange(manager: EntityManager, domainId: string, axis: string) {\n const policy = await standingPolicy(manager, domainId, axis)\n if (policy?.approvalRequired)\n throw new Refusal('MASTER_APPROVAL_REQUIRED', { axis }, 'This master axis requires approval in this domain.')\n}\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class MasterChangePolicyResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async masterChangePolicy(@Arg('axis') axis: string, @Ctx() context: ResolverContext) {\n /* 읽기만 하는 자리다. 안 정한 축에 줄을 세우지 않는다 — 그것은 고치러 들어간 쪽이 할 일이다. */\n return standingPolicy(getRepository(MasterChangePolicy).manager, context.state.domain.id, axis)\n }\n\n /**\n * 축 전부의 정책 상태 — **안 정한 것도 답한다.**\n *\n * `masterChangePolicy` 는 안 정했으면 거절한다. 그것은 「고치기 전에 정해라」를 말하는 자리라 맞지만,\n * **정하러 들어가는 화면까지 막는다.** 그래서 목록은 따로 둔다 — 안 정한 축은 안 정했다고 답한다.\n *\n * `canDeclare` 는 **이 사람이 소유자인가**다. 화면이 스스로 판단하지 않게 서버가 말한다 —\n * 눌러 보고 거절당하는 화면이 되지 않도록.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async masterChangePolicies(@Ctx() context: ResolverContext) {\n const { domain, user } = context.state\n const manager = getRepository(MasterChangePolicy).manager\n /*\n * **물러나지 않은 줄만** 읽는다. 축마다 줄이 여럿이라, 전부 담으면 저장소가 주는 차례대로 덮여서\n * 바꾼 뒤에도 화면에 옛 값이 떴다(2026-09-22 화면에서 확인).\n */\n const byAxis = await standingPolicies(manager, domain.id)\n\n return {\n canDeclare: domain.owner === user?.id,\n axes: await Promise.all(\n POLICY_AXES.map(async axis => {\n const policy = byAxis.get(axis)\n /*\n * 결재 길이 준비됐나 — **화면이 누르기 전에 알아야 한다.** 답이 없는 축은 이 앱이 아직 이\n * 물음에 답할 길이 없다는 뜻이라 막지 않는다(`inspectApprovalRoute`).\n */\n const route = await inspectApprovalRoute(manager, domain.id, axis)\n\n return {\n axis,\n declared: !!policy,\n /* 줄은 섰지만 사람이 고른 적은 없다 — 화면이 그것을 다르게 보인다. */\n byDefault: !!policy && !policy.creatorId,\n approvalRequired: policy?.approvalRequired ?? null,\n reason: policy?.reason ?? null,\n declaredAt: policy?.createdAt ?? null,\n /* 승인으로 켤 수 있는 축인가 — 아니면 화면은 누르기 전에 그 까닭을 말한다. */\n revisable: axisHasRevision(axis),\n approvalReady: route ? route.ready : true,\n approvalActivity: route?.activity ?? null\n }\n })\n )\n }\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async declareMasterChangePolicy(\n @Arg('axis') axis: string,\n @Arg('approvalRequired') approvalRequired: boolean,\n @Arg('reason') reason: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return declareMasterChangePolicy(tx, domain.id, user.id, axis, approvalRequired, reason)\n }\n}\n"]}
|
|
@@ -41,8 +41,8 @@ export declare class MaterialDefinitionRevisionResolver {
|
|
|
41
41
|
testSpecificationIds: string[];
|
|
42
42
|
effectiveFrom: Date;
|
|
43
43
|
reason: string;
|
|
44
|
-
creator: import("@things-factory/auth-base").User;
|
|
45
|
-
creatorId: string;
|
|
44
|
+
creator: import("@things-factory/auth-base").User | null;
|
|
45
|
+
creatorId: string | null;
|
|
46
46
|
createdAt: Date;
|
|
47
47
|
frozenAt: Date | null;
|
|
48
48
|
contentHash: string | null;
|
|
@@ -51,4 +51,9 @@ export declare class MaterialDefinitionRevisionResolver {
|
|
|
51
51
|
}>;
|
|
52
52
|
createMaterialDefinitionDraft(content: MaterialDefinitionDraftContent, materialId: string, name: string, baseRevisionId: string, context: ResolverContext): Promise<import("./material-revision").MaterialDefinitionRevision>;
|
|
53
53
|
editMaterialDefinitionDraft(revisionId: string, editGeneration: number, content: MaterialDefinitionDraftContent, context: ResolverContext): Promise<import("./material-revision").MaterialDefinitionRevision>;
|
|
54
|
+
/** 비규제 사업장의 저장 한 번 — 초안과 발행이 한 트랜잭션이다. 승인제면 거절되고 아무것도 안 남는다. */
|
|
55
|
+
saveAndPublishMaterialDefinition(content: MaterialDefinitionDraftContent, materialId: string, name: string, baseRevisionId: string, context: ResolverContext): Promise<{
|
|
56
|
+
revision: import("./material-revision").MaterialDefinitionRevision;
|
|
57
|
+
plan: import("./material-revision").MaterialDefinitionPlan;
|
|
58
|
+
}>;
|
|
54
59
|
}
|
|
@@ -22,6 +22,11 @@ let MaterialDefinitionRevisionResolver = class MaterialDefinitionRevisionResolve
|
|
|
22
22
|
const { tx, domain, user } = context.state;
|
|
23
23
|
return (0, material_revision_service_1.editMaterialDefinitionDraft)(tx, domain.id, user.id, revisionId, editGeneration, content);
|
|
24
24
|
}
|
|
25
|
+
/** 비규제 사업장의 저장 한 번 — 초안과 발행이 한 트랜잭션이다. 승인제면 거절되고 아무것도 안 남는다. */
|
|
26
|
+
async saveAndPublishMaterialDefinition(content, materialId, name, baseRevisionId, context) {
|
|
27
|
+
const { tx, domain, user } = context.state;
|
|
28
|
+
return (0, material_revision_service_1.saveAndPublishMaterialDefinition)(tx, domain.id, user.id, { materialId, name, baseRevisionId, content });
|
|
29
|
+
}
|
|
25
30
|
};
|
|
26
31
|
exports.MaterialDefinitionRevisionResolver = MaterialDefinitionRevisionResolver;
|
|
27
32
|
tslib_1.__decorate([
|
|
@@ -69,6 +74,19 @@ tslib_1.__decorate([
|
|
|
69
74
|
tslib_1.__metadata("design:paramtypes", [String, Number, Object, Object]),
|
|
70
75
|
tslib_1.__metadata("design:returntype", Promise)
|
|
71
76
|
], MaterialDefinitionRevisionResolver.prototype, "editMaterialDefinitionDraft", null);
|
|
77
|
+
tslib_1.__decorate([
|
|
78
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
|
|
79
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
80
|
+
(0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
|
|
81
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('content', () => shell_1.ScalarObject)),
|
|
82
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('materialId', () => String, { nullable: true })),
|
|
83
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('name', () => String, { nullable: true })),
|
|
84
|
+
tslib_1.__param(3, (0, type_graphql_1.Arg)('baseRevisionId', () => String, { nullable: true })),
|
|
85
|
+
tslib_1.__param(4, (0, type_graphql_1.Ctx)()),
|
|
86
|
+
tslib_1.__metadata("design:type", Function),
|
|
87
|
+
tslib_1.__metadata("design:paramtypes", [Object, String, String, String, Object]),
|
|
88
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
89
|
+
], MaterialDefinitionRevisionResolver.prototype, "saveAndPublishMaterialDefinition", null);
|
|
72
90
|
exports.MaterialDefinitionRevisionResolver = MaterialDefinitionRevisionResolver = tslib_1.__decorate([
|
|
73
91
|
(0, type_graphql_1.Resolver)(),
|
|
74
92
|
(0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"material-revision-resolver.js","sourceRoot":"","sources":["../../../server/service/material/material-revision-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAAiG;AACjG,iDAAkF;AAClF,+DAA0D;AAC1D,
|
|
1
|
+
{"version":3,"file":"material-revision-resolver.js","sourceRoot":"","sources":["../../../server/service/material/material-revision-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Int, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { getRepository, ScalarObject, SpeakRefusals } from '@things-factory/shell'\nimport { MaterialDefinition } from './material-definition'\nimport { saveAndPublishMaterialDefinition, createMaterialDefinitionDraft, editMaterialDefinitionDraft, publishUnregulatedMaterialDefinition, materialDefinitionRevisionOverview, MaterialDefinitionDraftContent } from './material-revision-service'\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class MaterialDefinitionRevisionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async publishMaterialDefinitionDraft(@Arg('revisionId') revisionId: string, @Arg('editGeneration', () => Int) editGeneration: number, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return publishUnregulatedMaterialDefinition(tx, domain.id, user.id, revisionId, editGeneration)\n }\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async materialDefinitionRevisions(@Arg('materialId') materialId: string, @Ctx() context: ResolverContext) {\n return materialDefinitionRevisionOverview(getRepository(MaterialDefinition).manager, context.state.domain.id, materialId)\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async createMaterialDefinitionDraft(\n @Arg('content', () => ScalarObject) content: MaterialDefinitionDraftContent,\n @Arg('materialId', () => String, { nullable: true }) materialId: string,\n @Arg('name', () => String, { nullable: true }) name: string,\n @Arg('baseRevisionId', () => String, { nullable: true }) baseRevisionId: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return createMaterialDefinitionDraft(tx, domain.id, user.id, { materialId, name, baseRevisionId, content })\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async editMaterialDefinitionDraft(\n @Arg('revisionId') revisionId: string,\n @Arg('editGeneration', () => Int) editGeneration: number,\n @Arg('content', () => ScalarObject) content: MaterialDefinitionDraftContent,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return editMaterialDefinitionDraft(tx, domain.id, user.id, revisionId, editGeneration, content)\n }\n\n /** 비규제 사업장의 저장 한 번 — 초안과 발행이 한 트랜잭션이다. 승인제면 거절되고 아무것도 안 남는다. */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async saveAndPublishMaterialDefinition(\n @Arg('content', () => ScalarObject) content: MaterialDefinitionDraftContent,\n @Arg('materialId', () => String, { nullable: true }) materialId: string,\n @Arg('name', () => String, { nullable: true }) name: string,\n @Arg('baseRevisionId', () => String, { nullable: true }) baseRevisionId: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return saveAndPublishMaterialDefinition(tx, domain.id, user.id, { materialId, name, baseRevisionId, content })\n }\n}\n"]}
|
|
@@ -81,6 +81,21 @@ export declare function resolveMaterialDefinition(manager: EntityManager, domain
|
|
|
81
81
|
plan: MaterialDefinitionPlan;
|
|
82
82
|
segment: MaterialDefinitionSegment;
|
|
83
83
|
}>;
|
|
84
|
+
/**
|
|
85
|
+
* **개정본이 하나도 없는 품목**마다 지금 서 있는 행을 첫 개정본으로 적는다 — 기준선(2026-09-24).
|
|
86
|
+
* 규칙은 검사 명세의 `standTestSpecificationBaselines` 와 같다. 품목만의 것 둘:
|
|
87
|
+
*
|
|
88
|
+
* - 등급 · 검사 명세는 **이름으로** 가리킨다. 그 이름이 이 사업장에 없으면 세우지 않고 까닭을 남긴다 —
|
|
89
|
+
* 없는 것을 가리키는 개정본은 새 지시가 쓰는 순간 거절된다. 이름은 사업장마다 한 번 읽어 대조한다.
|
|
90
|
+
* - 행의 `testResults` 는 개정본 내용이 아니다. 루트에 그대로 남는다.
|
|
91
|
+
*/
|
|
92
|
+
export declare function standMaterialDefinitionBaselines(manager: EntityManager, domainId: string, at: Date, edition: string): Promise<{
|
|
93
|
+
stood: number;
|
|
94
|
+
unreadable: {
|
|
95
|
+
name: string;
|
|
96
|
+
problem: string;
|
|
97
|
+
}[];
|
|
98
|
+
}>;
|
|
84
99
|
/** Replay reads verify the captured digest too, not only the currently effective schedule. */
|
|
85
100
|
export declare function materialDefinitionRevisionForBasis(manager: EntityManager, domainId: string, basis: MaterialDefinitionBasis): Promise<MaterialDefinitionRevision>;
|
|
86
101
|
/** Must run inside the transaction that writes the business record. */
|
|
@@ -124,8 +139,8 @@ export declare function materialDefinitionRevisionOverview(manager: EntityManage
|
|
|
124
139
|
testSpecificationIds: string[];
|
|
125
140
|
effectiveFrom: Date;
|
|
126
141
|
reason: string;
|
|
127
|
-
creator: import("@things-factory/auth-base").User;
|
|
128
|
-
creatorId: string;
|
|
142
|
+
creator: import("@things-factory/auth-base").User | null;
|
|
143
|
+
creatorId: string | null;
|
|
129
144
|
createdAt: Date;
|
|
130
145
|
frozenAt: Date | null;
|
|
131
146
|
contentHash: string | null;
|
|
@@ -142,3 +157,18 @@ export declare function effectiveMaterialDefinition(manager: EntityManager, doma
|
|
|
142
157
|
effectiveStart: Date;
|
|
143
158
|
effectiveEnd: Date;
|
|
144
159
|
}>;
|
|
160
|
+
/**
|
|
161
|
+
* **비규제 사업장의 「저장」 한 번** — 초안을 만들고 바로 발행한다, 한 트랜잭션에서(ADR-0086 09-22 보탬).
|
|
162
|
+
* 비규제면 사람이 누를 것은 저장 하나다. 두 걸음(초안 저장 → 발행)이 구조상 필연인 것처럼 보이게 하지 않는다.
|
|
163
|
+
* 이 축이 승인제면 발행이 `MASTER_APPROVAL_REQUIRED` 로 거절되고, 같은 트랜잭션이라 초안도 함께 물러난다 —
|
|
164
|
+
* 반쯤 저장된 것이 남지 않는다. 개정본 · 적용계획 · 구간은 두 걸음 때와 똑같이 선다.
|
|
165
|
+
*/
|
|
166
|
+
export declare function saveAndPublishMaterialDefinition(manager: EntityManager, domainId: string, actorId: string, asked: {
|
|
167
|
+
materialId?: string;
|
|
168
|
+
name?: string;
|
|
169
|
+
baseRevisionId?: string;
|
|
170
|
+
content: MaterialDefinitionDraftContent;
|
|
171
|
+
}): Promise<{
|
|
172
|
+
revision: MaterialDefinitionRevision;
|
|
173
|
+
plan: MaterialDefinitionPlan;
|
|
174
|
+
}>;
|