forked from DistinctCodes/AssetsUp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathasset-lifecycle.service.ts
More file actions
125 lines (116 loc) · 3.19 KB
/
Copy pathasset-lifecycle.service.ts
File metadata and controls
125 lines (116 loc) · 3.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { Injectable, BadRequestException } from '@nestjs/common';
export enum AssetStatus {
AVAILABLE = 'AVAILABLE',
ASSIGNED = 'ASSIGNED',
IN_MAINTENANCE = 'IN_MAINTENANCE',
IN_TRANSIT = 'IN_TRANSIT',
RETIRED = 'RETIRED',
DISPOSED = 'DISPOSED',
LOST = 'LOST',
}
/**
* Status values accepted on the wire: the canonical statuses plus the aliases
* the frontend AssetStatus enum uses (ACTIVE, MAINTENANCE).
*/
export enum AssetStatusInput {
AVAILABLE = 'AVAILABLE',
ACTIVE = 'ACTIVE',
ASSIGNED = 'ASSIGNED',
IN_MAINTENANCE = 'IN_MAINTENANCE',
MAINTENANCE = 'MAINTENANCE',
IN_TRANSIT = 'IN_TRANSIT',
RETIRED = 'RETIRED',
DISPOSED = 'DISPOSED',
LOST = 'LOST',
}
const STATUS_ALIASES: Record<string, AssetStatus> = {
ACTIVE: AssetStatus.AVAILABLE,
MAINTENANCE: AssetStatus.IN_MAINTENANCE,
};
const ALLOWED_TRANSITIONS: Record<AssetStatus, AssetStatus[]> = {
[AssetStatus.AVAILABLE]: [
AssetStatus.ASSIGNED,
AssetStatus.IN_MAINTENANCE,
AssetStatus.IN_TRANSIT,
AssetStatus.RETIRED,
AssetStatus.LOST,
],
[AssetStatus.ASSIGNED]: [
AssetStatus.AVAILABLE,
AssetStatus.IN_MAINTENANCE,
AssetStatus.IN_TRANSIT,
AssetStatus.RETIRED,
AssetStatus.LOST,
],
[AssetStatus.IN_MAINTENANCE]: [
AssetStatus.AVAILABLE,
AssetStatus.ASSIGNED,
AssetStatus.RETIRED,
],
[AssetStatus.IN_TRANSIT]: [
AssetStatus.AVAILABLE,
AssetStatus.ASSIGNED,
AssetStatus.LOST,
],
[AssetStatus.RETIRED]: [AssetStatus.DISPOSED],
[AssetStatus.DISPOSED]: [],
[AssetStatus.LOST]: [
AssetStatus.AVAILABLE,
AssetStatus.RETIRED,
AssetStatus.DISPOSED,
],
};
@Injectable()
export class AssetLifecycleService {
private history = new Map<string, any[]>();
/**
* Resolve a wire status value to its canonical AssetStatus, rejecting anything
* outside the enum with a 400.
*/
normalizeStatus(status: string): AssetStatus {
if (!status) {
throw new BadRequestException('status is required');
}
const upper = String(status).toUpperCase();
const resolved = STATUS_ALIASES[upper] ?? (upper as AssetStatus);
if (!Object.values(AssetStatus).includes(resolved)) {
throw new BadRequestException(
`Invalid asset status "${status}". Allowed values: ${Object.values(AssetStatusInput).join(', ')}`,
);
}
return resolved;
}
validateTransition(fromStatus: AssetStatus, toStatus: AssetStatus) {
if (fromStatus === toStatus) return true;
const allowed = ALLOWED_TRANSITIONS[fromStatus] || [];
if (!allowed.includes(toStatus)) {
throw new BadRequestException(
`Cannot transition asset status from ${fromStatus} to ${toStatus}`,
);
}
return true;
}
recordHistory(
assetId: string,
event: {
eventType: string;
actorUserId: string;
note?: string;
fieldChanges?: any;
},
) {
const list = this.history.get(assetId) || [];
const entry = {
id: `h_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
assetId,
...event,
timestamp: new Date(),
};
list.unshift(entry);
this.history.set(assetId, list);
return entry;
}
getHistory(assetId: string) {
return this.history.get(assetId) || [];
}
}