@things-factory/ops-master 10.1.37 → 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.js +8 -22
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/master-change-policy-axes.d.ts +17 -0
- package/dist-client/master-change-policy-axes.js +17 -0
- package/dist-client/master-change-policy-axes.js.map +1 -1
- package/dist-client/master-change-policy-panel.d.ts +10 -0
- package/dist-client/master-change-policy-panel.js +79 -20
- package/dist-client/master-change-policy-panel.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/index.d.ts +2 -1
- package/dist-server/service/index.js +1 -0
- 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 +9 -0
- package/dist-server/service/master-change-policy.js +21 -0
- 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 +4 -5
- package/translations/en.json +5 -1
- package/translations/ja.json +5 -1
- package/translations/ko.json +5 -1
- package/translations/ms.json +5 -1
- package/translations/zh.json +5 -1
|
@@ -46,8 +46,8 @@ export declare class RecipeRevisionResolver {
|
|
|
46
46
|
route: string | null;
|
|
47
47
|
effectiveFrom: Date;
|
|
48
48
|
reason: string;
|
|
49
|
-
creator: import("@things-factory/auth-base").User;
|
|
50
|
-
creatorId: string;
|
|
49
|
+
creator: import("@things-factory/auth-base").User | null;
|
|
50
|
+
creatorId: string | null;
|
|
51
51
|
createdAt: Date;
|
|
52
52
|
frozenAt: Date | null;
|
|
53
53
|
contentHash: string | null;
|
|
@@ -56,4 +56,9 @@ export declare class RecipeRevisionResolver {
|
|
|
56
56
|
}>;
|
|
57
57
|
createRecipeDraft(content: RecipeDraftContent, recipeId: string, name: string, baseRevisionId: string, context: ResolverContext): Promise<import("./recipe-revision").RecipeRevision>;
|
|
58
58
|
editRecipeDraft(revisionId: string, editGeneration: number, content: RecipeDraftContent, context: ResolverContext): Promise<import("./recipe-revision").RecipeRevision>;
|
|
59
|
+
/** 비규제 사업장의 저장 한 번 — 초안과 발행이 한 트랜잭션이다. 승인제면 거절되고 아무것도 안 남는다. */
|
|
60
|
+
saveAndPublishRecipe(content: RecipeDraftContent, recipeId: string, name: string, baseRevisionId: string, context: ResolverContext): Promise<{
|
|
61
|
+
revision: import("./recipe-revision").RecipeRevision;
|
|
62
|
+
plan: import("./recipe-revision").RecipePlan;
|
|
63
|
+
}>;
|
|
59
64
|
}
|
|
@@ -22,6 +22,11 @@ let RecipeRevisionResolver = class RecipeRevisionResolver {
|
|
|
22
22
|
const { tx, domain, user } = context.state;
|
|
23
23
|
return (0, recipe_revision_service_1.editRecipeDraft)(tx, domain.id, user.id, revisionId, editGeneration, content);
|
|
24
24
|
}
|
|
25
|
+
/** 비규제 사업장의 저장 한 번 — 초안과 발행이 한 트랜잭션이다. 승인제면 거절되고 아무것도 안 남는다. */
|
|
26
|
+
async saveAndPublishRecipe(content, recipeId, name, baseRevisionId, context) {
|
|
27
|
+
const { tx, domain, user } = context.state;
|
|
28
|
+
return (0, recipe_revision_service_1.saveAndPublishRecipe)(tx, domain.id, user.id, { recipeId, name, baseRevisionId, content });
|
|
29
|
+
}
|
|
25
30
|
};
|
|
26
31
|
exports.RecipeRevisionResolver = RecipeRevisionResolver;
|
|
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
|
], RecipeRevisionResolver.prototype, "editRecipeDraft", 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)('recipeId', () => 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
|
+
], RecipeRevisionResolver.prototype, "saveAndPublishRecipe", null);
|
|
72
90
|
exports.RecipeRevisionResolver = RecipeRevisionResolver = 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":"recipe-revision-resolver.js","sourceRoot":"","sources":["../../../server/service/recipe/recipe-revision-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAAiG;AACjG,iDAAkF;AAClF,2DAAsD;AACtD,
|
|
1
|
+
{"version":3,"file":"recipe-revision-resolver.js","sourceRoot":"","sources":["../../../server/service/recipe/recipe-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 { RecipeDefinition } from './recipe-definition'\nimport { saveAndPublishRecipe, createRecipeDraft, editRecipeDraft, publishUnregulatedRecipe, recipeRevisionOverview } from './recipe-revision-service'\n\nimport { RecipeDraftContent } from './recipe-revision-content'\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class RecipeRevisionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async publishRecipeDraft(@Arg('revisionId') revisionId: string, @Arg('editGeneration', () => Int) editGeneration: number, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return publishUnregulatedRecipe(tx, domain.id, user.id, revisionId, editGeneration)\n }\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async recipeRevisions(@Arg('recipeId') recipeId: string, @Ctx() context: ResolverContext) {\n return recipeRevisionOverview(getRepository(RecipeDefinition).manager, context.state.domain.id, recipeId)\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async createRecipeDraft(\n @Arg('content', () => ScalarObject) content: RecipeDraftContent,\n @Arg('recipeId', () => String, { nullable: true }) recipeId: 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 createRecipeDraft(tx, domain.id, user.id, { recipeId, name, baseRevisionId, content })\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async editRecipeDraft(\n @Arg('revisionId') revisionId: string,\n @Arg('editGeneration', () => Int) editGeneration: number,\n @Arg('content', () => ScalarObject) content: RecipeDraftContent,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return editRecipeDraft(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 saveAndPublishRecipe(\n @Arg('content', () => ScalarObject) content: RecipeDraftContent,\n @Arg('recipeId', () => String, { nullable: true }) recipeId: 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 saveAndPublishRecipe(tx, domain.id, user.id, { recipeId, name, baseRevisionId, content })\n }\n}\n"]}
|
|
@@ -79,6 +79,22 @@ export declare function resolveRecipe(manager: EntityManager, domainId: string,
|
|
|
79
79
|
plan: RecipePlan;
|
|
80
80
|
segment: RecipeSegment;
|
|
81
81
|
}>;
|
|
82
|
+
/**
|
|
83
|
+
* **개정본이 하나도 없는 제조법**마다 지금 서 있는 행을 첫 개정본으로 적는다 — 기준선(2026-09-24).
|
|
84
|
+
* 규칙은 검사 명세의 `standTestSpecificationBaselines` 와 같다. 제조법만의 것 둘:
|
|
85
|
+
*
|
|
86
|
+
* - 옛 행의 줄에는 `lineKey` 가 없다. **자리로 붙인다** — `input-1` · `output-1`. plant 시드가 쓰는 규칙과
|
|
87
|
+
* 같고, 줄의 차례가 뜻을 가지는 모델이라 자리에서 온 키는 내용을 바꾸지 않는다.
|
|
88
|
+
* - 품목 · 공정 · 경로는 이름으로 가리킨다. **쓰기 전에 전부 대조한다** — 줄을 쓰다가 거절되면 반쯤 쓴
|
|
89
|
+
* 개정본이 트랜잭션에 남는다. 없는 것을 가리키는 행은 세우지 않고 까닭을 남긴다.
|
|
90
|
+
*/
|
|
91
|
+
export declare function standRecipeBaselines(manager: EntityManager, domainId: string, at: Date, edition: string): Promise<{
|
|
92
|
+
stood: number;
|
|
93
|
+
unreadable: {
|
|
94
|
+
name: string;
|
|
95
|
+
problem: string;
|
|
96
|
+
}[];
|
|
97
|
+
}>;
|
|
82
98
|
/** Replay reads verify the captured digest too, not only the currently effective schedule. */
|
|
83
99
|
export declare function recipeRevisionForBasis(manager: EntityManager, domainId: string, basis: RecipeBasis): Promise<RecipeRevision>;
|
|
84
100
|
/** Must run inside the transaction that writes the execution result. */
|
|
@@ -127,8 +143,8 @@ export declare function recipeRevisionOverview(manager: EntityManager, domainId:
|
|
|
127
143
|
route: string | null;
|
|
128
144
|
effectiveFrom: Date;
|
|
129
145
|
reason: string;
|
|
130
|
-
creator: import("@things-factory/auth-base").User;
|
|
131
|
-
creatorId: string;
|
|
146
|
+
creator: import("@things-factory/auth-base").User | null;
|
|
147
|
+
creatorId: string | null;
|
|
132
148
|
createdAt: Date;
|
|
133
149
|
frozenAt: Date | null;
|
|
134
150
|
contentHash: string | null;
|
|
@@ -145,3 +161,18 @@ export declare function effectiveRecipe(manager: EntityManager, domainId: string
|
|
|
145
161
|
inputs: import("./recipe-revision-content").RecipeLineContent[];
|
|
146
162
|
outputs: import("./recipe-revision-content").RecipeLineContent[];
|
|
147
163
|
}>;
|
|
164
|
+
/**
|
|
165
|
+
* **비규제 사업장의 「저장」 한 번** — 초안을 만들고 바로 발행한다, 한 트랜잭션에서(ADR-0086 09-22 보탬).
|
|
166
|
+
* 비규제면 사람이 누를 것은 저장 하나다. 두 걸음(초안 저장 → 발행)이 구조상 필연인 것처럼 보이게 하지 않는다.
|
|
167
|
+
* 이 축이 승인제면 발행이 `MASTER_APPROVAL_REQUIRED` 로 거절되고, 같은 트랜잭션이라 초안도 함께 물러난다 —
|
|
168
|
+
* 반쯤 저장된 것이 남지 않는다. 개정본 · 적용계획 · 구간은 두 걸음 때와 똑같이 선다.
|
|
169
|
+
*/
|
|
170
|
+
export declare function saveAndPublishRecipe(manager: EntityManager, domainId: string, actorId: string, asked: {
|
|
171
|
+
recipeId?: string;
|
|
172
|
+
name?: string;
|
|
173
|
+
baseRevisionId?: string;
|
|
174
|
+
content: RecipeDraftContent;
|
|
175
|
+
}): Promise<{
|
|
176
|
+
revision: RecipeRevision;
|
|
177
|
+
plan: RecipePlan;
|
|
178
|
+
}>;
|
|
@@ -11,10 +11,12 @@ exports.bindRecipeApproval = bindRecipeApproval;
|
|
|
11
11
|
exports.publishRecipeRevision = publishRecipeRevision;
|
|
12
12
|
exports.publishUnregulatedRecipe = publishUnregulatedRecipe;
|
|
13
13
|
exports.resolveRecipe = resolveRecipe;
|
|
14
|
+
exports.standRecipeBaselines = standRecipeBaselines;
|
|
14
15
|
exports.recipeRevisionForBasis = recipeRevisionForBasis;
|
|
15
16
|
exports.captureRecipeBasis = captureRecipeBasis;
|
|
16
17
|
exports.recipeRevisionOverview = recipeRevisionOverview;
|
|
17
18
|
exports.effectiveRecipe = effectiveRecipe;
|
|
19
|
+
exports.saveAndPublishRecipe = saveAndPublishRecipe;
|
|
18
20
|
const node_crypto_1 = require("node:crypto");
|
|
19
21
|
const typeorm_1 = require("typeorm");
|
|
20
22
|
const shell_1 = require("@things-factory/shell");
|
|
@@ -499,6 +501,8 @@ async function resolveRecipe(manager, domainId, recipeId, businessTime, knownAt
|
|
|
499
501
|
if (currentLock(manager).lock)
|
|
500
502
|
query.setLock('pessimistic_write');
|
|
501
503
|
const segments = await query.getMany();
|
|
504
|
+
if (!segments.length)
|
|
505
|
+
await refuseBeforeBaseline(manager, domainId, recipeId, at);
|
|
502
506
|
if (segments.length !== 1)
|
|
503
507
|
refuse(segments.length ? 'AMBIGUOUS_EFFECTIVITY' : 'NO_EFFECTIVE_REVISION');
|
|
504
508
|
const selected = await revision(manager, domainId, segments[0].revisionId);
|
|
@@ -506,6 +510,130 @@ async function resolveRecipe(manager, domainId, recipeId, businessTime, knownAt
|
|
|
506
510
|
refuse('REVISION_INTEGRITY_FAILURE');
|
|
507
511
|
return { revision: selected, plan, segment: segments[0] };
|
|
508
512
|
}
|
|
513
|
+
/** 기준선보다 앞선 시각은 모른다고 말한다 — 검사 명세의 같은 자리를 보라(2026-09-24). */
|
|
514
|
+
async function refuseBeforeBaseline(manager, domainId, recipeId, at) {
|
|
515
|
+
const first = await manager.findOneBy(recipe_revision_1.RecipePlan, {
|
|
516
|
+
domain: { id: domainId }, recipe: { id: recipeId }, generation: 1, releaseKind: 'baseline'
|
|
517
|
+
});
|
|
518
|
+
if (!first)
|
|
519
|
+
return;
|
|
520
|
+
const recorded = await manager.findOneBy(recipe_revision_1.RecipeRevision, { domain: { id: domainId }, id: first.publishedRevisionId });
|
|
521
|
+
if (recorded && at < recorded.effectiveFrom)
|
|
522
|
+
throw new shell_1.Refusal('BEFORE_BASELINE', { baselineAt: recorded.effectiveFrom.toISOString() }, `This master was first recorded at ${recorded.effectiveFrom.toISOString()}; nothing is known about it before then.`);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* **개정본이 하나도 없는 제조법**마다 지금 서 있는 행을 첫 개정본으로 적는다 — 기준선(2026-09-24).
|
|
526
|
+
* 규칙은 검사 명세의 `standTestSpecificationBaselines` 와 같다. 제조법만의 것 둘:
|
|
527
|
+
*
|
|
528
|
+
* - 옛 행의 줄에는 `lineKey` 가 없다. **자리로 붙인다** — `input-1` · `output-1`. plant 시드가 쓰는 규칙과
|
|
529
|
+
* 같고, 줄의 차례가 뜻을 가지는 모델이라 자리에서 온 키는 내용을 바꾸지 않는다.
|
|
530
|
+
* - 품목 · 공정 · 경로는 이름으로 가리킨다. **쓰기 전에 전부 대조한다** — 줄을 쓰다가 거절되면 반쯤 쓴
|
|
531
|
+
* 개정본이 트랜잭션에 남는다. 없는 것을 가리키는 행은 세우지 않고 까닭을 남긴다.
|
|
532
|
+
*/
|
|
533
|
+
async function standRecipeBaselines(manager, domainId, at, edition) {
|
|
534
|
+
transaction(manager);
|
|
535
|
+
const roots = await manager
|
|
536
|
+
.createQueryBuilder(recipe_definition_1.RecipeDefinition, 'recipe')
|
|
537
|
+
.leftJoin(recipe_revision_1.RecipeRevision, 'rev', 'rev.recipe_id = recipe.id AND rev.domain_id = recipe.domain_id')
|
|
538
|
+
.where('recipe.domain_id = :domainId', { domainId })
|
|
539
|
+
.andWhere('rev.id IS NULL')
|
|
540
|
+
.getMany();
|
|
541
|
+
const outcome = { stood: 0, unreadable: [] };
|
|
542
|
+
if (!roots.length)
|
|
543
|
+
return outcome;
|
|
544
|
+
const policy = await (0, master_change_policy_1.masterChangePolicy)(manager, domainId, 'recipeDefinition');
|
|
545
|
+
const where = { where: { domain: { id: domainId } } };
|
|
546
|
+
const materials = new Set((await manager.find(material_definition_1.MaterialDefinition, where)).map(one => one.name));
|
|
547
|
+
const operations = new Set((await manager.find(operation_definition_1.OperationDefinition, where)).map(one => one.name));
|
|
548
|
+
const routes = new Map((await manager.find(route_definition_1.RouteDefinition, where)).map(one => [one.name, one]));
|
|
549
|
+
const keyed = (rows, side) => Array.isArray(rows) ? rows.map((row, index) => ({ ...row, lineKey: row?.lineKey ?? `${side}-${index + 1}` })) : rows;
|
|
550
|
+
for (const root of roots) {
|
|
551
|
+
if (root.planGeneration !== 0) {
|
|
552
|
+
outcome.unreadable.push({ name: root.name, problem: 'plan without revision' });
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
let read;
|
|
556
|
+
try {
|
|
557
|
+
read = (0, recipe_revision_content_1.recipeContentValues)({
|
|
558
|
+
materialDefinitionName: root.materialDefinitionName,
|
|
559
|
+
description: root.description ?? null,
|
|
560
|
+
route: root.route ?? null,
|
|
561
|
+
inputs: keyed(root.inputs, 'input'),
|
|
562
|
+
outputs: keyed(root.outputs, 'output')
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
outcome.unreadable.push({ name: root.name, problem: error?.message ?? String(error) });
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
const route = read.route ? routes.get(read.route) : null;
|
|
570
|
+
const missing = [
|
|
571
|
+
...(materials.has(read.materialDefinitionName) ? [] : [`material ${read.materialDefinitionName}`]),
|
|
572
|
+
...(read.route && !route ? [`route ${read.route}`] : []),
|
|
573
|
+
...[...read.inputs, ...read.outputs].filter(line => !materials.has(line.material)).map(line => `material ${line.material}`),
|
|
574
|
+
...read.inputs
|
|
575
|
+
.filter(line => line.operation && (!operations.has(line.operation) || !route?.steps.includes(line.operation)))
|
|
576
|
+
.map(line => `operation ${line.operation} in route ${read.route ?? '(none)'}`)
|
|
577
|
+
];
|
|
578
|
+
if (missing.length) {
|
|
579
|
+
outcome.unreadable.push({ name: root.name, problem: 'points at what this domain does not have: ' + [...new Set(missing)].join(', ') });
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
const validFrom = root.effectiveStart && root.effectiveStart > at ? root.effectiveStart : at;
|
|
583
|
+
const validTo = root.effectiveEnd ?? null;
|
|
584
|
+
const { inputs, outputs, ...header } = read;
|
|
585
|
+
const saved = await manager.save(recipe_revision_1.RecipeRevision, manager.create(recipe_revision_1.RecipeRevision, {
|
|
586
|
+
...header,
|
|
587
|
+
effectiveFrom: validFrom,
|
|
588
|
+
reason: `baseline: the row as it stood, recorded by ${edition}`,
|
|
589
|
+
...await headerReferences(manager, domainId, read),
|
|
590
|
+
policy: { id: policy.id },
|
|
591
|
+
domain: { id: domainId },
|
|
592
|
+
recipe: { id: root.id },
|
|
593
|
+
baseRevision: null,
|
|
594
|
+
baseGeneration: 0,
|
|
595
|
+
revisionNumber: 1,
|
|
596
|
+
editGeneration: 0,
|
|
597
|
+
creator: null,
|
|
598
|
+
createdAt: at,
|
|
599
|
+
frozenAt: at,
|
|
600
|
+
contentHash: null,
|
|
601
|
+
activityInstanceId: null
|
|
602
|
+
}));
|
|
603
|
+
await saveLines(manager, domainId, root.id, saved.id, inputs, outputs);
|
|
604
|
+
const stood = await revision(manager, domainId, saved.id);
|
|
605
|
+
const contentHash = recipeSubmissionHash(stood);
|
|
606
|
+
await manager.update(recipe_revision_1.RecipeRevision, { id: stood.id, domain: { id: domainId } }, { contentHash });
|
|
607
|
+
const plan = await manager.save(recipe_revision_1.RecipePlan, manager.create(recipe_revision_1.RecipePlan, {
|
|
608
|
+
domain: { id: domainId },
|
|
609
|
+
recipe: { id: root.id },
|
|
610
|
+
generation: 1,
|
|
611
|
+
recordedAt: at,
|
|
612
|
+
activityInstanceId: null,
|
|
613
|
+
submissionHash: contentHash,
|
|
614
|
+
approver: null,
|
|
615
|
+
publisher: null,
|
|
616
|
+
releaseKind: 'baseline',
|
|
617
|
+
publishedRevision: { id: stood.id },
|
|
618
|
+
policy: { id: policy.id }
|
|
619
|
+
}));
|
|
620
|
+
if (!validTo || validTo > validFrom) {
|
|
621
|
+
await manager.save(recipe_revision_1.RecipeSegment, manager.create(recipe_revision_1.RecipeSegment, {
|
|
622
|
+
domain: { id: domainId },
|
|
623
|
+
recipe: { id: root.id },
|
|
624
|
+
plan: { id: plan.id },
|
|
625
|
+
revision: { id: stood.id },
|
|
626
|
+
validFrom,
|
|
627
|
+
validTo
|
|
628
|
+
}));
|
|
629
|
+
}
|
|
630
|
+
const advanced = await manager.update(recipe_definition_1.RecipeDefinition, { id: root.id, domain: { id: domainId }, planGeneration: 0 }, { planGeneration: 1 });
|
|
631
|
+
if (advanced.affected !== 1)
|
|
632
|
+
refuse('PLAN_CONFLICT');
|
|
633
|
+
outcome.stood++;
|
|
634
|
+
}
|
|
635
|
+
return outcome;
|
|
636
|
+
}
|
|
509
637
|
/** Replay reads verify the captured digest too, not only the currently effective schedule. */
|
|
510
638
|
async function recipeRevisionForBasis(manager, domainId, basis) {
|
|
511
639
|
if (basis.domainId !== domainId)
|
|
@@ -601,4 +729,15 @@ async function effectiveRecipe(manager, domainId, recipe, at = new Date()) {
|
|
|
601
729
|
effectiveEnd: selected.segment.validTo ?? undefined
|
|
602
730
|
});
|
|
603
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* **비규제 사업장의 「저장」 한 번** — 초안을 만들고 바로 발행한다, 한 트랜잭션에서(ADR-0086 09-22 보탬).
|
|
734
|
+
* 비규제면 사람이 누를 것은 저장 하나다. 두 걸음(초안 저장 → 발행)이 구조상 필연인 것처럼 보이게 하지 않는다.
|
|
735
|
+
* 이 축이 승인제면 발행이 `MASTER_APPROVAL_REQUIRED` 로 거절되고, 같은 트랜잭션이라 초안도 함께 물러난다 —
|
|
736
|
+
* 반쯤 저장된 것이 남지 않는다. 개정본 · 적용계획 · 구간은 두 걸음 때와 똑같이 선다.
|
|
737
|
+
*/
|
|
738
|
+
async function saveAndPublishRecipe(manager, domainId, actorId, asked) {
|
|
739
|
+
const draft = await createRecipeDraft(manager, domainId, actorId, asked);
|
|
740
|
+
const plan = await publishUnregulatedRecipe(manager, domainId, actorId, draft.id, draft.editGeneration);
|
|
741
|
+
return { revision: await revision(manager, domainId, draft.id, false), plan };
|
|
742
|
+
}
|
|
604
743
|
//# sourceMappingURL=recipe-revision-service.js.map
|