@acorex-platform/module-software-management-client 0.0.46
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/fesm2022/acorex-platform-module-software-management-client-accept-software-suggestion.command-DVcM5TTv.mjs +83 -0
- package/fesm2022/acorex-platform-module-software-management-client-accept-software-suggestion.command-DVcM5TTv.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-complete-software-phase.activity-C56HhK20.mjs +182 -0
- package/fesm2022/acorex-platform-module-software-management-client-complete-software-phase.activity-C56HhK20.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-contracts.mjs +1196 -0
- package/fesm2022/acorex-platform-module-software-management-client-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-discovery-kickoff.commands-gB0_htfF.mjs +162 -0
- package/fesm2022/acorex-platform-module-software-management-client-discovery-kickoff.commands-gB0_htfF.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-discovery-report.commands-D2ricLpq.mjs +278 -0
- package/fesm2022/acorex-platform-module-software-management-client-discovery-report.commands-D2ricLpq.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-introduction-exit-message.util-DShFM8MY.mjs +71 -0
- package/fesm2022/acorex-platform-module-software-management-client-introduction-exit-message.util-DShFM8MY.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-introduction-task.activities-BOsJbeJe.mjs +158 -0
- package/fesm2022/acorex-platform-module-software-management-client-introduction-task.activities-BOsJbeJe.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-software-command.util-BsoKQCX5.mjs +9 -0
- package/fesm2022/acorex-platform-module-software-management-client-software-command.util-BsoKQCX5.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client-start-software-definition-flow.command-B0YCgv2V.mjs +124 -0
- package/fesm2022/acorex-platform-module-software-management-client-start-software-definition-flow.command-B0YCgv2V.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-software-management-client.mjs +260 -0
- package/fesm2022/acorex-platform-module-software-management-client.mjs.map +1 -0
- package/package.json +28 -0
- package/types/acorex-platform-module-software-management-client-contracts.d.ts +547 -0
- package/types/acorex-platform-module-software-management-client.d.ts +9 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { AXToastService } from '@acorex/components/toast';
|
|
2
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
3
|
+
import { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';
|
|
4
|
+
import { resolveEntityActionRef } from '@acorex-platform/framework-shared/runtime';
|
|
5
|
+
import { RootConfig } from '@acorex-platform/module-software-management-client/contracts';
|
|
6
|
+
import * as i0 from '@angular/core';
|
|
7
|
+
import { inject, Injectable } from '@angular/core';
|
|
8
|
+
import { r as resolveSoftwareText } from './acorex-platform-module-software-management-client-software-command.util-BsoKQCX5.mjs';
|
|
9
|
+
|
|
10
|
+
//#region ---- Imports ----
|
|
11
|
+
//#endregion
|
|
12
|
+
class AcceptSoftwareSuggestionCommand {
|
|
13
|
+
constructor() {
|
|
14
|
+
//#region ---- Services & Dependencies ----
|
|
15
|
+
this.entityService = inject(AXPEntityService);
|
|
16
|
+
this.toastService = inject(AXToastService);
|
|
17
|
+
this.translationService = inject(AXTranslationService);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region ---- Command Implementation ----
|
|
21
|
+
async execute(input) {
|
|
22
|
+
const resolved = resolveEntityActionRef(input);
|
|
23
|
+
const refType = resolveSoftwareText(resolved?.refType);
|
|
24
|
+
const refId = resolveSoftwareText(resolved?.refId);
|
|
25
|
+
if (!refType || !refId) {
|
|
26
|
+
return {
|
|
27
|
+
success: false,
|
|
28
|
+
message: {
|
|
29
|
+
text: await this.translationService.translateAsync('@software-management:messages.accept-suggestion.ref-required'),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const [moduleName, entityName] = refType.split('.');
|
|
34
|
+
if (moduleName !== RootConfig.module.name || !entityName) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
message: {
|
|
38
|
+
text: await this.translationService.translateAsync('@software-management:messages.accept-suggestion.unsupported'),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const record = (await this.entityService
|
|
43
|
+
.withEntity(moduleName, entityName)
|
|
44
|
+
.data()
|
|
45
|
+
.byKey(refId));
|
|
46
|
+
if (!record) {
|
|
47
|
+
return { success: false, message: { text: 'Record not found' } };
|
|
48
|
+
}
|
|
49
|
+
if (record.origin !== 'suggestion') {
|
|
50
|
+
this.toastService.show({
|
|
51
|
+
color: 'warning',
|
|
52
|
+
title: await this.translationService.translateAsync('@general:messages.generic.warning.title'),
|
|
53
|
+
content: await this.translationService.translateAsync('@software-management:messages.accept-suggestion.not-suggestion'),
|
|
54
|
+
closeButton: true,
|
|
55
|
+
timeOut: 5000,
|
|
56
|
+
});
|
|
57
|
+
return { success: false };
|
|
58
|
+
}
|
|
59
|
+
await this.entityService.withEntity(moduleName, entityName).data().update(refId, {
|
|
60
|
+
origin: 'approved-fact',
|
|
61
|
+
acceptedAt: new Date().toISOString(),
|
|
62
|
+
needsReview: false,
|
|
63
|
+
reviewReason: '',
|
|
64
|
+
});
|
|
65
|
+
this.toastService.show({
|
|
66
|
+
color: 'success',
|
|
67
|
+
title: await this.translationService.translateAsync('@general:messages.generic.success.title'),
|
|
68
|
+
content: await this.translationService.translateAsync('@software-management:messages.accept-suggestion.accepted'),
|
|
69
|
+
closeButton: true,
|
|
70
|
+
timeOut: 4000,
|
|
71
|
+
});
|
|
72
|
+
return { success: true, kind: 'object', data: undefined };
|
|
73
|
+
}
|
|
74
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AcceptSoftwareSuggestionCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
75
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AcceptSoftwareSuggestionCommand, providedIn: 'root' }); }
|
|
76
|
+
}
|
|
77
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AcceptSoftwareSuggestionCommand, decorators: [{
|
|
78
|
+
type: Injectable,
|
|
79
|
+
args: [{ providedIn: 'root' }]
|
|
80
|
+
}] });
|
|
81
|
+
|
|
82
|
+
export { AcceptSoftwareSuggestionCommand };
|
|
83
|
+
//# sourceMappingURL=acorex-platform-module-software-management-client-accept-software-suggestion.command-DVcM5TTv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-platform-module-software-management-client-accept-software-suggestion.command-DVcM5TTv.mjs","sources":["../../../../libs/modules/software-management/client/src/lib/commands/accept-software-suggestion.command.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { AXToastService } from '@acorex/components/toast';\nimport { AXTranslationService } from '@acorex/core/translation';\nimport { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';\nimport { AXPCommand, resolveEntityActionRef, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport { RootConfig } from '@acorex-platform/module-software-management-client/contracts';\nimport { inject, Injectable } from '@angular/core';\n\nimport { resolveSoftwareText } from './software-command.util';\n\n//#endregion\n\n@Injectable({ providedIn: 'root' })\nexport class AcceptSoftwareSuggestionCommand implements AXPCommand<unknown, void> {\n //#region ---- Services & Dependencies ----\n\n private readonly entityService = inject(AXPEntityService);\n private readonly toastService = inject(AXToastService);\n private readonly translationService = inject(AXTranslationService);\n\n //#endregion\n\n //#region ---- Command Implementation ----\n\n async execute(input: unknown): AXPCommandExecuteResultPromise<void> {\n const resolved = resolveEntityActionRef(input);\n const refType = resolveSoftwareText(resolved?.refType);\n const refId = resolveSoftwareText(resolved?.refId);\n if (!refType || !refId) {\n return {\n success: false,\n message: {\n text: await this.translationService.translateAsync(\n '@software-management:messages.accept-suggestion.ref-required',\n ),\n },\n };\n }\n\n const [moduleName, entityName] = refType.split('.');\n if (moduleName !== RootConfig.module.name || !entityName) {\n return {\n success: false,\n message: {\n text: await this.translationService.translateAsync(\n '@software-management:messages.accept-suggestion.unsupported',\n ),\n },\n };\n }\n\n const record = (await this.entityService\n .withEntity(moduleName, entityName)\n .data()\n .byKey(refId)) as { origin?: string } | null;\n\n if (!record) {\n return { success: false, message: { text: 'Record not found' } };\n }\n\n if (record.origin !== 'suggestion') {\n this.toastService.show({\n color: 'warning',\n title: await this.translationService.translateAsync('@general:messages.generic.warning.title'),\n content: await this.translationService.translateAsync(\n '@software-management:messages.accept-suggestion.not-suggestion',\n ),\n closeButton: true,\n timeOut: 5000,\n });\n return { success: false };\n }\n\n await this.entityService.withEntity(moduleName, entityName).data().update(refId, {\n origin: 'approved-fact',\n acceptedAt: new Date().toISOString(),\n needsReview: false,\n reviewReason: '',\n } as never);\n\n this.toastService.show({\n color: 'success',\n title: await this.translationService.translateAsync('@general:messages.generic.success.title'),\n content: await this.translationService.translateAsync(\n '@software-management:messages.accept-suggestion.accepted',\n ),\n closeButton: true,\n timeOut: 4000,\n });\n\n return { success: true, kind: 'object', data: undefined };\n }\n\n //#endregion\n}\n\n//#endregion\n"],"names":[],"mappings":";;;;;;;;;AAAA;AAWA;MAGa,+BAA+B,CAAA;AAD5C,IAAA,WAAA,GAAA;;AAImB,QAAA,IAAA,CAAA,aAAa,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACxC,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC;AACrC,QAAA,IAAA,CAAA,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC;AA4EnE,IAAA;;;IAtEC,MAAM,OAAO,CAAC,KAAc,EAAA;AAC1B,QAAA,MAAM,QAAQ,GAAG,sBAAsB,CAAC,KAAK,CAAC;QAC9C,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC;QACtD,MAAM,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC;AAClD,QAAA,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE;YACtB,OAAO;AACL,gBAAA,OAAO,EAAE,KAAK;AACd,gBAAA,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAChD,8DAA8D,CAC/D;AACF,iBAAA;aACF;QACH;AAEA,QAAA,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;QACnD,IAAI,UAAU,KAAK,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE;YACxD,OAAO;AACL,gBAAA,OAAO,EAAE,KAAK;AACd,gBAAA,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAChD,6DAA6D,CAC9D;AACF,iBAAA;aACF;QACH;AAEA,QAAA,MAAM,MAAM,IAAI,MAAM,IAAI,CAAC;AACxB,aAAA,UAAU,CAAC,UAAU,EAAE,UAAU;AACjC,aAAA,IAAI;AACJ,aAAA,KAAK,CAAC,KAAK,CAAC,CAA+B;QAE9C,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE;QAClE;AAEA,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY,EAAE;AAClC,YAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;AACrB,gBAAA,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAAC,yCAAyC,CAAC;gBAC9F,OAAO,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CACnD,gEAAgE,CACjE;AACD,gBAAA,WAAW,EAAE,IAAI;AACjB,gBAAA,OAAO,EAAE,IAAI;AACd,aAAA,CAAC;AACF,YAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;QAC3B;AAEA,QAAA,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;AAC/E,YAAA,MAAM,EAAE,eAAe;AACvB,YAAA,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;AACpC,YAAA,WAAW,EAAE,KAAK;AAClB,YAAA,YAAY,EAAE,EAAE;AACR,SAAA,CAAC;AAEX,QAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;AACrB,YAAA,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAAC,yCAAyC,CAAC;YAC9F,OAAO,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CACnD,0DAA0D,CAC3D;AACD,YAAA,WAAW,EAAE,IAAI;AACjB,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC;AAEF,QAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;IAC3D;8GA9EW,+BAA+B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAA/B,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,+BAA+B,cADlB,MAAM,EAAA,CAAA,CAAA;;2FACnB,+BAA+B,EAAA,UAAA,EAAA,CAAA;kBAD3C,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
2
|
+
import { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';
|
|
3
|
+
import { resolveEntityActionRef } from '@acorex-platform/framework-shared/runtime';
|
|
4
|
+
import { RootConfig, canCompletePhase, nextSoftwarePhase, kickoffText, kickoffPlainTextFromVersion, discoveryReportFingerprint } from '@acorex-platform/module-software-management-client/contracts';
|
|
5
|
+
import * as i0 from '@angular/core';
|
|
6
|
+
import { inject, Injectable } from '@angular/core';
|
|
7
|
+
import { r as resolveSoftwareText } from './acorex-platform-module-software-management-client-software-command.util-BsoKQCX5.mjs';
|
|
8
|
+
import { l as loadIntroductionKickoffCheck, t as translateIntroductionExit } from './acorex-platform-module-software-management-client-introduction-exit-message.util-DShFM8MY.mjs';
|
|
9
|
+
|
|
10
|
+
//#region ---- Imports ----
|
|
11
|
+
//#endregion
|
|
12
|
+
class CompleteSoftwarePhaseActivity {
|
|
13
|
+
constructor() {
|
|
14
|
+
//#region ---- Services & Dependencies ----
|
|
15
|
+
this.entityService = inject(AXPEntityService);
|
|
16
|
+
this.i18n = inject(AXTranslationService);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region ---- Command Implementation ----
|
|
20
|
+
async execute(input) {
|
|
21
|
+
const softwareId = await this.resolveSoftwareId(input);
|
|
22
|
+
if (!softwareId) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
message: { text: 'softwareId is required' },
|
|
26
|
+
metadata: { outcome: 'Failed' },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const software = (await this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.software.name).data().byKey(softwareId));
|
|
30
|
+
if (!software) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
message: { text: 'Software not found' },
|
|
34
|
+
metadata: { outcome: 'NotFound' },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const phase = (resolveSoftwareText(input?.phase) || resolveSoftwareText(software.currentPhase) || 'introduction');
|
|
38
|
+
const kickoff = await loadIntroductionKickoffCheck(this.entityService, software);
|
|
39
|
+
const discovery = phase === 'discovery' ? await this.loadDiscoveryCheck(software) : undefined;
|
|
40
|
+
const gate = canCompletePhase(phase, software, kickoff, discovery);
|
|
41
|
+
if (!gate.ok) {
|
|
42
|
+
const text = phase === 'introduction'
|
|
43
|
+
? await translateIntroductionExit(this.i18n, gate)
|
|
44
|
+
: gate.message.startsWith('@')
|
|
45
|
+
? await this.i18n.translateAsync(gate.message)
|
|
46
|
+
: gate.message;
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
message: { text, type: 'warning' },
|
|
50
|
+
metadata: { outcome: 'Failed', currentPhase: software.currentPhase },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const alreadyAdvanced = phase === 'introduction' ? Boolean(software.introductionCompletedAt) && software.currentPhase !== 'introduction' : software.currentPhase !== phase;
|
|
54
|
+
if (alreadyAdvanced) {
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
kind: 'object',
|
|
58
|
+
data: { valid: true, currentPhase: software.currentPhase },
|
|
59
|
+
metadata: { outcome: 'Done' },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (phase === 'discovery' && gate.advance === false) {
|
|
63
|
+
return {
|
|
64
|
+
success: true,
|
|
65
|
+
kind: 'object',
|
|
66
|
+
data: { valid: true, currentPhase: software.currentPhase },
|
|
67
|
+
message: { text: gate.message },
|
|
68
|
+
metadata: { outcome: 'Done' },
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const nextPhase = nextSoftwarePhase(phase);
|
|
72
|
+
const now = new Date().toISOString();
|
|
73
|
+
const patch = {
|
|
74
|
+
currentPhase: nextPhase ?? phase,
|
|
75
|
+
};
|
|
76
|
+
if (phase === 'introduction') {
|
|
77
|
+
patch['introductionCompletedAt'] = now;
|
|
78
|
+
}
|
|
79
|
+
if (!nextPhase) {
|
|
80
|
+
patch['definitionCompletedAt'] = now;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
await this.entityService
|
|
84
|
+
.withEntity(RootConfig.module.name, RootConfig.entities.software.name)
|
|
85
|
+
.data()
|
|
86
|
+
.update(softwareId, patch);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const raw = error instanceof Error ? error.message : gate.message;
|
|
90
|
+
const text = raw.startsWith('@') ? await this.i18n.translateAsync(raw) : raw;
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
message: { text: text || gate.message, type: 'warning' },
|
|
94
|
+
metadata: { outcome: 'Failed', currentPhase: software.currentPhase },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
kind: 'object',
|
|
100
|
+
data: { valid: true, currentPhase: String(patch['currentPhase']) },
|
|
101
|
+
metadata: { outcome: 'Done' },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region ---- Helpers ----
|
|
106
|
+
async resolveSoftwareId(input) {
|
|
107
|
+
const fromInput = resolveSoftwareText(input?.softwareId);
|
|
108
|
+
if (fromInput) {
|
|
109
|
+
return fromInput;
|
|
110
|
+
}
|
|
111
|
+
const resolved = resolveEntityActionRef(input);
|
|
112
|
+
return resolveSoftwareText(resolved?.refId);
|
|
113
|
+
}
|
|
114
|
+
async loadDiscoveryCheck(software) {
|
|
115
|
+
const kickoffVersionId = kickoffText(software.kickoffApprovedVersionId);
|
|
116
|
+
let kickoffPlainText = '';
|
|
117
|
+
if (kickoffVersionId) {
|
|
118
|
+
try {
|
|
119
|
+
const version = (await this.entityService.withEntity('DocumentManagement', 'DocumentVersion').data().byKey(kickoffVersionId));
|
|
120
|
+
kickoffPlainText = kickoffPlainTextFromVersion(version);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
kickoffPlainText = '';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
let findings = [];
|
|
127
|
+
try {
|
|
128
|
+
const page = await this.entityService
|
|
129
|
+
.withEntity(RootConfig.module.name, RootConfig.entities.discoveryItem.name)
|
|
130
|
+
.data()
|
|
131
|
+
.query({
|
|
132
|
+
skip: 0,
|
|
133
|
+
take: 500,
|
|
134
|
+
filter: { field: 'softwareId', operator: { type: 'equal' }, value: software.id },
|
|
135
|
+
});
|
|
136
|
+
findings = (page.items ?? []);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
findings = [];
|
|
140
|
+
}
|
|
141
|
+
const documentId = kickoffText(software.discoveryReportDocumentId);
|
|
142
|
+
let statusId = '';
|
|
143
|
+
let hostVersionId = '';
|
|
144
|
+
if (documentId) {
|
|
145
|
+
try {
|
|
146
|
+
const document = (await this.entityService.withEntity('DocumentManagement', 'Document').data().byKey(documentId));
|
|
147
|
+
statusId = kickoffText(document?.statusId);
|
|
148
|
+
hostVersionId = kickoffText(document?.versionId);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
statusId = '';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
currentPhase: software.currentPhase,
|
|
156
|
+
decision: software.discoveryDecision,
|
|
157
|
+
rationale: software.discoveryRationale,
|
|
158
|
+
kickoffApprovedVersionId: kickoffVersionId,
|
|
159
|
+
findings,
|
|
160
|
+
statusId,
|
|
161
|
+
hostVersionId,
|
|
162
|
+
approvedVersionId: software.discoveryApprovedVersionId,
|
|
163
|
+
approvedFingerprint: software.discoveryApprovedFingerprint,
|
|
164
|
+
reportFingerprint: discoveryReportFingerprint({
|
|
165
|
+
kickoffVersionId,
|
|
166
|
+
kickoffPlainText,
|
|
167
|
+
decision: kickoffText(software.discoveryDecision),
|
|
168
|
+
rationale: kickoffText(software.discoveryRationale),
|
|
169
|
+
findings,
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CompleteSoftwarePhaseActivity, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
174
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CompleteSoftwarePhaseActivity, providedIn: 'root' }); }
|
|
175
|
+
}
|
|
176
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CompleteSoftwarePhaseActivity, decorators: [{
|
|
177
|
+
type: Injectable,
|
|
178
|
+
args: [{ providedIn: 'root' }]
|
|
179
|
+
}] });
|
|
180
|
+
|
|
181
|
+
export { CompleteSoftwarePhaseActivity };
|
|
182
|
+
//# sourceMappingURL=acorex-platform-module-software-management-client-complete-software-phase.activity-C56HhK20.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acorex-platform-module-software-management-client-complete-software-phase.activity-C56HhK20.mjs","sources":["../../../../libs/modules/software-management/client/src/lib/workflows/activities/complete-software-phase.activity.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { AXTranslationService } from '@acorex/core/translation';\nimport { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';\nimport { AXPCommand, resolveEntityActionRef, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport {\n canCompletePhase,\n discoveryReportFingerprint,\n kickoffPlainTextFromVersion,\n kickoffText,\n nextSoftwarePhase,\n RootConfig,\n type AXMSoftwareManagementSoftwareEntityModel,\n type DiscoveryFindingRecord,\n type SoftwarePhase,\n} from '@acorex-platform/module-software-management-client/contracts';\nimport { inject, Injectable } from '@angular/core';\n\nimport { resolveSoftwareText } from '../../commands/software-command.util';\nimport { loadIntroductionKickoffCheck } from './load-introduction-kickoff-check';\nimport { translateIntroductionExit } from './introduction-exit-message.util';\n\n//#endregion\n\n//#region ---- Types ----\n\nexport interface CompleteSoftwarePhaseInput {\n softwareId?: string;\n phase?: SoftwarePhase;\n}\n\nexport interface CompleteSoftwarePhaseOutput {\n valid: boolean;\n currentPhase?: string;\n}\n\n//#endregion\n\n@Injectable({ providedIn: 'root' })\nexport class CompleteSoftwarePhaseActivity implements AXPCommand<CompleteSoftwarePhaseInput, CompleteSoftwarePhaseOutput> {\n //#region ---- Services & Dependencies ----\n\n private readonly entityService = inject(AXPEntityService);\n private readonly i18n = inject(AXTranslationService);\n\n //#endregion\n\n //#region ---- Command Implementation ----\n\n async execute(input: CompleteSoftwarePhaseInput): AXPCommandExecuteResultPromise<CompleteSoftwarePhaseOutput> {\n const softwareId = await this.resolveSoftwareId(input);\n if (!softwareId) {\n return {\n success: false,\n message: { text: 'softwareId is required' },\n metadata: { outcome: 'Failed' },\n };\n }\n\n const software = (await this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.software.name).data().byKey(softwareId)) as AXMSoftwareManagementSoftwareEntityModel | null;\n\n if (!software) {\n return {\n success: false,\n message: { text: 'Software not found' },\n metadata: { outcome: 'NotFound' },\n };\n }\n\n const phase = (resolveSoftwareText(input?.phase) || resolveSoftwareText(software.currentPhase) || 'introduction') as SoftwarePhase;\n\n const kickoff = await loadIntroductionKickoffCheck(this.entityService, software);\n const discovery = phase === 'discovery' ? await this.loadDiscoveryCheck(software) : undefined;\n const gate = canCompletePhase(phase, software, kickoff, discovery);\n if (!gate.ok) {\n const text =\n phase === 'introduction'\n ? await translateIntroductionExit(this.i18n, gate)\n : gate.message.startsWith('@')\n ? await this.i18n.translateAsync(gate.message)\n : gate.message;\n return {\n success: false,\n message: { text, type: 'warning' },\n metadata: { outcome: 'Failed', currentPhase: software.currentPhase },\n };\n }\n\n const alreadyAdvanced = phase === 'introduction' ? Boolean(software.introductionCompletedAt) && software.currentPhase !== 'introduction' : software.currentPhase !== phase;\n\n if (alreadyAdvanced) {\n return {\n success: true,\n kind: 'object',\n data: { valid: true, currentPhase: software.currentPhase },\n metadata: { outcome: 'Done' },\n };\n }\n\n if (phase === 'discovery' && gate.advance === false) {\n return {\n success: true,\n kind: 'object',\n data: { valid: true, currentPhase: software.currentPhase },\n message: { text: gate.message },\n metadata: { outcome: 'Done' },\n };\n }\n\n const nextPhase = nextSoftwarePhase(phase);\n const now = new Date().toISOString();\n const patch: Record<string, unknown> = {\n currentPhase: nextPhase ?? phase,\n };\n\n if (phase === 'introduction') {\n patch['introductionCompletedAt'] = now;\n }\n\n if (!nextPhase) {\n patch['definitionCompletedAt'] = now;\n }\n\n try {\n await this.entityService\n .withEntity(RootConfig.module.name, RootConfig.entities.software.name)\n .data()\n .update(softwareId, patch as never);\n } catch (error) {\n const raw = error instanceof Error ? error.message : gate.message;\n const text = raw.startsWith('@') ? await this.i18n.translateAsync(raw) : raw;\n return {\n success: false,\n message: { text: text || gate.message, type: 'warning' },\n metadata: { outcome: 'Failed', currentPhase: software.currentPhase },\n };\n }\n\n return {\n success: true,\n kind: 'object',\n data: { valid: true, currentPhase: String(patch['currentPhase']) },\n metadata: { outcome: 'Done' },\n };\n }\n\n //#endregion\n\n //#region ---- Helpers ----\n\n private async resolveSoftwareId(input: CompleteSoftwarePhaseInput): Promise<string> {\n const fromInput = resolveSoftwareText(input?.softwareId);\n if (fromInput) {\n return fromInput;\n }\n\n const resolved = resolveEntityActionRef(input);\n return resolveSoftwareText(resolved?.refId);\n }\n\n private async loadDiscoveryCheck(software: AXMSoftwareManagementSoftwareEntityModel) {\n const kickoffVersionId = kickoffText(software.kickoffApprovedVersionId);\n let kickoffPlainText = '';\n if (kickoffVersionId) {\n try {\n const version = (await this.entityService.withEntity('DocumentManagement', 'DocumentVersion').data().byKey(kickoffVersionId)) as { metaDataForm?: unknown } | null;\n kickoffPlainText = kickoffPlainTextFromVersion(version);\n } catch {\n kickoffPlainText = '';\n }\n }\n\n let findings: DiscoveryFindingRecord[] = [];\n try {\n const page = await this.entityService\n .withEntity(RootConfig.module.name, RootConfig.entities.discoveryItem.name)\n .data()\n .query({\n skip: 0,\n take: 500,\n filter: { field: 'softwareId', operator: { type: 'equal' }, value: software.id },\n });\n findings = (page.items ?? []) as DiscoveryFindingRecord[];\n } catch {\n findings = [];\n }\n\n const documentId = kickoffText(software.discoveryReportDocumentId);\n let statusId = '';\n let hostVersionId = '';\n if (documentId) {\n try {\n const document = (await this.entityService.withEntity('DocumentManagement', 'Document').data().byKey(documentId)) as { statusId?: unknown; versionId?: unknown } | null;\n statusId = kickoffText(document?.statusId);\n hostVersionId = kickoffText(document?.versionId);\n } catch {\n statusId = '';\n }\n }\n\n return {\n currentPhase: software.currentPhase,\n decision: software.discoveryDecision,\n rationale: software.discoveryRationale,\n kickoffApprovedVersionId: kickoffVersionId,\n findings,\n statusId,\n hostVersionId,\n approvedVersionId: software.discoveryApprovedVersionId,\n approvedFingerprint: software.discoveryApprovedFingerprint,\n reportFingerprint: discoveryReportFingerprint({\n kickoffVersionId,\n kickoffPlainText,\n decision: kickoffText(software.discoveryDecision),\n rationale: kickoffText(software.discoveryRationale),\n findings,\n }),\n };\n }\n\n //#endregion\n}\n\n//#endregion\n"],"names":[],"mappings":"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"}
|