law-common 14.0.1-beta.4 → 14.0.1-beta.6
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/src/api/interface/timesheet.entity.response.interface.d.ts +3 -2
- package/dist/src/entities/flow-configs/base-flow-config.factory.d.ts +4 -2
- package/dist/src/entities/flow-configs/base-flow-config.factory.js +1 -1
- package/dist/src/entities/flow-configs/billing_transaction_flow.config.d.ts +2 -1
- package/dist/src/entities/flow-configs/flow-config.type.d.ts +0 -7
- package/dist/src/entities/flow-configs/timesheet_flow.config.js +52 -79
- package/dist/src/entities/model/timesheet.entity.model.d.ts +14 -0
- package/dist/src/entities/model/timesheet.entity.model.js +146 -0
- package/package.json +1 -1
|
@@ -17,8 +17,9 @@ export type ITimesheetEntityGet = {
|
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* LW-828: this used to re-declare the flow-config shape by hand. It is now an alias of the shared
|
|
20
|
-
* `FlowConfig`, whose action config carries the
|
|
21
|
-
*
|
|
20
|
+
* `FlowConfig`, whose action config carries the `isApplicable` runtime guard this entity relies on
|
|
21
|
+
* (LW-830 collapse — the previous `validActionPastThreshold` boolean is commented out in
|
|
22
|
+
* `IFlowConfigActionConfig`). Kept as a named type so every existing consumer keeps compiling unchanged.
|
|
22
23
|
*/
|
|
23
24
|
export type ITimesheetFlowConfig = FlowConfig<TimesheetStatusEnum, TimesheetActionEnum, ITimesheetFlowContextData>;
|
|
24
25
|
export type ITimesheetFlowContextData = {
|
|
@@ -114,19 +114,21 @@ export type ActionMap<A extends string, S extends string> = {
|
|
|
114
114
|
*
|
|
115
115
|
* @property isApplicable runtime guard evaluated against the same context as `next()`
|
|
116
116
|
* @property validActionPastThreshold whether the action survives a consumer's data-entry threshold
|
|
117
|
+
* (commented out below since the LW-830 collapse — see note)
|
|
118
|
+
* @property isAvailable unified runtime guard introduced by LW-830; preferred over
|
|
119
|
+
* `isApplicable` when both are present (commented out below
|
|
120
|
+
* since the LW-830 collapse — see note)
|
|
117
121
|
* @property description human-readable copy for this transition. Already part of
|
|
118
122
|
* `IFlowConfigActionConfig` and rendered by the entity-flow
|
|
119
123
|
* component, so a converted config must be able to emit it.
|
|
120
124
|
*/
|
|
121
125
|
export type ActionConfigOptions<CTX> = {
|
|
122
126
|
isApplicable?: (context: CTX) => boolean;
|
|
123
|
-
validActionPastThreshold?: boolean;
|
|
124
127
|
description?: string;
|
|
125
128
|
};
|
|
126
129
|
export declare function createFlowConfigFactory<CTX = never>(): <A extends string, S extends string, AM extends ActionMap<A, S>>(actionMap: AM) => {
|
|
127
130
|
createActionConfig: <Act extends A, Subset extends { [key in A]: AM[key]["reachableStatues"]; }[Act][number] = { [key in A]: AM[key]["reachableStatues"]; }[Act][number]>(action: Act, nextStatus: Subset | ((context: CTX) => Subset), overridePermissions?: string[], options?: ActionConfigOptions<CTX>) => {
|
|
128
131
|
description?: string | undefined;
|
|
129
|
-
validActionPastThreshold?: boolean | undefined;
|
|
130
132
|
isApplicable?: ((context: CTX) => boolean) | undefined;
|
|
131
133
|
permissions: string[];
|
|
132
134
|
next: (context: CTX) => Subset;
|
|
@@ -99,7 +99,7 @@ function createFlowConfigFactory() {
|
|
|
99
99
|
* (currentStatus, action) pair. Must be a subtype of
|
|
100
100
|
* `StatusForAction<Act>`.
|
|
101
101
|
*/
|
|
102
|
-
const createActionConfig = (action, nextStatus, overridePermissions, options) => (Object.assign(Object.assign(
|
|
102
|
+
const createActionConfig = (action, nextStatus, overridePermissions, options) => (Object.assign(Object.assign({ permissions: overridePermissions !== null && overridePermissions !== void 0 ? overridePermissions : actionMap[action].permissions, next: createTransition(nextStatus) }, ((options === null || options === void 0 ? void 0 : options.isApplicable) !== undefined ? { isApplicable: options.isApplicable } : {})), ((options === null || options === void 0 ? void 0 : options.description) !== undefined ? { description: options.description } : {})));
|
|
103
103
|
return { createActionConfig, createTransition };
|
|
104
104
|
};
|
|
105
105
|
}
|
|
@@ -10,7 +10,8 @@ export interface IBillingTransactionContextFlowData {
|
|
|
10
10
|
/**
|
|
11
11
|
* LW-828: this used to re-declare the flow-config shape by hand. It is now an alias of the shared
|
|
12
12
|
* `FlowConfig`, which is a strict superset of the old declaration — same `permissions` and `next`, plus
|
|
13
|
-
* the optional `description` / `isApplicable`
|
|
13
|
+
* the optional `description` / `isApplicable` this entity does not use (`validActionPastThreshold` was
|
|
14
|
+
* commented out of the shared type during the LW-830 collapse).
|
|
14
15
|
* Kept as a named type so every existing consumer keeps compiling unchanged.
|
|
15
16
|
*/
|
|
16
17
|
export type IBillingTransactionFlowConfig = FlowConfig<BillingTransactionStatusEnum, BillingTransactionActionEnum, IBillingTransactionContextFlowData>;
|
|
@@ -69,13 +69,6 @@ export interface IFlowConfigActionConfig<STATUS_ENUM_TYPE extends string, CONTEX
|
|
|
69
69
|
* to the row in its current state.
|
|
70
70
|
*/
|
|
71
71
|
isApplicable?: (data: CONTEXT_DATA_TYPE) => boolean;
|
|
72
|
-
/**
|
|
73
|
-
* Optional flag read by consumers that enforce a data-entry threshold: whether this action
|
|
74
|
-
* stays available once the row is older than that threshold. Absent means "yes" — consumers
|
|
75
|
-
* read it as `validActionPastThreshold ?? true`. It is a plain boolean rather than a
|
|
76
|
-
* function so it survives serialisation to clients that receive the flow config over HTTP.
|
|
77
|
-
*/
|
|
78
|
-
validActionPastThreshold?: boolean;
|
|
79
72
|
}
|
|
80
73
|
/**
|
|
81
74
|
* A flow whose parent and child rows each have their own status/action vocabulary.
|
|
@@ -112,8 +112,8 @@ const transitionOntimeStatusUpdateAction = createActionConfig(timesheet_action_e
|
|
|
112
112
|
return timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING;
|
|
113
113
|
}
|
|
114
114
|
return isBackdated(data) ? timesheet_status_enum_1.TimesheetStatusEnum.ONTIME : timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING;
|
|
115
|
-
}, undefined, {
|
|
116
|
-
const transitionOntimeStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
115
|
+
}, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
116
|
+
const transitionOntimeStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
117
117
|
const transitionOntimeStatusDeleteAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.DELETE, (data) => {
|
|
118
118
|
// non-billable timesheets: auto-delete
|
|
119
119
|
const nonBillableStatus = resolveStatusForNonBillableTimesheet(data);
|
|
@@ -123,18 +123,16 @@ const transitionOntimeStatusDeleteAction = createActionConfig(timesheet_action_e
|
|
|
123
123
|
return timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING;
|
|
124
124
|
}
|
|
125
125
|
return isBackdated(data) ? timesheet_status_enum_1.TimesheetStatusEnum.DELETED : timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING;
|
|
126
|
-
}, undefined, {
|
|
126
|
+
}, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
127
127
|
const transitionOntimeStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.ONTIME);
|
|
128
128
|
const transitionOntimeStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.ONTIME, undefined, {
|
|
129
|
-
isApplicable:
|
|
130
|
-
validActionPastThreshold: true,
|
|
129
|
+
isApplicable: makeThresholdApplicability(true),
|
|
131
130
|
});
|
|
132
131
|
const transitionOntimeStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.ONTIME, undefined, {
|
|
133
|
-
isApplicable: ()
|
|
134
|
-
validActionPastThreshold: true,
|
|
132
|
+
isApplicable: makeThresholdApplicability(true),
|
|
135
133
|
});
|
|
136
134
|
// CREATE_APPROVAL_PENDING status transitions
|
|
137
|
-
const transitionCreateApprovalPendingStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING, undefined, { isApplicable:
|
|
135
|
+
const transitionCreateApprovalPendingStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
138
136
|
const transitionCreateApprovalPendingStatusDeleteAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.DELETE, (data) => {
|
|
139
137
|
// non-billable timesheets: auto-delete
|
|
140
138
|
const nonBillableStatus = resolveStatusForNonBillableTimesheet(data);
|
|
@@ -144,26 +142,25 @@ const transitionCreateApprovalPendingStatusDeleteAction = createActionConfig(tim
|
|
|
144
142
|
// return TimesheetStatusEnum.DELETE_APPROVAL_PENDING;
|
|
145
143
|
// }
|
|
146
144
|
return timesheet_status_enum_1.TimesheetStatusEnum.DELETED;
|
|
147
|
-
}, undefined, { isApplicable:
|
|
145
|
+
}, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
148
146
|
const transitionCreateApprovalPendingStatusApproveAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.APPROVE, (data) => {
|
|
149
147
|
// non-billable timesheets: auto-approve pending or delete
|
|
150
148
|
const nonBillableStatus = resolveStatusForNonBillableTimesheet(data);
|
|
151
149
|
if (nonBillableStatus)
|
|
152
150
|
return nonBillableStatus;
|
|
153
151
|
return timesheet_status_enum_1.TimesheetStatusEnum.APPROVED;
|
|
154
|
-
}, undefined, { isApplicable:
|
|
152
|
+
}, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
155
153
|
const transitionCreateApprovalPendingStatusRejectAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.REJECT, timesheet_status_enum_1.TimesheetStatusEnum.REJECTED, undefined, {
|
|
156
|
-
isApplicable:
|
|
154
|
+
isApplicable: makeThresholdApplicability(true),
|
|
157
155
|
});
|
|
158
|
-
const transitionCreateApprovalPendingStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
156
|
+
const transitionCreateApprovalPendingStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
159
157
|
const transitionCreateApprovalPendingStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING);
|
|
160
|
-
const transitionCreateApprovalPendingStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING, undefined, { isApplicable:
|
|
161
|
-
const transitionCreateApprovalPendingStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING, undefined, { isApplicable: ()
|
|
158
|
+
const transitionCreateApprovalPendingStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
159
|
+
const transitionCreateApprovalPendingStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.CREATE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
162
160
|
// UPDATE_APPROVAL_PENDING status transitions
|
|
163
|
-
const transitionUpdateApprovalPendingStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, { isApplicable:
|
|
161
|
+
const transitionUpdateApprovalPendingStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
164
162
|
const transitionUpdateApprovalPendingStatusRecallAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.RECALL, timesheet_status_enum_1.TimesheetStatusEnum.DELETED, undefined, {
|
|
165
|
-
isApplicable:
|
|
166
|
-
validActionPastThreshold: false,
|
|
163
|
+
isApplicable: makeThresholdApplicability(false),
|
|
167
164
|
});
|
|
168
165
|
const transitionUpdateApprovalPendingStatusApproveAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.APPROVE, (data) => {
|
|
169
166
|
// non-billable timesheets: auto-approve pending or delete
|
|
@@ -171,19 +168,18 @@ const transitionUpdateApprovalPendingStatusApproveAction = createActionConfig(ti
|
|
|
171
168
|
if (nonBillableStatus)
|
|
172
169
|
return nonBillableStatus;
|
|
173
170
|
return timesheet_status_enum_1.TimesheetStatusEnum.APPROVED;
|
|
174
|
-
}, undefined, { isApplicable:
|
|
171
|
+
}, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
175
172
|
const transitionUpdateApprovalPendingStatusRejectAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.REJECT, timesheet_status_enum_1.TimesheetStatusEnum.RESOLVED_REJECTED, undefined, {
|
|
176
|
-
isApplicable:
|
|
173
|
+
isApplicable: makeThresholdApplicability(true),
|
|
177
174
|
});
|
|
178
|
-
const transitionUpdateApprovalPendingStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
175
|
+
const transitionUpdateApprovalPendingStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
179
176
|
const transitionUpdateApprovalPendingStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING);
|
|
180
|
-
const transitionUpdateApprovalPendingStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, { isApplicable:
|
|
181
|
-
const transitionUpdateApprovalPendingStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, { isApplicable: ()
|
|
177
|
+
const transitionUpdateApprovalPendingStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
178
|
+
const transitionUpdateApprovalPendingStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
182
179
|
// DELETE_APPROVAL_PENDING status transitions
|
|
183
|
-
const transitionDeleteApprovalPendingStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING, undefined, { isApplicable:
|
|
180
|
+
const transitionDeleteApprovalPendingStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
184
181
|
const transitionDeleteApprovalPendingStatusRecallAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.RECALL, timesheet_status_enum_1.TimesheetStatusEnum.DELETED, undefined, {
|
|
185
|
-
isApplicable:
|
|
186
|
-
validActionPastThreshold: false,
|
|
182
|
+
isApplicable: makeThresholdApplicability(false),
|
|
187
183
|
});
|
|
188
184
|
const transitionDeleteApprovalPendingStatusApproveAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.APPROVE, (data) => {
|
|
189
185
|
// non-billable timesheets: auto-approve pending or delete
|
|
@@ -191,18 +187,17 @@ const transitionDeleteApprovalPendingStatusApproveAction = createActionConfig(ti
|
|
|
191
187
|
if (nonBillableStatus)
|
|
192
188
|
return nonBillableStatus;
|
|
193
189
|
return timesheet_status_enum_1.TimesheetStatusEnum.DELETED;
|
|
194
|
-
}, undefined, { isApplicable:
|
|
190
|
+
}, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
195
191
|
const transitionDeleteApprovalPendingStatusRejectAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.REJECT, timesheet_status_enum_1.TimesheetStatusEnum.RESOLVED_REJECTED, undefined, {
|
|
196
|
-
isApplicable:
|
|
192
|
+
isApplicable: makeThresholdApplicability(true),
|
|
197
193
|
});
|
|
198
|
-
const transitionDeleteApprovalPendingStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
194
|
+
const transitionDeleteApprovalPendingStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
199
195
|
const transitionDeleteApprovalPendingStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING);
|
|
200
|
-
const transitionDeleteApprovalPendingStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING, undefined, { isApplicable:
|
|
201
|
-
const transitionDeleteApprovalPendingStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING, undefined, { isApplicable: ()
|
|
196
|
+
const transitionDeleteApprovalPendingStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
197
|
+
const transitionDeleteApprovalPendingStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
202
198
|
// RESOLVED_REJECTED status transitions
|
|
203
199
|
const transitionResolvedRejectedStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING, undefined, {
|
|
204
|
-
isApplicable:
|
|
205
|
-
validActionPastThreshold: false,
|
|
200
|
+
isApplicable: makeThresholdApplicability(false),
|
|
206
201
|
});
|
|
207
202
|
const transitionResolvedRejectedStatusDeleteAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.DELETE, (data) => {
|
|
208
203
|
// non-billable timesheets: auto-delete
|
|
@@ -210,14 +205,13 @@ const transitionResolvedRejectedStatusDeleteAction = createActionConfig(timeshee
|
|
|
210
205
|
if (nonBillableStatus)
|
|
211
206
|
return nonBillableStatus;
|
|
212
207
|
return timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING;
|
|
213
|
-
}, undefined, { isApplicable:
|
|
214
|
-
const transitionResolvedRejectedStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
208
|
+
}, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
209
|
+
const transitionResolvedRejectedStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
215
210
|
const transitionResolvedRejectedStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.RESOLVED_REJECTED);
|
|
216
211
|
const transitionResolvedRejectedStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.RESOLVED_REJECTED, undefined, {
|
|
217
|
-
isApplicable:
|
|
218
|
-
validActionPastThreshold: true,
|
|
212
|
+
isApplicable: makeThresholdApplicability(true),
|
|
219
213
|
});
|
|
220
|
-
const transitionResolvedRejectedStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.RESOLVED_REJECTED, undefined, { isApplicable: ()
|
|
214
|
+
const transitionResolvedRejectedStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.RESOLVED_REJECTED, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
221
215
|
// APPROVED status transitions
|
|
222
216
|
const transitionApprovedStatusUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.UPDATE, (data) => {
|
|
223
217
|
// non-billable timesheets: keep ontime/approved on edit
|
|
@@ -225,32 +219,30 @@ const transitionApprovedStatusUpdateAction = createActionConfig(timesheet_action
|
|
|
225
219
|
if (nonBillableStatus)
|
|
226
220
|
return nonBillableStatus;
|
|
227
221
|
return timesheet_status_enum_1.TimesheetStatusEnum.UPDATE_APPROVAL_PENDING;
|
|
228
|
-
}, undefined, { isApplicable:
|
|
229
|
-
const transitionApprovedStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
222
|
+
}, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
223
|
+
const transitionApprovedStatusMoveTimesheetAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
230
224
|
const transitionApprovedStatusDeleteAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.DELETE, (data) => {
|
|
231
225
|
// non-billable timesheets: auto-delete
|
|
232
226
|
const nonBillableStatus = resolveStatusForNonBillableTimesheet(data);
|
|
233
227
|
if (nonBillableStatus)
|
|
234
228
|
return nonBillableStatus;
|
|
235
229
|
return timesheet_status_enum_1.TimesheetStatusEnum.DELETE_APPROVAL_PENDING;
|
|
236
|
-
}, undefined, { isApplicable:
|
|
230
|
+
}, undefined, { isApplicable: makeThresholdApplicability(false) });
|
|
237
231
|
const transitionApprovedStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.APPROVED);
|
|
238
232
|
const transitionApprovedStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.APPROVED, undefined, {
|
|
239
|
-
isApplicable:
|
|
240
|
-
validActionPastThreshold: true,
|
|
233
|
+
isApplicable: makeThresholdApplicability(true),
|
|
241
234
|
});
|
|
242
235
|
const transitionApprovedStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.APPROVED, undefined, {
|
|
243
|
-
isApplicable: ()
|
|
244
|
-
validActionPastThreshold: true,
|
|
236
|
+
isApplicable: makeThresholdApplicability(true),
|
|
245
237
|
});
|
|
246
238
|
// MOVING_TIMESHEET_PENDING_APPROVAL status transitions
|
|
247
239
|
const transitionMovingTimesheetPendingApprovalStatusApproveAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.APPROVE, (data) => getNextStatusForMovingTimesheetActions(data), // <== currentEntity will always have status
|
|
248
240
|
// ?? TimesheetStatusEnum.APPROVED,
|
|
249
|
-
undefined, {
|
|
250
|
-
const transitionMovingTimesheetPendingApprovalStatusRejectAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.REJECT, (data) => getNextStatusForMovingTimesheetActions(data), undefined, { isApplicable:
|
|
241
|
+
undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
242
|
+
const transitionMovingTimesheetPendingApprovalStatusRejectAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.REJECT, (data) => getNextStatusForMovingTimesheetActions(data), undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
251
243
|
const transitionMovingTimesheetPendingApprovalStatusBillingUpdateAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL);
|
|
252
|
-
const transitionMovingTimesheetPendingApprovalStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable:
|
|
253
|
-
const transitionMovingTimesheetPendingApprovalStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: ()
|
|
244
|
+
const transitionMovingTimesheetPendingApprovalStatusClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
245
|
+
const transitionMovingTimesheetPendingApprovalStatusAutoClassifyAction = createActionConfig(timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY, timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL, undefined, { isApplicable: makeThresholdApplicability(true) });
|
|
254
246
|
exports.timesheetFlowConfig = {
|
|
255
247
|
[timesheet_status_enum_1.TimesheetStatusEnum.ONTIME]: {
|
|
256
248
|
actions: {
|
|
@@ -354,39 +346,20 @@ function isBackdated(data) {
|
|
|
354
346
|
}
|
|
355
347
|
return booleanValue;
|
|
356
348
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
// subtract threshold days from today
|
|
349
|
+
function makeThresholdApplicability(availableWhenPastThreshold) {
|
|
350
|
+
return (data) => {
|
|
351
|
+
const { currentTimesheet, timesheetEnterThresholdValue } = data;
|
|
352
|
+
if (!currentTimesheet || !timesheetEnterThresholdValue) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
const recordDateModel = new utils_1.DateCodeModel(currentTimesheet.dateCode);
|
|
365
356
|
const todayModel = new utils_1.DateCodeModel(utils_1.DateCodeModel.fromDate(new Date()));
|
|
366
|
-
const
|
|
367
|
-
if (
|
|
368
|
-
|
|
369
|
-
if (!actionConfig) {
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
if (!dto.actionData) {
|
|
373
|
-
throw new exceptions_1.AppBadRequestException({
|
|
374
|
-
key: error_key_enum_1.ErrorKeyEnum.ACTION_DATA,
|
|
375
|
-
message: ["Action data is required when updating a timesheet."],
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
// return actionConfig.validAction.includes(dto.actionData.action);
|
|
379
|
-
const actionConfigForAction = actionConfig.actions[dto.actionData.action];
|
|
380
|
-
if (!actionConfigForAction) {
|
|
381
|
-
throw new exceptions_1.AppBadRequestException({
|
|
382
|
-
key: error_key_enum_1.ErrorKeyEnum.ACTION,
|
|
383
|
-
message: [`No config found for action: ${dto.actionData.action}. Please try with a valid action.`],
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
isValidAction = (_a = actionConfigForAction.validActionPastThreshold) !== null && _a !== void 0 ? _a : true;
|
|
357
|
+
const thresholdDateModel = todayModel.getByOffset(timesheetEnterThresholdValue);
|
|
358
|
+
if (recordDateModel.dateCode < thresholdDateModel.dateCode) {
|
|
359
|
+
return availableWhenPastThreshold;
|
|
387
360
|
}
|
|
388
|
-
|
|
389
|
-
|
|
361
|
+
return true;
|
|
362
|
+
};
|
|
390
363
|
}
|
|
391
364
|
function getNextStatusForMovingTimesheetActions(contextData) {
|
|
392
365
|
var _a;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { ITimesheetEntityUpdateDto, ITimesheetFlowContextData } from "../../api";
|
|
1
2
|
import { CurrencyEnum } from "../../enums";
|
|
3
|
+
import { TimesheetActionEnum } from "../enums/timesheet_action_enum";
|
|
2
4
|
import { TimesheetClassificationStatusEnum } from "../enums/timesheet_classification_status_enum";
|
|
3
5
|
import { TimesheetStatusEnum } from "../enums/timesheet_status_enum";
|
|
4
6
|
import { IDesignationEntity } from "../interface/designation.entity.interface";
|
|
@@ -23,6 +25,11 @@ export interface SortableTimesheet {
|
|
|
23
25
|
resourceName?: string;
|
|
24
26
|
dateCodeForsorting?: Date;
|
|
25
27
|
}
|
|
28
|
+
export interface ITimesheetActionItem {
|
|
29
|
+
label: string;
|
|
30
|
+
value: TimesheetActionEnum | string;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
}
|
|
26
33
|
export declare class TimesheetEntityModel extends BaseEntityModel<EntityEnum.TIMESHEET> implements ITimesheetEntity {
|
|
27
34
|
id: number;
|
|
28
35
|
userId: number;
|
|
@@ -51,6 +58,13 @@ export declare class TimesheetEntityModel extends BaseEntityModel<EntityEnum.TIM
|
|
|
51
58
|
getTimesheetAmount(projectCurrency: CurrencyEnum): number;
|
|
52
59
|
get formattedDate(): string;
|
|
53
60
|
get userName(): string | undefined;
|
|
61
|
+
getUpdateActionVisibility(currentUser: UserEntityModel, currentUserProjectIds?: number[]): {
|
|
62
|
+
[key: string]: () => boolean;
|
|
63
|
+
};
|
|
64
|
+
getAvailableActions(currentUser: UserEntityModel, contextData?: ITimesheetFlowContextData, currentUserProjectIds?: number[], options?: {
|
|
65
|
+
mergeApproveReject?: boolean;
|
|
66
|
+
}): ITimesheetActionItem[];
|
|
67
|
+
getNextStatus(currentUser: UserEntityModel, dto: ITimesheetEntityUpdateDto, contextData?: ITimesheetFlowContextData): TimesheetStatusEnum;
|
|
54
68
|
get approvers(): UserEntityModel[];
|
|
55
69
|
get latestStatusFromHistory(): TimesheetStatusEnum | undefined;
|
|
56
70
|
getDateCodeDay(): number;
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TimesheetEntityModel = void 0;
|
|
4
4
|
const date_fns_1 = require("date-fns");
|
|
5
|
+
const error_key_enum_1 = require("../../enums/error.key.enum");
|
|
6
|
+
const exceptions_1 = require("../../exceptions");
|
|
5
7
|
const utils_1 = require("../../utils");
|
|
6
8
|
const relation_type_enum_1 = require("../enums/relation_type_enum");
|
|
9
|
+
const timesheet_action_enum_1 = require("../enums/timesheet_action_enum");
|
|
7
10
|
const timesheet_classification_status_enum_1 = require("../enums/timesheet_classification_status_enum");
|
|
8
11
|
const timesheet_status_enum_1 = require("../enums/timesheet_status_enum");
|
|
12
|
+
const timesheet_flow_config_1 = require("../flow-configs/timesheet_flow.config");
|
|
9
13
|
const entity_utils_interface_1 = require("../interface/entity.utils.interface");
|
|
10
14
|
const base_entity_model_1 = require("./base.entity.model");
|
|
11
15
|
// Use this mdoel for UI & BACKEND
|
|
@@ -45,6 +49,148 @@ class TimesheetEntityModel extends base_entity_model_1.BaseEntityModel {
|
|
|
45
49
|
var _a;
|
|
46
50
|
return (_a = this.user) === null || _a === void 0 ? void 0 : _a.name;
|
|
47
51
|
}
|
|
52
|
+
getUpdateActionVisibility(currentUser, currentUserProjectIds) {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
const projectIds = (_b = currentUserProjectIds !== null && currentUserProjectIds !== void 0 ? currentUserProjectIds : (_a = currentUser.projecUserMappings) === null || _a === void 0 ? void 0 : _a.map((mapping) => mapping.projectId)) !== null && _b !== void 0 ? _b : [];
|
|
55
|
+
const isProjectOwnerOrSelf = (projectId) => projectIds.includes(projectId) || currentUser.id === this.userId;
|
|
56
|
+
return {
|
|
57
|
+
TIMESHEETS_UPDATE_SELF: () => currentUser.id === this.userId,
|
|
58
|
+
TIMESHEETS_UPDATE_ORG: () => true,
|
|
59
|
+
TIMESHEETS_UPDATE_PROJECT: () => isProjectOwnerOrSelf(this.projectId),
|
|
60
|
+
TIMESHEETS_DELETE_SELF: () => currentUser.id === this.userId,
|
|
61
|
+
TIMESHEETS_DELETE_PROJECT: () => isProjectOwnerOrSelf(this.projectId),
|
|
62
|
+
TIMESHEETS_DELETE_ORG: () => true,
|
|
63
|
+
TIMESHEETS_APPROVER_PROJECT: () => isProjectOwnerOrSelf(this.projectId),
|
|
64
|
+
TIMESHEETS_APPROVER_ORG: () => true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
getAvailableActions(currentUser, contextData = {}, currentUserProjectIds, options) {
|
|
68
|
+
var _a, _b;
|
|
69
|
+
if (this.status === timesheet_status_enum_1.TimesheetStatusEnum.MOVING_TIMESHEET_PENDING_APPROVAL)
|
|
70
|
+
return [];
|
|
71
|
+
const flowForStatus = timesheet_flow_config_1.timesheetFlowConfig[this.status];
|
|
72
|
+
if (!flowForStatus)
|
|
73
|
+
return [];
|
|
74
|
+
const actionVisibilityConfig = this.getUpdateActionVisibility(currentUser, currentUserProjectIds);
|
|
75
|
+
const context = Object.assign(Object.assign({}, contextData), { currentTimesheet: this });
|
|
76
|
+
let actions = Object.entries(flowForStatus.actions)
|
|
77
|
+
.filter(([actionKey]) => {
|
|
78
|
+
if ([timesheet_action_enum_1.TimesheetActionEnum.BILLING_UPDATE, timesheet_action_enum_1.TimesheetActionEnum.MOVE_TIMESHEET, timesheet_action_enum_1.TimesheetActionEnum.AUTO_CLASSIFY].includes(actionKey))
|
|
79
|
+
return false;
|
|
80
|
+
if (actionKey === timesheet_action_enum_1.TimesheetActionEnum.CLASSIFY)
|
|
81
|
+
return false;
|
|
82
|
+
if (actionKey === timesheet_action_enum_1.TimesheetActionEnum.UPDATE && this.classificationStatus && this.classificationStatus !== timesheet_classification_status_enum_1.TimesheetClassificationStatusEnum.PENDING)
|
|
83
|
+
return false;
|
|
84
|
+
return true;
|
|
85
|
+
})
|
|
86
|
+
.map(([actionKey, actionConfig]) => {
|
|
87
|
+
const actionPermissions = actionConfig.permissions || [];
|
|
88
|
+
const hasPermission = actionPermissions.some((permission) => { var _a; return (_a = currentUser.permissions) === null || _a === void 0 ? void 0 : _a.includes(permission); });
|
|
89
|
+
if (!hasPermission)
|
|
90
|
+
return null;
|
|
91
|
+
const isVisible = actionPermissions.some((permission) => actionVisibilityConfig[permission] && actionVisibilityConfig[permission]());
|
|
92
|
+
if (!isVisible)
|
|
93
|
+
return null;
|
|
94
|
+
// LW-830 collapse: `isApplicable` is the single runtime guard. Previous 3-way logic
|
|
95
|
+
// (isAvailable ?? isApplicable, else validActionPastThreshold ?? true) is commented out:
|
|
96
|
+
// const predicate = actionConfig.isAvailable ?? actionConfig.isApplicable;
|
|
97
|
+
// let available = true;
|
|
98
|
+
// if (predicate) {
|
|
99
|
+
// available = predicate(context);
|
|
100
|
+
// } else {
|
|
101
|
+
// available = actionConfig.validActionPastThreshold ?? true;
|
|
102
|
+
// }
|
|
103
|
+
const predicate = actionConfig.isApplicable;
|
|
104
|
+
let available = true;
|
|
105
|
+
if (predicate) {
|
|
106
|
+
available = predicate(context);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
label: timesheet_action_enum_1.TimesheetActionEnum.getLabel(actionKey),
|
|
110
|
+
value: actionKey,
|
|
111
|
+
disabled: !available,
|
|
112
|
+
};
|
|
113
|
+
})
|
|
114
|
+
.filter(Boolean);
|
|
115
|
+
const hasApprove = actions.some((a) => a.value === timesheet_action_enum_1.TimesheetActionEnum.APPROVE);
|
|
116
|
+
const hasReject = actions.some((a) => a.value === timesheet_action_enum_1.TimesheetActionEnum.REJECT);
|
|
117
|
+
if ((options === null || options === void 0 ? void 0 : options.mergeApproveReject) !== false && hasApprove && hasReject) {
|
|
118
|
+
const approveDisabled = (_a = actions.find((a) => a.value === timesheet_action_enum_1.TimesheetActionEnum.APPROVE)) === null || _a === void 0 ? void 0 : _a.disabled;
|
|
119
|
+
const rejectDisabled = (_b = actions.find((a) => a.value === timesheet_action_enum_1.TimesheetActionEnum.REJECT)) === null || _b === void 0 ? void 0 : _b.disabled;
|
|
120
|
+
actions = actions.filter((a) => a.value !== timesheet_action_enum_1.TimesheetActionEnum.APPROVE && a.value !== timesheet_action_enum_1.TimesheetActionEnum.REJECT);
|
|
121
|
+
actions.push({
|
|
122
|
+
label: "Approve/Reject",
|
|
123
|
+
value: "APPROVE_REJECT",
|
|
124
|
+
disabled: approveDisabled && rejectDisabled,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return actions;
|
|
128
|
+
}
|
|
129
|
+
getNextStatus(currentUser, dto, contextData = {}) {
|
|
130
|
+
if (!this.status) {
|
|
131
|
+
throw new exceptions_1.AppBadRequestException({
|
|
132
|
+
key: error_key_enum_1.ErrorKeyEnum.STATUS,
|
|
133
|
+
message: [`Status not found: ${this.status}`],
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const flowForStatus = timesheet_flow_config_1.timesheetFlowConfig[this.status];
|
|
137
|
+
if (!flowForStatus) {
|
|
138
|
+
throw new exceptions_1.AppBadRequestException({
|
|
139
|
+
key: error_key_enum_1.ErrorKeyEnum.STATUS,
|
|
140
|
+
message: [`No flow configuration found for status: ${this.status}`],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (!(dto === null || dto === void 0 ? void 0 : dto.actionData)) {
|
|
144
|
+
throw new exceptions_1.AppBadRequestException({
|
|
145
|
+
key: error_key_enum_1.ErrorKeyEnum.ACTION_DATA,
|
|
146
|
+
message: ["dto.actionData is required"],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const matchingPermissionConfig = flowForStatus.actions[dto.actionData.action];
|
|
150
|
+
if (!matchingPermissionConfig) {
|
|
151
|
+
throw new exceptions_1.AppBadRequestException({
|
|
152
|
+
key: error_key_enum_1.ErrorKeyEnum.ACTION,
|
|
153
|
+
message: [`Action '${dto.actionData.action}' is not valid for the current status '${this.status}'.`],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (!currentUser.permissions || currentUser.permissions.length === 0) {
|
|
157
|
+
throw new exceptions_1.AppBadRequestException({
|
|
158
|
+
key: error_key_enum_1.ErrorKeyEnum.PERMISSIONS,
|
|
159
|
+
message: ["Permission not available for currentUser"],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const hasPermission = matchingPermissionConfig.permissions.some((permission) => { var _a; return (_a = currentUser.permissions) === null || _a === void 0 ? void 0 : _a.includes(permission); });
|
|
163
|
+
if (!hasPermission) {
|
|
164
|
+
throw new exceptions_1.AppBadRequestException({
|
|
165
|
+
key: error_key_enum_1.ErrorKeyEnum.PERMISSION,
|
|
166
|
+
message: [
|
|
167
|
+
`Current user does not have permission to ${dto.actionData.action} timesheet. Required permissions: ${matchingPermissionConfig.permissions.join(", ")}`,
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const context = Object.assign(Object.assign({}, contextData), { currentTimesheet: this });
|
|
172
|
+
// LW-830 collapse: `isApplicable` is the single runtime guard.
|
|
173
|
+
// const predicate = matchingPermissionConfig.isAvailable ?? matchingPermissionConfig.isApplicable;
|
|
174
|
+
// if (predicate) {
|
|
175
|
+
// const isActionAvailable = predicate(context);
|
|
176
|
+
// if (!isActionAvailable) {
|
|
177
|
+
// throw new AppBadRequestException({
|
|
178
|
+
// key: ErrorKeyEnum.ACTION,
|
|
179
|
+
// message: [`Action ${dto.actionData.action} is not applicable for current timesheet status: ${this.status}.`],
|
|
180
|
+
// });
|
|
181
|
+
// }
|
|
182
|
+
// }
|
|
183
|
+
if (matchingPermissionConfig.isApplicable) {
|
|
184
|
+
const isApplicable = matchingPermissionConfig.isApplicable(context);
|
|
185
|
+
if (!isApplicable) {
|
|
186
|
+
throw new exceptions_1.AppBadRequestException({
|
|
187
|
+
key: error_key_enum_1.ErrorKeyEnum.ACTION,
|
|
188
|
+
message: [`Action ${dto.actionData.action} is not applicable for current timesheet status: ${this.status}.`],
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return matchingPermissionConfig.next(context);
|
|
193
|
+
}
|
|
48
194
|
get approvers() {
|
|
49
195
|
var _a, _b, _c;
|
|
50
196
|
return (_c = (_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.partnersOwnerUsersOfProject) === null || _b === void 0 ? void 0 : _b.filter((u) => u.email)) !== null && _c !== void 0 ? _c : [];
|