@hed-hog/operations 0.0.298 → 0.0.300

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.
Files changed (34) hide show
  1. package/dist/operations.service.js +1193 -1193
  2. package/hedhog/data/menu.yaml +198 -198
  3. package/hedhog/data/role.yaml +23 -23
  4. package/hedhog/data/route.yaml +317 -317
  5. package/hedhog/table/operations_approval.yaml +49 -49
  6. package/hedhog/table/operations_approval_history.yaml +29 -29
  7. package/hedhog/table/operations_collaborator.yaml +67 -67
  8. package/hedhog/table/operations_collaborator_schedule_day.yaml +34 -34
  9. package/hedhog/table/operations_contract.yaml +100 -100
  10. package/hedhog/table/operations_contract_document.yaml +39 -39
  11. package/hedhog/table/operations_contract_financial_term.yaml +40 -40
  12. package/hedhog/table/operations_contract_history.yaml +27 -27
  13. package/hedhog/table/operations_contract_party.yaml +46 -46
  14. package/hedhog/table/operations_contract_revision.yaml +38 -38
  15. package/hedhog/table/operations_contract_signature.yaml +38 -38
  16. package/hedhog/table/operations_project.yaml +54 -54
  17. package/hedhog/table/operations_project_assignment.yaml +55 -55
  18. package/hedhog/table/operations_schedule_adjustment_day.yaml +34 -34
  19. package/hedhog/table/operations_schedule_adjustment_request.yaml +53 -53
  20. package/hedhog/table/operations_time_off_request.yaml +57 -57
  21. package/hedhog/table/operations_timesheet.yaml +41 -41
  22. package/hedhog/table/operations_timesheet_entry.yaml +40 -40
  23. package/package.json +4 -4
  24. package/src/operations.controller.ts +182 -182
  25. package/src/operations.module.ts +22 -22
  26. package/src/operations.service.ts +3595 -3595
  27. package/dist/operations-data.controller.d.ts +0 -139
  28. package/dist/operations-data.controller.d.ts.map +0 -1
  29. package/dist/operations-data.controller.js +0 -113
  30. package/dist/operations-data.controller.js.map +0 -1
  31. package/dist/operations-growth.controller.d.ts +0 -48
  32. package/dist/operations-growth.controller.d.ts.map +0 -1
  33. package/dist/operations-growth.controller.js +0 -90
  34. package/dist/operations-growth.controller.js.map +0 -1
@@ -37,35 +37,35 @@ let OperationsService = class OperationsService {
37
37
  }
38
38
  : { clause: '1 = 0', params: [] };
39
39
  const [projects, timesheets, timeOff, schedules, approvals, recentTimesheets] = await Promise.all([
40
- this.querySingle(`SELECT COUNT(*)::text AS total,
41
- COUNT(*) FILTER (WHERE status = 'active')::text AS active
42
- FROM operations_project p
40
+ this.querySingle(`SELECT COUNT(*)::text AS total,
41
+ COUNT(*) FILTER (WHERE status = 'active')::text AS active
42
+ FROM operations_project p
43
43
  WHERE p.deleted_at IS NULL AND ${projectFilter.clause}`, projectFilter.params),
44
- this.querySingle(`SELECT COUNT(*)::text AS total,
45
- COUNT(*) FILTER (WHERE status = 'submitted')::text AS submitted
46
- FROM operations_timesheet t
44
+ this.querySingle(`SELECT COUNT(*)::text AS total,
45
+ COUNT(*) FILTER (WHERE status = 'submitted')::text AS submitted
46
+ FROM operations_timesheet t
47
47
  WHERE t.deleted_at IS NULL AND ${collaboratorFilter.clause}`, collaboratorFilter.params),
48
- this.querySingle(`SELECT COUNT(*)::text AS total,
49
- COUNT(*) FILTER (WHERE status = 'submitted')::text AS submitted
50
- FROM operations_time_off_request tor
48
+ this.querySingle(`SELECT COUNT(*)::text AS total,
49
+ COUNT(*) FILTER (WHERE status = 'submitted')::text AS submitted
50
+ FROM operations_time_off_request tor
51
51
  WHERE tor.deleted_at IS NULL AND ${timeOffFilter.clause}`, timeOffFilter.params),
52
- this.querySingle(`SELECT COUNT(*)::text AS total,
53
- COUNT(*) FILTER (WHERE status = 'submitted')::text AS submitted
54
- FROM operations_schedule_adjustment_request sar
52
+ this.querySingle(`SELECT COUNT(*)::text AS total,
53
+ COUNT(*) FILTER (WHERE status = 'submitted')::text AS submitted
54
+ FROM operations_schedule_adjustment_request sar
55
55
  WHERE sar.deleted_at IS NULL AND ${scheduleFilter.clause}`, scheduleFilter.params),
56
- this.querySingle(`SELECT COUNT(*) FILTER (WHERE status = 'pending')::text AS pending
57
- FROM operations_approval a
56
+ this.querySingle(`SELECT COUNT(*) FILTER (WHERE status = 'pending')::text AS pending
57
+ FROM operations_approval a
58
58
  WHERE ${approvalFilter.clause}`, approvalFilter.params),
59
- this.queryRows(`SELECT t.id,
60
- c.display_name AS "collaboratorName",
61
- t.week_start_date AS "weekStartDate",
62
- t.week_end_date AS "weekEndDate",
63
- t.total_hours AS "totalHours",
64
- t.status
65
- FROM operations_timesheet t
66
- JOIN operations_collaborator c ON c.id = t.collaborator_id
67
- WHERE t.deleted_at IS NULL AND ${collaboratorFilter.clause}
68
- ORDER BY COALESCE(t.submitted_at, t.updated_at) DESC
59
+ this.queryRows(`SELECT t.id,
60
+ c.display_name AS "collaboratorName",
61
+ t.week_start_date AS "weekStartDate",
62
+ t.week_end_date AS "weekEndDate",
63
+ t.total_hours AS "totalHours",
64
+ t.status
65
+ FROM operations_timesheet t
66
+ JOIN operations_collaborator c ON c.id = t.collaborator_id
67
+ WHERE t.deleted_at IS NULL AND ${collaboratorFilter.clause}
68
+ ORDER BY COALESCE(t.submitted_at, t.updated_at) DESC
69
69
  LIMIT 5`, collaboratorFilter.params),
70
70
  ]);
71
71
  return {
@@ -95,43 +95,43 @@ let OperationsService = class OperationsService {
95
95
  const actor = await this.getActorContext(userId);
96
96
  this.ensureCollaborator(actor);
97
97
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'c.id', actor.isDirector);
98
- return this.queryRows(`SELECT c.id,
99
- c.user_id AS "userId",
100
- c.code,
101
- c.collaborator_type AS "collaboratorType",
102
- c.display_name AS "displayName",
103
- c.department,
104
- c.title,
105
- c.level_label AS "levelLabel",
106
- c.weekly_capacity_hours AS "weeklyCapacityHours",
107
- c.status,
108
- c.joined_at AS "joinedAt",
109
- c.left_at AS "leftAt",
110
- c.notes,
111
- s.id AS "supervisorId",
112
- s.display_name AS "supervisorName",
113
- hiring_contract.id AS "contractId",
114
- hiring_contract.status AS "contractStatus",
115
- COUNT(DISTINCT pa.id)::int AS "activeAssignments"
116
- FROM operations_collaborator c
117
- LEFT JOIN operations_collaborator s
118
- ON s.id = c.supervisor_collaborator_id
119
- LEFT JOIN operations_project_assignment pa
120
- ON pa.collaborator_id = c.id
121
- AND pa.deleted_at IS NULL
122
- AND pa.status IN ('planned', 'active')
123
- LEFT JOIN LATERAL (
124
- SELECT oc.id, oc.status
125
- FROM operations_contract oc
126
- WHERE oc.related_collaborator_id = c.id
127
- AND oc.deleted_at IS NULL
128
- ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
129
- oc.created_at DESC
130
- LIMIT 1
131
- ) hiring_contract ON TRUE
132
- WHERE c.deleted_at IS NULL
133
- AND ${filter.clause}
134
- GROUP BY c.id, s.id, hiring_contract.id, hiring_contract.status
98
+ return this.queryRows(`SELECT c.id,
99
+ c.user_id AS "userId",
100
+ c.code,
101
+ c.collaborator_type AS "collaboratorType",
102
+ c.display_name AS "displayName",
103
+ c.department,
104
+ c.title,
105
+ c.level_label AS "levelLabel",
106
+ c.weekly_capacity_hours AS "weeklyCapacityHours",
107
+ c.status,
108
+ c.joined_at AS "joinedAt",
109
+ c.left_at AS "leftAt",
110
+ c.notes,
111
+ s.id AS "supervisorId",
112
+ s.display_name AS "supervisorName",
113
+ hiring_contract.id AS "contractId",
114
+ hiring_contract.status AS "contractStatus",
115
+ COUNT(DISTINCT pa.id)::int AS "activeAssignments"
116
+ FROM operations_collaborator c
117
+ LEFT JOIN operations_collaborator s
118
+ ON s.id = c.supervisor_collaborator_id
119
+ LEFT JOIN operations_project_assignment pa
120
+ ON pa.collaborator_id = c.id
121
+ AND pa.deleted_at IS NULL
122
+ AND pa.status IN ('planned', 'active')
123
+ LEFT JOIN LATERAL (
124
+ SELECT oc.id, oc.status
125
+ FROM operations_contract oc
126
+ WHERE oc.related_collaborator_id = c.id
127
+ AND oc.deleted_at IS NULL
128
+ ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
129
+ oc.created_at DESC
130
+ LIMIT 1
131
+ ) hiring_contract ON TRUE
132
+ WHERE c.deleted_at IS NULL
133
+ AND ${filter.clause}
134
+ GROUP BY c.id, s.id, hiring_contract.id, hiring_contract.status
135
135
  ORDER BY c.display_name ASC`, filter.params);
136
136
  }
137
137
  async getMyCollaborator(userId) {
@@ -167,155 +167,155 @@ let OperationsService = class OperationsService {
167
167
  };
168
168
  }
169
169
  const teamFilter = this.buildIdFilter(actor.teamCollaboratorIds, 'c.id', false);
170
- const teamMembers = await this.queryRows(`SELECT c.id,
171
- c.user_id AS "userId",
172
- c.code,
173
- c.display_name AS "displayName",
174
- c.collaborator_type AS "collaboratorType",
175
- c.department,
176
- c.title,
177
- c.status,
178
- COUNT(DISTINCT pa.id)::int AS "activeAssignments",
179
- COUNT(DISTINCT a.id) FILTER (WHERE a.status = 'pending')::int AS "pendingApprovals",
180
- COUNT(DISTINCT tor.id) FILTER (WHERE tor.status = 'submitted')::int AS "pendingTimeOffRequests",
181
- COUNT(DISTINCT sar.id) FILTER (WHERE sar.status = 'submitted')::int AS "pendingScheduleAdjustmentRequests"
182
- FROM operations_collaborator c
183
- LEFT JOIN operations_project_assignment pa
184
- ON pa.collaborator_id = c.id
185
- AND pa.deleted_at IS NULL
186
- AND pa.status IN ('planned', 'active')
187
- LEFT JOIN operations_approval a
188
- ON a.requester_collaborator_id = c.id
189
- AND a.deleted_at IS NULL
190
- AND a.status = 'pending'
191
- LEFT JOIN operations_time_off_request tor
192
- ON tor.collaborator_id = c.id
193
- AND tor.deleted_at IS NULL
194
- AND tor.status = 'submitted'
195
- LEFT JOIN operations_schedule_adjustment_request sar
196
- ON sar.collaborator_id = c.id
197
- AND sar.deleted_at IS NULL
198
- AND sar.status = 'submitted'
199
- WHERE c.deleted_at IS NULL AND ${teamFilter.clause}
200
- GROUP BY c.id
170
+ const teamMembers = await this.queryRows(`SELECT c.id,
171
+ c.user_id AS "userId",
172
+ c.code,
173
+ c.display_name AS "displayName",
174
+ c.collaborator_type AS "collaboratorType",
175
+ c.department,
176
+ c.title,
177
+ c.status,
178
+ COUNT(DISTINCT pa.id)::int AS "activeAssignments",
179
+ COUNT(DISTINCT a.id) FILTER (WHERE a.status = 'pending')::int AS "pendingApprovals",
180
+ COUNT(DISTINCT tor.id) FILTER (WHERE tor.status = 'submitted')::int AS "pendingTimeOffRequests",
181
+ COUNT(DISTINCT sar.id) FILTER (WHERE sar.status = 'submitted')::int AS "pendingScheduleAdjustmentRequests"
182
+ FROM operations_collaborator c
183
+ LEFT JOIN operations_project_assignment pa
184
+ ON pa.collaborator_id = c.id
185
+ AND pa.deleted_at IS NULL
186
+ AND pa.status IN ('planned', 'active')
187
+ LEFT JOIN operations_approval a
188
+ ON a.requester_collaborator_id = c.id
189
+ AND a.deleted_at IS NULL
190
+ AND a.status = 'pending'
191
+ LEFT JOIN operations_time_off_request tor
192
+ ON tor.collaborator_id = c.id
193
+ AND tor.deleted_at IS NULL
194
+ AND tor.status = 'submitted'
195
+ LEFT JOIN operations_schedule_adjustment_request sar
196
+ ON sar.collaborator_id = c.id
197
+ AND sar.deleted_at IS NULL
198
+ AND sar.status = 'submitted'
199
+ WHERE c.deleted_at IS NULL AND ${teamFilter.clause}
200
+ GROUP BY c.id
201
201
  ORDER BY c.display_name ASC`, teamFilter.params);
202
202
  const [teamProjects, pendingApprovalQueue, pendingTimeOffRequests, pendingScheduleAdjustmentRequests] = await Promise.all([
203
- this.queryRows(`SELECT p.id,
204
- p.code,
205
- p.name,
206
- p.client_name AS "clientName",
207
- p.status,
208
- COUNT(DISTINCT pa.collaborator_id)::int AS "teamSize",
209
- COUNT(DISTINCT t.id) FILTER (WHERE t.status = 'submitted')::int AS "pendingTimesheets"
210
- FROM operations_project p
211
- JOIN operations_project_assignment pa
212
- ON pa.project_id = p.id
213
- AND pa.deleted_at IS NULL
214
- AND pa.status IN ('planned', 'active')
215
- LEFT JOIN operations_timesheet_entry te
216
- ON te.project_assignment_id = pa.id
217
- AND te.deleted_at IS NULL
218
- LEFT JOIN operations_timesheet t
219
- ON t.id = te.timesheet_id
220
- AND t.deleted_at IS NULL
221
- WHERE p.deleted_at IS NULL
222
- AND pa.collaborator_id = ANY($1::int[])
223
- GROUP BY p.id
203
+ this.queryRows(`SELECT p.id,
204
+ p.code,
205
+ p.name,
206
+ p.client_name AS "clientName",
207
+ p.status,
208
+ COUNT(DISTINCT pa.collaborator_id)::int AS "teamSize",
209
+ COUNT(DISTINCT t.id) FILTER (WHERE t.status = 'submitted')::int AS "pendingTimesheets"
210
+ FROM operations_project p
211
+ JOIN operations_project_assignment pa
212
+ ON pa.project_id = p.id
213
+ AND pa.deleted_at IS NULL
214
+ AND pa.status IN ('planned', 'active')
215
+ LEFT JOIN operations_timesheet_entry te
216
+ ON te.project_assignment_id = pa.id
217
+ AND te.deleted_at IS NULL
218
+ LEFT JOIN operations_timesheet t
219
+ ON t.id = te.timesheet_id
220
+ AND t.deleted_at IS NULL
221
+ WHERE p.deleted_at IS NULL
222
+ AND pa.collaborator_id = ANY($1::int[])
223
+ GROUP BY p.id
224
224
  ORDER BY p.name ASC`, [actor.teamCollaboratorIds]),
225
- this.queryRows(`SELECT a.id,
226
- a.target_type AS "targetType",
227
- a.target_id AS "targetId",
228
- a.requester_collaborator_id AS "requesterCollaboratorId",
229
- requester.display_name AS "requesterName",
230
- a.approver_collaborator_id AS "approverCollaboratorId",
231
- approver.display_name AS "approverName",
232
- a.status,
233
- a.submitted_at AS "submittedAt",
234
- a.decided_at AS "decidedAt",
235
- a.decision_note AS "decisionNote",
236
- t.week_start_date AS "timesheetWeekStartDate",
237
- t.week_end_date AS "timesheetWeekEndDate",
238
- t.total_hours AS "timesheetTotalHours",
239
- COALESCE(
240
- STRING_AGG(DISTINCT p.name, ', ') FILTER (WHERE p.name IS NOT NULL),
241
- ''
242
- ) AS "timesheetProjectNames",
243
- tor.request_type AS "timeOffType",
244
- tor.start_date AS "timeOffStartDate",
245
- tor.end_date AS "timeOffEndDate",
246
- tor.reason AS "timeOffReason",
247
- sar.request_scope AS "scheduleRequestScope",
248
- sar.effective_start_date AS "scheduleStartDate",
249
- sar.effective_end_date AS "scheduleEndDate",
250
- sar.reason AS "scheduleReason"
251
- FROM operations_approval a
252
- JOIN operations_collaborator requester
253
- ON requester.id = a.requester_collaborator_id
254
- LEFT JOIN operations_collaborator approver
255
- ON approver.id = a.approver_collaborator_id
256
- LEFT JOIN operations_timesheet t
257
- ON a.target_type = 'timesheet'
258
- AND t.id = a.target_id
259
- LEFT JOIN operations_timesheet_entry te
260
- ON te.timesheet_id = t.id
261
- AND te.deleted_at IS NULL
262
- LEFT JOIN operations_project_assignment pa
263
- ON pa.id = te.project_assignment_id
264
- LEFT JOIN operations_project p
265
- ON p.id = pa.project_id
266
- LEFT JOIN operations_time_off_request tor
267
- ON a.target_type = 'time_off_request'
268
- AND tor.id = a.target_id
269
- LEFT JOIN operations_schedule_adjustment_request sar
270
- ON a.target_type = 'schedule_adjustment_request'
271
- AND sar.id = a.target_id
272
- WHERE a.deleted_at IS NULL
273
- AND a.status = 'pending'
274
- AND a.approver_collaborator_id = $1
275
- GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id
225
+ this.queryRows(`SELECT a.id,
226
+ a.target_type AS "targetType",
227
+ a.target_id AS "targetId",
228
+ a.requester_collaborator_id AS "requesterCollaboratorId",
229
+ requester.display_name AS "requesterName",
230
+ a.approver_collaborator_id AS "approverCollaboratorId",
231
+ approver.display_name AS "approverName",
232
+ a.status,
233
+ a.submitted_at AS "submittedAt",
234
+ a.decided_at AS "decidedAt",
235
+ a.decision_note AS "decisionNote",
236
+ t.week_start_date AS "timesheetWeekStartDate",
237
+ t.week_end_date AS "timesheetWeekEndDate",
238
+ t.total_hours AS "timesheetTotalHours",
239
+ COALESCE(
240
+ STRING_AGG(DISTINCT p.name, ', ') FILTER (WHERE p.name IS NOT NULL),
241
+ ''
242
+ ) AS "timesheetProjectNames",
243
+ tor.request_type AS "timeOffType",
244
+ tor.start_date AS "timeOffStartDate",
245
+ tor.end_date AS "timeOffEndDate",
246
+ tor.reason AS "timeOffReason",
247
+ sar.request_scope AS "scheduleRequestScope",
248
+ sar.effective_start_date AS "scheduleStartDate",
249
+ sar.effective_end_date AS "scheduleEndDate",
250
+ sar.reason AS "scheduleReason"
251
+ FROM operations_approval a
252
+ JOIN operations_collaborator requester
253
+ ON requester.id = a.requester_collaborator_id
254
+ LEFT JOIN operations_collaborator approver
255
+ ON approver.id = a.approver_collaborator_id
256
+ LEFT JOIN operations_timesheet t
257
+ ON a.target_type = 'timesheet'
258
+ AND t.id = a.target_id
259
+ LEFT JOIN operations_timesheet_entry te
260
+ ON te.timesheet_id = t.id
261
+ AND te.deleted_at IS NULL
262
+ LEFT JOIN operations_project_assignment pa
263
+ ON pa.id = te.project_assignment_id
264
+ LEFT JOIN operations_project p
265
+ ON p.id = pa.project_id
266
+ LEFT JOIN operations_time_off_request tor
267
+ ON a.target_type = 'time_off_request'
268
+ AND tor.id = a.target_id
269
+ LEFT JOIN operations_schedule_adjustment_request sar
270
+ ON a.target_type = 'schedule_adjustment_request'
271
+ AND sar.id = a.target_id
272
+ WHERE a.deleted_at IS NULL
273
+ AND a.status = 'pending'
274
+ AND a.approver_collaborator_id = $1
275
+ GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id
276
276
  ORDER BY a.submitted_at DESC, a.id DESC`, [actor.collaboratorId]),
277
- this.queryRows(`SELECT tor.id,
278
- tor.collaborator_id AS "collaboratorId",
279
- c.display_name AS "collaboratorName",
280
- tor.request_type AS "requestType",
281
- tor.start_date AS "startDate",
282
- tor.end_date AS "endDate",
283
- tor.total_days AS "totalDays",
284
- tor.status,
285
- tor.reason,
286
- tor.submitted_at AS "submittedAt",
287
- tor.reviewed_at AS "reviewedAt",
288
- approval.decision_note AS "approverNote"
289
- FROM operations_time_off_request tor
290
- JOIN operations_collaborator c ON c.id = tor.collaborator_id
291
- LEFT JOIN operations_approval approval
292
- ON approval.target_type = 'time_off_request'
293
- AND approval.target_id = tor.id
294
- AND approval.deleted_at IS NULL
295
- WHERE tor.deleted_at IS NULL
296
- AND tor.status = 'submitted'
297
- AND tor.collaborator_id = ANY($1::int[])
277
+ this.queryRows(`SELECT tor.id,
278
+ tor.collaborator_id AS "collaboratorId",
279
+ c.display_name AS "collaboratorName",
280
+ tor.request_type AS "requestType",
281
+ tor.start_date AS "startDate",
282
+ tor.end_date AS "endDate",
283
+ tor.total_days AS "totalDays",
284
+ tor.status,
285
+ tor.reason,
286
+ tor.submitted_at AS "submittedAt",
287
+ tor.reviewed_at AS "reviewedAt",
288
+ approval.decision_note AS "approverNote"
289
+ FROM operations_time_off_request tor
290
+ JOIN operations_collaborator c ON c.id = tor.collaborator_id
291
+ LEFT JOIN operations_approval approval
292
+ ON approval.target_type = 'time_off_request'
293
+ AND approval.target_id = tor.id
294
+ AND approval.deleted_at IS NULL
295
+ WHERE tor.deleted_at IS NULL
296
+ AND tor.status = 'submitted'
297
+ AND tor.collaborator_id = ANY($1::int[])
298
298
  ORDER BY tor.start_date ASC, tor.id ASC`, [actor.teamCollaboratorIds]),
299
- this.queryRows(`SELECT sar.id,
300
- sar.collaborator_id AS "collaboratorId",
301
- c.display_name AS "collaboratorName",
302
- sar.request_scope AS "requestScope",
303
- sar.effective_start_date AS "effectiveStartDate",
304
- sar.effective_end_date AS "effectiveEndDate",
305
- sar.status,
306
- sar.reason,
307
- sar.submitted_at AS "submittedAt",
308
- sar.reviewed_at AS "reviewedAt",
309
- approval.decision_note AS "approverNote"
310
- FROM operations_schedule_adjustment_request sar
311
- JOIN operations_collaborator c ON c.id = sar.collaborator_id
312
- LEFT JOIN operations_approval approval
313
- ON approval.target_type = 'schedule_adjustment_request'
314
- AND approval.target_id = sar.id
315
- AND approval.deleted_at IS NULL
316
- WHERE sar.deleted_at IS NULL
317
- AND sar.status = 'submitted'
318
- AND sar.collaborator_id = ANY($1::int[])
299
+ this.queryRows(`SELECT sar.id,
300
+ sar.collaborator_id AS "collaboratorId",
301
+ c.display_name AS "collaboratorName",
302
+ sar.request_scope AS "requestScope",
303
+ sar.effective_start_date AS "effectiveStartDate",
304
+ sar.effective_end_date AS "effectiveEndDate",
305
+ sar.status,
306
+ sar.reason,
307
+ sar.submitted_at AS "submittedAt",
308
+ sar.reviewed_at AS "reviewedAt",
309
+ approval.decision_note AS "approverNote"
310
+ FROM operations_schedule_adjustment_request sar
311
+ JOIN operations_collaborator c ON c.id = sar.collaborator_id
312
+ LEFT JOIN operations_approval approval
313
+ ON approval.target_type = 'schedule_adjustment_request'
314
+ AND approval.target_id = sar.id
315
+ AND approval.deleted_at IS NULL
316
+ WHERE sar.deleted_at IS NULL
317
+ AND sar.status = 'submitted'
318
+ AND sar.collaborator_id = ANY($1::int[])
319
319
  ORDER BY sar.effective_start_date ASC, sar.id ASC`, [actor.teamCollaboratorIds]),
320
320
  ]);
321
321
  const pendingItemCounts = {
@@ -328,10 +328,10 @@ let OperationsService = class OperationsService {
328
328
  teamMembers,
329
329
  projectCount: (await this.getAssignedProjectIds(actor.teamCollaboratorIds))
330
330
  .length,
331
- pendingApprovals: Number((_b = (_a = (await this.querySingle(`SELECT COUNT(*)::text AS total
332
- FROM operations_approval
333
- WHERE deleted_at IS NULL
334
- AND status = 'pending'
331
+ pendingApprovals: Number((_b = (_a = (await this.querySingle(`SELECT COUNT(*)::text AS total
332
+ FROM operations_approval
333
+ WHERE deleted_at IS NULL
334
+ AND status = 'pending'
335
335
  AND approver_collaborator_id = $1`, [actor.collaboratorId]))) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 0),
336
336
  pendingItems: pendingItemCounts,
337
337
  teamProjects,
@@ -346,26 +346,26 @@ let OperationsService = class OperationsService {
346
346
  this.requireFields(data, ['userId', 'code', 'displayName']);
347
347
  const collaboratorId = await this.prisma.$transaction(async (tx) => {
348
348
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
349
- const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_collaborator (
350
- user_id,
351
- supervisor_collaborator_id,
352
- code,
353
- collaborator_type,
354
- display_name,
355
- department,
356
- title,
357
- level_label,
358
- weekly_capacity_hours,
359
- status,
360
- joined_at,
361
- left_at,
362
- notes,
363
- created_at,
364
- updated_at
365
- ) VALUES (
366
- $1, $2, $3, COALESCE($4, 'other'), $5, $6, $7, $8, $9,
367
- COALESCE($10, 'active'), $11, $12, $13, NOW(), NOW()
368
- )
349
+ const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_collaborator (
350
+ user_id,
351
+ supervisor_collaborator_id,
352
+ code,
353
+ collaborator_type,
354
+ display_name,
355
+ department,
356
+ title,
357
+ level_label,
358
+ weekly_capacity_hours,
359
+ status,
360
+ joined_at,
361
+ left_at,
362
+ notes,
363
+ created_at,
364
+ updated_at
365
+ ) VALUES (
366
+ $1, $2, $3, COALESCE($4, 'other'), $5, $6, $7, $8, $9,
367
+ COALESCE($10, 'active'), $11, $12, $13, NOW(), NOW()
368
+ )
369
369
  RETURNING id`, data.userId, (_a = data.supervisorCollaboratorId) !== null && _a !== void 0 ? _a : null, data.code, (_b = data.collaboratorType) !== null && _b !== void 0 ? _b : 'other', data.displayName, (_c = data.department) !== null && _c !== void 0 ? _c : null, (_d = data.title) !== null && _d !== void 0 ? _d : null, (_e = data.levelLabel) !== null && _e !== void 0 ? _e : null, (_f = data.weeklyCapacityHours) !== null && _f !== void 0 ? _f : null, (_g = data.status) !== null && _g !== void 0 ? _g : 'active', (_h = data.joinedAt) !== null && _h !== void 0 ? _h : null, (_j = data.leftAt) !== null && _j !== void 0 ? _j : null, (_k = data.notes) !== null && _k !== void 0 ? _k : null));
370
370
  const createdCollaboratorId = (_l = created[0]) === null || _l === void 0 ? void 0 : _l.id;
371
371
  await this.replaceCollaboratorScheduleDays(tx, createdCollaboratorId, data.weeklySchedule);
@@ -407,9 +407,9 @@ let OperationsService = class OperationsService {
407
407
  await this.prisma.$transaction(async (tx) => {
408
408
  if (updates.length) {
409
409
  params.push(collaboratorId);
410
- await tx.$executeRawUnsafe(`UPDATE operations_collaborator
411
- SET ${updates.join(', ')},
412
- updated_at = NOW()
410
+ await tx.$executeRawUnsafe(`UPDATE operations_collaborator
411
+ SET ${updates.join(', ')},
412
+ updated_at = NOW()
413
413
  WHERE id = $${params.length}`, ...params);
414
414
  }
415
415
  if (data.weeklySchedule) {
@@ -423,37 +423,37 @@ let OperationsService = class OperationsService {
423
423
  const filter = this.buildIdFilter(actor.visibleProjectIds, 'p.id', actor.isDirector);
424
424
  const assignmentParams = [];
425
425
  const ownAssignmentSelect = actor.collaboratorId
426
- ? `MAX(CASE WHEN pa.collaborator_id = ${this.param(assignmentParams, actor.collaboratorId)} THEN pa.id END)::int AS "myAssignmentId",
426
+ ? `MAX(CASE WHEN pa.collaborator_id = ${this.param(assignmentParams, actor.collaboratorId)} THEN pa.id END)::int AS "myAssignmentId",
427
427
  MAX(CASE WHEN pa.collaborator_id = ${this.param(assignmentParams, actor.collaboratorId)} THEN pa.role_label END) AS "myRoleLabel",`
428
- : `NULL::int AS "myAssignmentId",
428
+ : `NULL::int AS "myAssignmentId",
429
429
  NULL::varchar AS "myRoleLabel",`;
430
- return this.queryRows(`SELECT p.id,
431
- p.contract_id AS "contractId",
432
- p.manager_collaborator_id AS "managerCollaboratorId",
433
- p.code,
434
- p.name,
435
- p.client_name AS "clientName",
436
- p.summary,
437
- p.status,
438
- p.progress_percent AS "progressPercent",
439
- p.delivery_model AS "deliveryModel",
440
- p.budget_amount AS "budgetAmount",
441
- p.start_date AS "startDate",
442
- p.end_date AS "endDate",
443
- c.name AS "contractName",
444
- c.status AS "contractStatus",
445
- m.display_name AS "managerName",
446
- ${ownAssignmentSelect}
447
- COUNT(DISTINCT pa.id)::int AS "teamSize"
448
- FROM operations_project p
449
- LEFT JOIN operations_contract c ON c.id = p.contract_id
450
- LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
451
- LEFT JOIN operations_project_assignment pa
452
- ON pa.project_id = p.id
453
- AND pa.deleted_at IS NULL
454
- AND pa.status IN ('planned', 'active')
455
- WHERE p.deleted_at IS NULL AND ${filter.clause}
456
- GROUP BY p.id, c.id, m.id
430
+ return this.queryRows(`SELECT p.id,
431
+ p.contract_id AS "contractId",
432
+ p.manager_collaborator_id AS "managerCollaboratorId",
433
+ p.code,
434
+ p.name,
435
+ p.client_name AS "clientName",
436
+ p.summary,
437
+ p.status,
438
+ p.progress_percent AS "progressPercent",
439
+ p.delivery_model AS "deliveryModel",
440
+ p.budget_amount AS "budgetAmount",
441
+ p.start_date AS "startDate",
442
+ p.end_date AS "endDate",
443
+ c.name AS "contractName",
444
+ c.status AS "contractStatus",
445
+ m.display_name AS "managerName",
446
+ ${ownAssignmentSelect}
447
+ COUNT(DISTINCT pa.id)::int AS "teamSize"
448
+ FROM operations_project p
449
+ LEFT JOIN operations_contract c ON c.id = p.contract_id
450
+ LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
451
+ LEFT JOIN operations_project_assignment pa
452
+ ON pa.project_id = p.id
453
+ AND pa.deleted_at IS NULL
454
+ AND pa.status IN ('planned', 'active')
455
+ WHERE p.deleted_at IS NULL AND ${filter.clause}
456
+ GROUP BY p.id, c.id, m.id
457
457
  ORDER BY p.name ASC`, [...assignmentParams, ...filter.params]);
458
458
  }
459
459
  async getProjectById(userId, projectId) {
@@ -467,25 +467,25 @@ let OperationsService = class OperationsService {
467
467
  this.requireFields(data, ['code', 'name']);
468
468
  const createdProjectId = await this.prisma.$transaction(async (tx) => {
469
469
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
470
- const created = await tx.$queryRawUnsafe(`INSERT INTO operations_project (
471
- contract_id,
472
- manager_collaborator_id,
473
- code,
474
- name,
475
- client_name,
476
- summary,
477
- status,
478
- progress_percent,
479
- delivery_model,
480
- budget_amount,
481
- start_date,
482
- end_date,
483
- created_at,
484
- updated_at
485
- ) VALUES (
486
- $1, $2, $3, $4, $5, $6, COALESCE($7, 'planning'), $8,
487
- COALESCE($9, 'project_delivery'), $10, $11, $12, NOW(), NOW()
488
- )
470
+ const created = await tx.$queryRawUnsafe(`INSERT INTO operations_project (
471
+ contract_id,
472
+ manager_collaborator_id,
473
+ code,
474
+ name,
475
+ client_name,
476
+ summary,
477
+ status,
478
+ progress_percent,
479
+ delivery_model,
480
+ budget_amount,
481
+ start_date,
482
+ end_date,
483
+ created_at,
484
+ updated_at
485
+ ) VALUES (
486
+ $1, $2, $3, $4, $5, $6, COALESCE($7, 'planning'), $8,
487
+ COALESCE($9, 'project_delivery'), $10, $11, $12, NOW(), NOW()
488
+ )
489
489
  RETURNING id`, (_a = data.contractId) !== null && _a !== void 0 ? _a : null, (_b = data.managerCollaboratorId) !== null && _b !== void 0 ? _b : null, data.code, data.name, (_c = data.clientName) !== null && _c !== void 0 ? _c : null, (_d = data.summary) !== null && _d !== void 0 ? _d : null, (_e = data.status) !== null && _e !== void 0 ? _e : 'planning', (_f = data.progressPercent) !== null && _f !== void 0 ? _f : null, (_g = data.deliveryModel) !== null && _g !== void 0 ? _g : 'project_delivery', (_h = data.budgetAmount) !== null && _h !== void 0 ? _h : null, (_j = data.startDate) !== null && _j !== void 0 ? _j : null, (_k = data.endDate) !== null && _k !== void 0 ? _k : null);
490
490
  const projectId = (_l = created[0]) === null || _l === void 0 ? void 0 : _l.id;
491
491
  if ((_m = data.teamAssignments) === null || _m === void 0 ? void 0 : _m.length) {
@@ -507,9 +507,9 @@ let OperationsService = class OperationsService {
507
507
  contractName: (_w = data.contractName) !== null && _w !== void 0 ? _w : null,
508
508
  description: (_y = (_x = data.contractDescription) !== null && _x !== void 0 ? _x : data.summary) !== null && _y !== void 0 ? _y : null,
509
509
  });
510
- await tx.$executeRawUnsafe(`UPDATE operations_project
511
- SET contract_id = $1,
512
- updated_at = NOW()
510
+ await tx.$executeRawUnsafe(`UPDATE operations_project
511
+ SET contract_id = $1,
512
+ updated_at = NOW()
513
513
  WHERE id = $2`, contractId, projectId);
514
514
  }
515
515
  return projectId;
@@ -537,9 +537,9 @@ let OperationsService = class OperationsService {
537
537
  await this.prisma.$transaction(async (tx) => {
538
538
  if (updates.length) {
539
539
  params.push(projectId);
540
- await tx.$executeRawUnsafe(`UPDATE operations_project
541
- SET ${updates.join(', ')},
542
- updated_at = NOW()
540
+ await tx.$executeRawUnsafe(`UPDATE operations_project
541
+ SET ${updates.join(', ')},
542
+ updated_at = NOW()
543
543
  WHERE id = $${params.length}`, ...params);
544
544
  }
545
545
  if (data.teamAssignments) {
@@ -553,220 +553,220 @@ let OperationsService = class OperationsService {
553
553
  const params = [];
554
554
  const accessClause = actor.isDirector
555
555
  ? 'c.deleted_at IS NULL'
556
- : `c.deleted_at IS NULL AND (
557
- c.related_collaborator_id = ANY(${this.param(params, actor.visibleCollaboratorIds)}::int[])
558
- OR EXISTS (
559
- SELECT 1
560
- FROM operations_project p_access
561
- WHERE p_access.contract_id = c.id
562
- AND p_access.deleted_at IS NULL
563
- AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
564
- )
556
+ : `c.deleted_at IS NULL AND (
557
+ c.related_collaborator_id = ANY(${this.param(params, actor.visibleCollaboratorIds)}::int[])
558
+ OR EXISTS (
559
+ SELECT 1
560
+ FROM operations_project p_access
561
+ WHERE p_access.contract_id = c.id
562
+ AND p_access.deleted_at IS NULL
563
+ AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
564
+ )
565
565
  )`;
566
- return this.queryRows(`SELECT c.id,
567
- c.code,
568
- c.name,
569
- c.contract_category AS "contractCategory",
570
- c.contract_type AS "contractType",
571
- c.client_name AS "clientName",
572
- c.signature_status AS "signatureStatus",
573
- c.is_active AS "isActive",
574
- c.billing_model AS "billingModel",
575
- c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
576
- c.related_collaborator_id AS "relatedCollaboratorId",
577
- c.origin_type AS "originType",
578
- c.origin_id AS "originId",
579
- c.start_date AS "startDate",
580
- c.end_date AS "endDate",
581
- c.signed_at AS "signedAt",
582
- c.effective_date AS "effectiveDate",
583
- c.budget_amount AS "budgetAmount",
584
- c.monthly_hour_cap AS "monthlyHourCap",
585
- c.status,
586
- c.description,
587
- m.display_name AS "accountManagerName",
588
- linked.display_name AS "relatedCollaboratorName",
589
- COALESCE(primary_party.display_name, linked.display_name, c.client_name) AS "mainRelatedPartyName",
590
- COALESCE(financials.value_amount, 0) AS "valueAmount",
591
- COALESCE(financials.payment_amount, 0) AS "paymentAmount",
592
- COALESCE(financials.revenue_amount, 0) AS "revenueAmount",
593
- COALESCE(financials.fine_amount, 0) AS "fineAmount",
594
- COALESCE(pdf_document.file_name, '') AS "currentPdfFileName",
595
- COUNT(DISTINCT p.id)::int AS "projectCount"
596
- FROM operations_contract c
597
- LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
598
- LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
599
- LEFT JOIN LATERAL (
600
- SELECT cp.display_name
601
- FROM operations_contract_party cp
602
- WHERE cp.contract_id = c.id
603
- AND cp.deleted_at IS NULL
604
- ORDER BY cp.is_primary DESC, cp.id ASC
605
- LIMIT 1
606
- ) primary_party ON TRUE
607
- LEFT JOIN LATERAL (
608
- SELECT
609
- SUM(CASE WHEN term_type = 'value' THEN amount ELSE 0 END) AS value_amount,
610
- SUM(CASE WHEN term_type = 'payment' THEN amount ELSE 0 END) AS payment_amount,
611
- SUM(CASE WHEN term_type = 'revenue' THEN amount ELSE 0 END) AS revenue_amount,
612
- SUM(CASE WHEN term_type = 'fine' THEN amount ELSE 0 END) AS fine_amount
613
- FROM operations_contract_financial_term ft
614
- WHERE ft.contract_id = c.id
615
- AND ft.deleted_at IS NULL
616
- ) financials ON TRUE
617
- LEFT JOIN LATERAL (
618
- SELECT cd.file_name
619
- FROM operations_contract_document cd
620
- WHERE cd.contract_id = c.id
621
- AND cd.deleted_at IS NULL
622
- AND cd.is_current = true
623
- AND cd.document_type IN ('uploaded_pdf', 'generated_pdf')
624
- ORDER BY cd.id DESC
625
- LIMIT 1
626
- ) pdf_document ON TRUE
627
- LEFT JOIN operations_project p
628
- ON p.contract_id = c.id
629
- AND p.deleted_at IS NULL
630
- WHERE ${accessClause}
631
- GROUP BY c.id, m.id, linked.id
566
+ return this.queryRows(`SELECT c.id,
567
+ c.code,
568
+ c.name,
569
+ c.contract_category AS "contractCategory",
570
+ c.contract_type AS "contractType",
571
+ c.client_name AS "clientName",
572
+ c.signature_status AS "signatureStatus",
573
+ c.is_active AS "isActive",
574
+ c.billing_model AS "billingModel",
575
+ c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
576
+ c.related_collaborator_id AS "relatedCollaboratorId",
577
+ c.origin_type AS "originType",
578
+ c.origin_id AS "originId",
579
+ c.start_date AS "startDate",
580
+ c.end_date AS "endDate",
581
+ c.signed_at AS "signedAt",
582
+ c.effective_date AS "effectiveDate",
583
+ c.budget_amount AS "budgetAmount",
584
+ c.monthly_hour_cap AS "monthlyHourCap",
585
+ c.status,
586
+ c.description,
587
+ m.display_name AS "accountManagerName",
588
+ linked.display_name AS "relatedCollaboratorName",
589
+ COALESCE(primary_party.display_name, linked.display_name, c.client_name) AS "mainRelatedPartyName",
590
+ COALESCE(financials.value_amount, 0) AS "valueAmount",
591
+ COALESCE(financials.payment_amount, 0) AS "paymentAmount",
592
+ COALESCE(financials.revenue_amount, 0) AS "revenueAmount",
593
+ COALESCE(financials.fine_amount, 0) AS "fineAmount",
594
+ COALESCE(pdf_document.file_name, '') AS "currentPdfFileName",
595
+ COUNT(DISTINCT p.id)::int AS "projectCount"
596
+ FROM operations_contract c
597
+ LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
598
+ LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
599
+ LEFT JOIN LATERAL (
600
+ SELECT cp.display_name
601
+ FROM operations_contract_party cp
602
+ WHERE cp.contract_id = c.id
603
+ AND cp.deleted_at IS NULL
604
+ ORDER BY cp.is_primary DESC, cp.id ASC
605
+ LIMIT 1
606
+ ) primary_party ON TRUE
607
+ LEFT JOIN LATERAL (
608
+ SELECT
609
+ SUM(CASE WHEN term_type = 'value' THEN amount ELSE 0 END) AS value_amount,
610
+ SUM(CASE WHEN term_type = 'payment' THEN amount ELSE 0 END) AS payment_amount,
611
+ SUM(CASE WHEN term_type = 'revenue' THEN amount ELSE 0 END) AS revenue_amount,
612
+ SUM(CASE WHEN term_type = 'fine' THEN amount ELSE 0 END) AS fine_amount
613
+ FROM operations_contract_financial_term ft
614
+ WHERE ft.contract_id = c.id
615
+ AND ft.deleted_at IS NULL
616
+ ) financials ON TRUE
617
+ LEFT JOIN LATERAL (
618
+ SELECT cd.file_name
619
+ FROM operations_contract_document cd
620
+ WHERE cd.contract_id = c.id
621
+ AND cd.deleted_at IS NULL
622
+ AND cd.is_current = true
623
+ AND cd.document_type IN ('uploaded_pdf', 'generated_pdf')
624
+ ORDER BY cd.id DESC
625
+ LIMIT 1
626
+ ) pdf_document ON TRUE
627
+ LEFT JOIN operations_project p
628
+ ON p.contract_id = c.id
629
+ AND p.deleted_at IS NULL
630
+ WHERE ${accessClause}
631
+ GROUP BY c.id, m.id, linked.id
632
632
  ORDER BY c.name ASC`, params);
633
633
  }
634
634
  async getContractById(userId, contractId) {
635
635
  var _a, _b, _c;
636
636
  const actor = await this.getActorContext(userId);
637
- const contract = await this.querySingle(`SELECT c.id,
638
- c.code,
639
- c.name,
640
- c.contract_category AS "contractCategory",
641
- c.contract_type AS "contractType",
642
- c.client_name AS "clientName",
643
- c.signature_status AS "signatureStatus",
644
- c.is_active AS "isActive",
645
- c.billing_model AS "billingModel",
646
- c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
647
- c.related_collaborator_id AS "relatedCollaboratorId",
648
- c.origin_type AS "originType",
649
- c.origin_id AS "originId",
650
- c.start_date AS "startDate",
651
- c.end_date AS "endDate",
652
- c.signed_at AS "signedAt",
653
- c.effective_date AS "effectiveDate",
654
- c.budget_amount AS "budgetAmount",
655
- c.monthly_hour_cap AS "monthlyHourCap",
656
- c.status,
657
- c.description,
658
- c.content_html AS "contentHtml",
659
- m.display_name AS "accountManagerName",
660
- linked.display_name AS "relatedCollaboratorName"
661
- FROM operations_contract c
662
- LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
663
- LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
664
- WHERE c.id = $1
637
+ const contract = await this.querySingle(`SELECT c.id,
638
+ c.code,
639
+ c.name,
640
+ c.contract_category AS "contractCategory",
641
+ c.contract_type AS "contractType",
642
+ c.client_name AS "clientName",
643
+ c.signature_status AS "signatureStatus",
644
+ c.is_active AS "isActive",
645
+ c.billing_model AS "billingModel",
646
+ c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
647
+ c.related_collaborator_id AS "relatedCollaboratorId",
648
+ c.origin_type AS "originType",
649
+ c.origin_id AS "originId",
650
+ c.start_date AS "startDate",
651
+ c.end_date AS "endDate",
652
+ c.signed_at AS "signedAt",
653
+ c.effective_date AS "effectiveDate",
654
+ c.budget_amount AS "budgetAmount",
655
+ c.monthly_hour_cap AS "monthlyHourCap",
656
+ c.status,
657
+ c.description,
658
+ c.content_html AS "contentHtml",
659
+ m.display_name AS "accountManagerName",
660
+ linked.display_name AS "relatedCollaboratorName"
661
+ FROM operations_contract c
662
+ LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
663
+ LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
664
+ WHERE c.id = $1
665
665
  AND c.deleted_at IS NULL`, [contractId]);
666
666
  if (!contract) {
667
667
  throw new common_1.NotFoundException('Contract not found.');
668
668
  }
669
669
  if (!actor.isDirector) {
670
- const access = await this.querySingle(`SELECT EXISTS (
671
- SELECT 1
672
- FROM operations_contract c
673
- WHERE c.id = $1
674
- AND c.deleted_at IS NULL
675
- AND (
676
- c.related_collaborator_id = ANY($2::int[])
677
- OR EXISTS (
678
- SELECT 1
679
- FROM operations_project p
680
- WHERE p.contract_id = c.id
681
- AND p.deleted_at IS NULL
682
- AND p.id = ANY($3::int[])
683
- )
684
- )
670
+ const access = await this.querySingle(`SELECT EXISTS (
671
+ SELECT 1
672
+ FROM operations_contract c
673
+ WHERE c.id = $1
674
+ AND c.deleted_at IS NULL
675
+ AND (
676
+ c.related_collaborator_id = ANY($2::int[])
677
+ OR EXISTS (
678
+ SELECT 1
679
+ FROM operations_project p
680
+ WHERE p.contract_id = c.id
681
+ AND p.deleted_at IS NULL
682
+ AND p.id = ANY($3::int[])
683
+ )
684
+ )
685
685
  ) AS exists`, [contractId, actor.visibleCollaboratorIds, actor.visibleProjectIds]);
686
686
  if (!(access === null || access === void 0 ? void 0 : access.exists)) {
687
687
  throw new common_1.ForbiddenException('You do not have access to this contract.');
688
688
  }
689
689
  }
690
690
  const [projects, scheduleSummary, parties, signatures, financialTerms, documents, revisions, history] = await Promise.all([
691
- this.queryRows(`SELECT id, code, name, status
692
- FROM operations_project
693
- WHERE contract_id = $1
694
- AND deleted_at IS NULL
691
+ this.queryRows(`SELECT id, code, name, status
692
+ FROM operations_project
693
+ WHERE contract_id = $1
694
+ AND deleted_at IS NULL
695
695
  ORDER BY name ASC`, [contractId]),
696
696
  contract.relatedCollaboratorId
697
- ? this.queryRows(`SELECT weekday,
698
- is_working_day AS "isWorkingDay",
699
- start_time AS "startTime",
700
- end_time AS "endTime",
701
- break_minutes AS "breakMinutes"
702
- FROM operations_collaborator_schedule_day
703
- WHERE collaborator_id = $1
704
- AND deleted_at IS NULL
697
+ ? this.queryRows(`SELECT weekday,
698
+ is_working_day AS "isWorkingDay",
699
+ start_time AS "startTime",
700
+ end_time AS "endTime",
701
+ break_minutes AS "breakMinutes"
702
+ FROM operations_collaborator_schedule_day
703
+ WHERE collaborator_id = $1
704
+ AND deleted_at IS NULL
705
705
  ORDER BY id ASC`, [contract.relatedCollaboratorId])
706
706
  : Promise.resolve([]),
707
- this.queryRows(`SELECT id,
708
- party_role AS "partyRole",
709
- party_type AS "partyType",
710
- display_name AS "displayName",
711
- document_number AS "documentNumber",
712
- email,
713
- phone,
714
- is_primary AS "isPrimary"
715
- FROM operations_contract_party
716
- WHERE contract_id = $1
717
- AND deleted_at IS NULL
707
+ this.queryRows(`SELECT id,
708
+ party_role AS "partyRole",
709
+ party_type AS "partyType",
710
+ display_name AS "displayName",
711
+ document_number AS "documentNumber",
712
+ email,
713
+ phone,
714
+ is_primary AS "isPrimary"
715
+ FROM operations_contract_party
716
+ WHERE contract_id = $1
717
+ AND deleted_at IS NULL
718
718
  ORDER BY is_primary DESC, id ASC`, [contractId]),
719
- this.queryRows(`SELECT id,
720
- signer_name AS "signerName",
721
- signer_role AS "signerRole",
722
- signer_email AS "signerEmail",
723
- signer_status AS status,
724
- signed_at AS "signedAt"
725
- FROM operations_contract_signature
726
- WHERE contract_id = $1
727
- AND deleted_at IS NULL
719
+ this.queryRows(`SELECT id,
720
+ signer_name AS "signerName",
721
+ signer_role AS "signerRole",
722
+ signer_email AS "signerEmail",
723
+ signer_status AS status,
724
+ signed_at AS "signedAt"
725
+ FROM operations_contract_signature
726
+ WHERE contract_id = $1
727
+ AND deleted_at IS NULL
728
728
  ORDER BY id ASC`, [contractId]),
729
- this.queryRows(`SELECT id,
730
- term_type AS "termType",
731
- label,
732
- amount,
733
- recurrence,
734
- due_day AS "dueDay",
735
- notes
736
- FROM operations_contract_financial_term
737
- WHERE contract_id = $1
738
- AND deleted_at IS NULL
729
+ this.queryRows(`SELECT id,
730
+ term_type AS "termType",
731
+ label,
732
+ amount,
733
+ recurrence,
734
+ due_day AS "dueDay",
735
+ notes
736
+ FROM operations_contract_financial_term
737
+ WHERE contract_id = $1
738
+ AND deleted_at IS NULL
739
739
  ORDER BY id ASC`, [contractId]),
740
- this.queryRows(`SELECT id,
741
- document_type AS "documentType",
742
- file_name AS "fileName",
743
- mime_type AS "mimeType",
744
- file_content_base64 AS "fileContentBase64",
745
- is_current AS "isCurrent",
746
- notes,
747
- created_at AS "createdAt"
748
- FROM operations_contract_document
749
- WHERE contract_id = $1
750
- AND deleted_at IS NULL
740
+ this.queryRows(`SELECT id,
741
+ document_type AS "documentType",
742
+ file_name AS "fileName",
743
+ mime_type AS "mimeType",
744
+ file_content_base64 AS "fileContentBase64",
745
+ is_current AS "isCurrent",
746
+ notes,
747
+ created_at AS "createdAt"
748
+ FROM operations_contract_document
749
+ WHERE contract_id = $1
750
+ AND deleted_at IS NULL
751
751
  ORDER BY is_current DESC, id DESC`, [contractId]),
752
- this.queryRows(`SELECT id,
753
- revision_type AS "revisionType",
754
- title,
755
- effective_date AS "effectiveDate",
756
- status,
757
- summary
758
- FROM operations_contract_revision
759
- WHERE contract_id = $1
760
- AND deleted_at IS NULL
752
+ this.queryRows(`SELECT id,
753
+ revision_type AS "revisionType",
754
+ title,
755
+ effective_date AS "effectiveDate",
756
+ status,
757
+ summary
758
+ FROM operations_contract_revision
759
+ WHERE contract_id = $1
760
+ AND deleted_at IS NULL
761
761
  ORDER BY effective_date DESC NULLS LAST, id DESC`, [contractId]),
762
- this.queryRows(`SELECT id,
763
- actor_user_id AS "actorUserId",
764
- action,
765
- note,
766
- metadata_json AS "metadataJson",
767
- created_at AS "createdAt"
768
- FROM operations_contract_history
769
- WHERE contract_id = $1
762
+ this.queryRows(`SELECT id,
763
+ actor_user_id AS "actorUserId",
764
+ action,
765
+ note,
766
+ metadata_json AS "metadataJson",
767
+ created_at AS "createdAt"
768
+ FROM operations_contract_history
769
+ WHERE contract_id = $1
770
770
  ORDER BY id DESC`, [contractId]),
771
771
  ]);
772
772
  return Object.assign(Object.assign({}, contract), { mainRelatedPartyName: (_c = (_b = (_a = parties.find((party) => party.isPrimary)) === null || _a === void 0 ? void 0 : _a.displayName) !== null && _b !== void 0 ? _b : contract.relatedCollaboratorName) !== null && _c !== void 0 ? _c : contract.clientName, projects,
@@ -784,35 +784,35 @@ let OperationsService = class OperationsService {
784
784
  this.requireFields(data, ['code', 'name', 'clientName', 'startDate']);
785
785
  const createdId = await this.prisma.$transaction(async (tx) => {
786
786
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
787
- const created = await tx.$queryRawUnsafe(`INSERT INTO operations_contract (
788
- code,
789
- name,
790
- contract_category,
791
- contract_type,
792
- client_name,
793
- signature_status,
794
- is_active,
795
- billing_model,
796
- account_manager_collaborator_id,
797
- related_collaborator_id,
798
- origin_type,
799
- origin_id,
800
- start_date,
801
- end_date,
802
- signed_at,
803
- effective_date,
804
- budget_amount,
805
- monthly_hour_cap,
806
- status,
807
- description,
808
- content_html,
809
- created_at,
810
- updated_at
811
- ) VALUES (
812
- $1, $2, COALESCE($3, 'client'), COALESCE($4, 'service_agreement'), $5, COALESCE($6, 'not_started'),
813
- COALESCE($7, true), COALESCE($8, 'time_and_material'), $9, $10, COALESCE($11, 'manual'), $12, $13,
814
- $14, $15, $16, $17, $18, COALESCE($19, 'draft'), $20, $21, NOW(), NOW()
815
- )
787
+ const created = await tx.$queryRawUnsafe(`INSERT INTO operations_contract (
788
+ code,
789
+ name,
790
+ contract_category,
791
+ contract_type,
792
+ client_name,
793
+ signature_status,
794
+ is_active,
795
+ billing_model,
796
+ account_manager_collaborator_id,
797
+ related_collaborator_id,
798
+ origin_type,
799
+ origin_id,
800
+ start_date,
801
+ end_date,
802
+ signed_at,
803
+ effective_date,
804
+ budget_amount,
805
+ monthly_hour_cap,
806
+ status,
807
+ description,
808
+ content_html,
809
+ created_at,
810
+ updated_at
811
+ ) VALUES (
812
+ $1, $2, COALESCE($3, 'client'), COALESCE($4, 'service_agreement'), $5, COALESCE($6, 'not_started'),
813
+ COALESCE($7, true), COALESCE($8, 'time_and_material'), $9, $10, COALESCE($11, 'manual'), $12, $13,
814
+ $14, $15, $16, $17, $18, COALESCE($19, 'draft'), $20, $21, NOW(), NOW()
815
+ )
816
816
  RETURNING id`, data.code, data.name, (_a = data.contractCategory) !== null && _a !== void 0 ? _a : 'client', (_b = data.contractType) !== null && _b !== void 0 ? _b : 'service_agreement', data.clientName, (_c = data.signatureStatus) !== null && _c !== void 0 ? _c : 'not_started', (_d = data.isActive) !== null && _d !== void 0 ? _d : true, (_e = data.billingModel) !== null && _e !== void 0 ? _e : 'time_and_material', (_f = data.accountManagerCollaboratorId) !== null && _f !== void 0 ? _f : null, (_g = data.relatedCollaboratorId) !== null && _g !== void 0 ? _g : null, (_h = data.originType) !== null && _h !== void 0 ? _h : 'manual', (_j = data.originId) !== null && _j !== void 0 ? _j : null, data.startDate, (_k = data.endDate) !== null && _k !== void 0 ? _k : null, (_l = data.signedAt) !== null && _l !== void 0 ? _l : null, (_m = data.effectiveDate) !== null && _m !== void 0 ? _m : data.startDate, (_o = data.budgetAmount) !== null && _o !== void 0 ? _o : null, (_p = data.monthlyHourCap) !== null && _p !== void 0 ? _p : null, (_q = data.status) !== null && _q !== void 0 ? _q : 'draft', (_r = data.description) !== null && _r !== void 0 ? _r : null, (_s = data.contentHtml) !== null && _s !== void 0 ? _s : null);
817
817
  const contractId = (_t = created[0]) === null || _t === void 0 ? void 0 : _t.id;
818
818
  await this.replaceContractParties(tx, contractId, data.parties);
@@ -859,9 +859,9 @@ let OperationsService = class OperationsService {
859
859
  await this.prisma.$transaction(async (tx) => {
860
860
  if (updates.length) {
861
861
  params.push(contractId);
862
- await tx.$executeRawUnsafe(`UPDATE operations_contract
863
- SET ${updates.join(', ')},
864
- updated_at = NOW()
862
+ await tx.$executeRawUnsafe(`UPDATE operations_contract
863
+ SET ${updates.join(', ')},
864
+ updated_at = NOW()
865
865
  WHERE id = $${params.length}`, ...params);
866
866
  }
867
867
  if (data.parties) {
@@ -886,46 +886,46 @@ let OperationsService = class OperationsService {
886
886
  async listTimesheets(userId) {
887
887
  const actor = await this.getActorContext(userId);
888
888
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 't.collaborator_id', actor.isDirector);
889
- const headers = await this.queryRows(`SELECT t.id,
890
- t.collaborator_id AS "collaboratorId",
891
- c.display_name AS "collaboratorName",
892
- t.approver_collaborator_id AS "approverCollaboratorId",
893
- a.display_name AS "approverName",
894
- t.week_start_date AS "weekStartDate",
895
- t.week_end_date AS "weekEndDate",
896
- t.total_hours AS "totalHours",
897
- t.status,
898
- t.submitted_at AS "submittedAt",
899
- t.reviewed_at AS "reviewedAt",
900
- t.notes,
901
- approval.decision_note AS "decisionNote"
902
- FROM operations_timesheet t
903
- JOIN operations_collaborator c ON c.id = t.collaborator_id
904
- LEFT JOIN operations_collaborator a ON a.id = t.approver_collaborator_id
905
- LEFT JOIN operations_approval approval
906
- ON approval.target_type = 'timesheet'
907
- AND approval.target_id = t.id
908
- AND approval.deleted_at IS NULL
909
- WHERE t.deleted_at IS NULL AND ${filter.clause}
889
+ const headers = await this.queryRows(`SELECT t.id,
890
+ t.collaborator_id AS "collaboratorId",
891
+ c.display_name AS "collaboratorName",
892
+ t.approver_collaborator_id AS "approverCollaboratorId",
893
+ a.display_name AS "approverName",
894
+ t.week_start_date AS "weekStartDate",
895
+ t.week_end_date AS "weekEndDate",
896
+ t.total_hours AS "totalHours",
897
+ t.status,
898
+ t.submitted_at AS "submittedAt",
899
+ t.reviewed_at AS "reviewedAt",
900
+ t.notes,
901
+ approval.decision_note AS "decisionNote"
902
+ FROM operations_timesheet t
903
+ JOIN operations_collaborator c ON c.id = t.collaborator_id
904
+ LEFT JOIN operations_collaborator a ON a.id = t.approver_collaborator_id
905
+ LEFT JOIN operations_approval approval
906
+ ON approval.target_type = 'timesheet'
907
+ AND approval.target_id = t.id
908
+ AND approval.deleted_at IS NULL
909
+ WHERE t.deleted_at IS NULL AND ${filter.clause}
910
910
  ORDER BY t.week_start_date DESC, t.id DESC`, filter.params);
911
911
  if (!headers.length) {
912
912
  return headers;
913
913
  }
914
- const entries = await this.queryRows(`SELECT e.id,
915
- e.timesheet_id AS "timesheetId",
916
- e.project_assignment_id AS "projectAssignmentId",
917
- pa.project_id AS "projectId",
918
- p.name AS "projectName",
919
- pa.role_label AS "roleLabel",
920
- e.activity_label AS "activityLabel",
921
- e.work_date AS "workDate",
922
- e.hours,
923
- e.description
924
- FROM operations_timesheet_entry e
925
- LEFT JOIN operations_project_assignment pa ON pa.id = e.project_assignment_id
926
- LEFT JOIN operations_project p ON p.id = pa.project_id
927
- WHERE e.deleted_at IS NULL
928
- AND e.timesheet_id = ANY($1::int[])
914
+ const entries = await this.queryRows(`SELECT e.id,
915
+ e.timesheet_id AS "timesheetId",
916
+ e.project_assignment_id AS "projectAssignmentId",
917
+ pa.project_id AS "projectId",
918
+ p.name AS "projectName",
919
+ pa.role_label AS "roleLabel",
920
+ e.activity_label AS "activityLabel",
921
+ e.work_date AS "workDate",
922
+ e.hours,
923
+ e.description
924
+ FROM operations_timesheet_entry e
925
+ LEFT JOIN operations_project_assignment pa ON pa.id = e.project_assignment_id
926
+ LEFT JOIN operations_project p ON p.id = pa.project_id
927
+ WHERE e.deleted_at IS NULL
928
+ AND e.timesheet_id = ANY($1::int[])
929
929
  ORDER BY e.work_date ASC, e.id ASC`, [headers.map((item) => item.id)]);
930
930
  const grouped = this.groupBy(entries, 'timesheetId');
931
931
  return headers.map((timesheet) => {
@@ -949,16 +949,16 @@ let OperationsService = class OperationsService {
949
949
  const collaborator = await this.getCollaboratorById(collaboratorId);
950
950
  const created = await this.prisma.$transaction(async (tx) => {
951
951
  var _a, _b, _c, _d;
952
- const row = (await tx.$queryRawUnsafe(`INSERT INTO operations_timesheet (
953
- collaborator_id,
954
- approver_collaborator_id,
955
- week_start_date,
956
- week_end_date,
957
- notes,
958
- status,
959
- created_at,
960
- updated_at
961
- ) VALUES ($1, $2, $3, $4, $5, 'draft', NOW(), NOW())
952
+ const row = (await tx.$queryRawUnsafe(`INSERT INTO operations_timesheet (
953
+ collaborator_id,
954
+ approver_collaborator_id,
955
+ week_start_date,
956
+ week_end_date,
957
+ notes,
958
+ status,
959
+ created_at,
960
+ updated_at
961
+ ) VALUES ($1, $2, $3, $4, $5, 'draft', NOW(), NOW())
962
962
  RETURNING id`, collaboratorId, (_a = collaborator.supervisorId) !== null && _a !== void 0 ? _a : null, data.weekStartDate, data.weekEndDate, (_b = data.notes) !== null && _b !== void 0 ? _b : null));
963
963
  const timesheetId = (_c = row[0]) === null || _c === void 0 ? void 0 : _c.id;
964
964
  await this.replaceTimesheetEntries(tx, timesheetId, (_d = data.entries) !== null && _d !== void 0 ? _d : [], collaboratorId);
@@ -984,9 +984,9 @@ let OperationsService = class OperationsService {
984
984
  this.pushUpdate(updates, params, 'notes', data.notes);
985
985
  if (updates.length) {
986
986
  params.push(timesheetId);
987
- await tx.$executeRawUnsafe(`UPDATE operations_timesheet
988
- SET ${updates.join(', ')},
989
- updated_at = NOW()
987
+ await tx.$executeRawUnsafe(`UPDATE operations_timesheet
988
+ SET ${updates.join(', ')},
989
+ updated_at = NOW()
990
990
  WHERE id = $${params.length}`, ...params);
991
991
  }
992
992
  if (data.entries) {
@@ -1009,11 +1009,11 @@ let OperationsService = class OperationsService {
1009
1009
  const collaborator = await this.getCollaboratorById(current.collaboratorId);
1010
1010
  const approverId = (_b = (_a = current.approverCollaboratorId) !== null && _a !== void 0 ? _a : collaborator.supervisorId) !== null && _b !== void 0 ? _b : null;
1011
1011
  await this.prisma.$transaction(async (tx) => {
1012
- await tx.$executeRawUnsafe(`UPDATE operations_timesheet
1013
- SET status = 'submitted',
1014
- approver_collaborator_id = $1,
1015
- submitted_at = NOW(),
1016
- updated_at = NOW()
1012
+ await tx.$executeRawUnsafe(`UPDATE operations_timesheet
1013
+ SET status = 'submitted',
1014
+ approver_collaborator_id = $1,
1015
+ submitted_at = NOW(),
1016
+ updated_at = NOW()
1017
1017
  WHERE id = $2`, approverId, timesheetId);
1018
1018
  await this.upsertApproval(tx, {
1019
1019
  targetType: 'timesheet',
@@ -1027,28 +1027,28 @@ let OperationsService = class OperationsService {
1027
1027
  async listTimeOffRequests(userId) {
1028
1028
  const actor = await this.getActorContext(userId);
1029
1029
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'tor.collaborator_id', actor.isDirector);
1030
- return this.queryRows(`SELECT tor.id,
1031
- tor.collaborator_id AS "collaboratorId",
1032
- c.display_name AS "collaboratorName",
1033
- tor.approver_collaborator_id AS "approverCollaboratorId",
1034
- a.display_name AS "approverName",
1035
- tor.request_type AS "requestType",
1036
- tor.start_date AS "startDate",
1037
- tor.end_date AS "endDate",
1038
- tor.total_days AS "totalDays",
1039
- tor.status,
1040
- tor.reason,
1041
- tor.submitted_at AS "submittedAt",
1042
- tor.reviewed_at AS "reviewedAt",
1043
- approval.decision_note AS "approverNote"
1044
- FROM operations_time_off_request tor
1045
- JOIN operations_collaborator c ON c.id = tor.collaborator_id
1046
- LEFT JOIN operations_collaborator a ON a.id = tor.approver_collaborator_id
1047
- LEFT JOIN operations_approval approval
1048
- ON approval.target_type = 'time_off_request'
1049
- AND approval.target_id = tor.id
1050
- AND approval.deleted_at IS NULL
1051
- WHERE tor.deleted_at IS NULL AND ${filter.clause}
1030
+ return this.queryRows(`SELECT tor.id,
1031
+ tor.collaborator_id AS "collaboratorId",
1032
+ c.display_name AS "collaboratorName",
1033
+ tor.approver_collaborator_id AS "approverCollaboratorId",
1034
+ a.display_name AS "approverName",
1035
+ tor.request_type AS "requestType",
1036
+ tor.start_date AS "startDate",
1037
+ tor.end_date AS "endDate",
1038
+ tor.total_days AS "totalDays",
1039
+ tor.status,
1040
+ tor.reason,
1041
+ tor.submitted_at AS "submittedAt",
1042
+ tor.reviewed_at AS "reviewedAt",
1043
+ approval.decision_note AS "approverNote"
1044
+ FROM operations_time_off_request tor
1045
+ JOIN operations_collaborator c ON c.id = tor.collaborator_id
1046
+ LEFT JOIN operations_collaborator a ON a.id = tor.approver_collaborator_id
1047
+ LEFT JOIN operations_approval approval
1048
+ ON approval.target_type = 'time_off_request'
1049
+ AND approval.target_id = tor.id
1050
+ AND approval.deleted_at IS NULL
1051
+ WHERE tor.deleted_at IS NULL AND ${filter.clause}
1052
1052
  ORDER BY tor.start_date DESC, tor.id DESC`, filter.params);
1053
1053
  }
1054
1054
  async createTimeOffRequest(userId, data) {
@@ -1067,19 +1067,19 @@ let OperationsService = class OperationsService {
1067
1067
  const collaborator = await this.getCollaboratorById(collaboratorId);
1068
1068
  const created = await this.prisma.$transaction(async (tx) => {
1069
1069
  var _a, _b, _c, _d, _e, _f;
1070
- const row = (await tx.$queryRawUnsafe(`INSERT INTO operations_time_off_request (
1071
- collaborator_id,
1072
- approver_collaborator_id,
1073
- request_type,
1074
- start_date,
1075
- end_date,
1076
- total_days,
1077
- status,
1078
- reason,
1079
- submitted_at,
1080
- created_at,
1081
- updated_at
1082
- ) VALUES ($1, $2, COALESCE($3, 'vacation'), $4, $5, $6, 'submitted', $7, NOW(), NOW(), NOW())
1070
+ const row = (await tx.$queryRawUnsafe(`INSERT INTO operations_time_off_request (
1071
+ collaborator_id,
1072
+ approver_collaborator_id,
1073
+ request_type,
1074
+ start_date,
1075
+ end_date,
1076
+ total_days,
1077
+ status,
1078
+ reason,
1079
+ submitted_at,
1080
+ created_at,
1081
+ updated_at
1082
+ ) VALUES ($1, $2, COALESCE($3, 'vacation'), $4, $5, $6, 'submitted', $7, NOW(), NOW(), NOW())
1083
1083
  RETURNING id`, collaboratorId, (_a = collaborator.supervisorId) !== null && _a !== void 0 ? _a : null, (_b = data.requestType) !== null && _b !== void 0 ? _b : 'vacation', data.startDate, data.endDate, (_c = data.totalDays) !== null && _c !== void 0 ? _c : null, (_d = data.reason) !== null && _d !== void 0 ? _d : null));
1084
1084
  const requestId = (_e = row[0]) === null || _e === void 0 ? void 0 : _e.id;
1085
1085
  await this.upsertApproval(tx, {
@@ -1090,65 +1090,65 @@ let OperationsService = class OperationsService {
1090
1090
  });
1091
1091
  return requestId;
1092
1092
  });
1093
- return this.querySingle(`SELECT id,
1094
- collaborator_id AS "collaboratorId",
1095
- approver_collaborator_id AS "approverCollaboratorId",
1096
- request_type AS "requestType",
1097
- start_date AS "startDate",
1098
- end_date AS "endDate",
1099
- total_days AS "totalDays",
1100
- status,
1101
- reason,
1102
- submitted_at AS "submittedAt",
1103
- reviewed_at AS "reviewedAt"
1104
- FROM operations_time_off_request
1093
+ return this.querySingle(`SELECT id,
1094
+ collaborator_id AS "collaboratorId",
1095
+ approver_collaborator_id AS "approverCollaboratorId",
1096
+ request_type AS "requestType",
1097
+ start_date AS "startDate",
1098
+ end_date AS "endDate",
1099
+ total_days AS "totalDays",
1100
+ status,
1101
+ reason,
1102
+ submitted_at AS "submittedAt",
1103
+ reviewed_at AS "reviewedAt"
1104
+ FROM operations_time_off_request
1105
1105
  WHERE id = $1`, [created]);
1106
1106
  }
1107
1107
  async listScheduleAdjustments(userId) {
1108
1108
  const actor = await this.getActorContext(userId);
1109
1109
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'sar.collaborator_id', actor.isDirector);
1110
- const requests = await this.queryRows(`SELECT sar.id,
1111
- sar.collaborator_id AS "collaboratorId",
1112
- c.display_name AS "collaboratorName",
1113
- sar.approver_collaborator_id AS "approverCollaboratorId",
1114
- a.display_name AS "approverName",
1115
- sar.request_scope AS "requestScope",
1116
- sar.effective_start_date AS "effectiveStartDate",
1117
- sar.effective_end_date AS "effectiveEndDate",
1118
- sar.status,
1119
- sar.reason,
1120
- sar.submitted_at AS "submittedAt",
1121
- sar.reviewed_at AS "reviewedAt",
1122
- approval.decision_note AS "approverNote"
1123
- FROM operations_schedule_adjustment_request sar
1124
- JOIN operations_collaborator c ON c.id = sar.collaborator_id
1125
- LEFT JOIN operations_collaborator a ON a.id = sar.approver_collaborator_id
1126
- LEFT JOIN operations_approval approval
1127
- ON approval.target_type = 'schedule_adjustment_request'
1128
- AND approval.target_id = sar.id
1129
- AND approval.deleted_at IS NULL
1130
- WHERE sar.deleted_at IS NULL AND ${filter.clause}
1110
+ const requests = await this.queryRows(`SELECT sar.id,
1111
+ sar.collaborator_id AS "collaboratorId",
1112
+ c.display_name AS "collaboratorName",
1113
+ sar.approver_collaborator_id AS "approverCollaboratorId",
1114
+ a.display_name AS "approverName",
1115
+ sar.request_scope AS "requestScope",
1116
+ sar.effective_start_date AS "effectiveStartDate",
1117
+ sar.effective_end_date AS "effectiveEndDate",
1118
+ sar.status,
1119
+ sar.reason,
1120
+ sar.submitted_at AS "submittedAt",
1121
+ sar.reviewed_at AS "reviewedAt",
1122
+ approval.decision_note AS "approverNote"
1123
+ FROM operations_schedule_adjustment_request sar
1124
+ JOIN operations_collaborator c ON c.id = sar.collaborator_id
1125
+ LEFT JOIN operations_collaborator a ON a.id = sar.approver_collaborator_id
1126
+ LEFT JOIN operations_approval approval
1127
+ ON approval.target_type = 'schedule_adjustment_request'
1128
+ AND approval.target_id = sar.id
1129
+ AND approval.deleted_at IS NULL
1130
+ WHERE sar.deleted_at IS NULL AND ${filter.clause}
1131
1131
  ORDER BY sar.effective_start_date DESC, sar.id DESC`, filter.params);
1132
1132
  if (!requests.length) {
1133
1133
  return requests;
1134
1134
  }
1135
- const days = await this.queryRows(`SELECT schedule_adjustment_request_id AS "requestId",
1136
- weekday,
1137
- is_working_day AS "isWorkingDay",
1138
- start_time AS "startTime",
1139
- end_time AS "endTime",
1140
- break_minutes AS "breakMinutes"
1141
- FROM operations_schedule_adjustment_day
1142
- WHERE schedule_adjustment_request_id = ANY($1::int[])
1135
+ const days = await this.queryRows(`SELECT schedule_adjustment_request_id AS "requestId",
1136
+ weekday,
1137
+ is_working_day AS "isWorkingDay",
1138
+ start_time AS "startTime",
1139
+ end_time AS "endTime",
1140
+ break_minutes AS "breakMinutes"
1141
+ FROM operations_schedule_adjustment_day
1142
+ WHERE schedule_adjustment_request_id = ANY($1::int[])
1143
1143
  ORDER BY id ASC`, [requests.map((item) => item.id)]);
1144
- const currentSchedule = await this.queryRows(`SELECT collaborator_id AS "collaboratorId",
1145
- weekday,
1146
- is_working_day AS "isWorkingDay",
1147
- start_time AS "startTime",
1148
- end_time AS "endTime",
1149
- break_minutes AS "breakMinutes"
1150
- FROM operations_collaborator_schedule_day
1151
- WHERE collaborator_id = ANY($1::int[])
1144
+ const currentSchedule = await this.queryRows(`SELECT collaborator_id AS "collaboratorId",
1145
+ weekday,
1146
+ is_working_day AS "isWorkingDay",
1147
+ start_time AS "startTime",
1148
+ end_time AS "endTime",
1149
+ break_minutes AS "breakMinutes"
1150
+ FROM operations_collaborator_schedule_day
1151
+ WHERE collaborator_id = ANY($1::int[])
1152
1152
  ORDER BY id ASC`, [this.uniqueNumbers(requests.map((item) => item.collaboratorId))]);
1153
1153
  const grouped = this.groupBy(days, 'requestId');
1154
1154
  const currentScheduleByCollaborator = this.groupBy(currentSchedule, 'collaboratorId');
@@ -1176,32 +1176,32 @@ let OperationsService = class OperationsService {
1176
1176
  const collaborator = await this.getCollaboratorById(collaboratorId);
1177
1177
  const created = await this.prisma.$transaction(async (tx) => {
1178
1178
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1179
- const row = (await tx.$queryRawUnsafe(`INSERT INTO operations_schedule_adjustment_request (
1180
- collaborator_id,
1181
- approver_collaborator_id,
1182
- request_scope,
1183
- effective_start_date,
1184
- effective_end_date,
1185
- status,
1186
- reason,
1187
- submitted_at,
1188
- created_at,
1189
- updated_at
1190
- ) VALUES (
1191
- $1, $2, COALESCE($3, 'temporary'), $4, $5, 'submitted', $6, NOW(), NOW(), NOW()
1192
- )
1179
+ const row = (await tx.$queryRawUnsafe(`INSERT INTO operations_schedule_adjustment_request (
1180
+ collaborator_id,
1181
+ approver_collaborator_id,
1182
+ request_scope,
1183
+ effective_start_date,
1184
+ effective_end_date,
1185
+ status,
1186
+ reason,
1187
+ submitted_at,
1188
+ created_at,
1189
+ updated_at
1190
+ ) VALUES (
1191
+ $1, $2, COALESCE($3, 'temporary'), $4, $5, 'submitted', $6, NOW(), NOW(), NOW()
1192
+ )
1193
1193
  RETURNING id`, collaboratorId, (_a = collaborator.supervisorId) !== null && _a !== void 0 ? _a : null, (_b = data.requestScope) !== null && _b !== void 0 ? _b : 'temporary', data.effectiveStartDate, (_c = data.effectiveEndDate) !== null && _c !== void 0 ? _c : null, (_d = data.reason) !== null && _d !== void 0 ? _d : null));
1194
1194
  const requestId = (_e = row[0]) === null || _e === void 0 ? void 0 : _e.id;
1195
1195
  for (const day of data.days) {
1196
- await tx.$executeRawUnsafe(`INSERT INTO operations_schedule_adjustment_day (
1197
- schedule_adjustment_request_id,
1198
- weekday,
1199
- is_working_day,
1200
- start_time,
1201
- end_time,
1202
- break_minutes,
1203
- created_at,
1204
- updated_at
1196
+ await tx.$executeRawUnsafe(`INSERT INTO operations_schedule_adjustment_day (
1197
+ schedule_adjustment_request_id,
1198
+ weekday,
1199
+ is_working_day,
1200
+ start_time,
1201
+ end_time,
1202
+ break_minutes,
1203
+ created_at,
1204
+ updated_at
1205
1205
  ) VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())`, requestId, day.weekday, (_f = day.isWorkingDay) !== null && _f !== void 0 ? _f : true, (_g = day.startTime) !== null && _g !== void 0 ? _g : null, (_h = day.endTime) !== null && _h !== void 0 ? _h : null, (_j = day.breakMinutes) !== null && _j !== void 0 ? _j : null);
1206
1206
  }
1207
1207
  await this.upsertApproval(tx, {
@@ -1212,17 +1212,17 @@ let OperationsService = class OperationsService {
1212
1212
  });
1213
1213
  return requestId;
1214
1214
  });
1215
- return this.querySingle(`SELECT id,
1216
- collaborator_id AS "collaboratorId",
1217
- approver_collaborator_id AS "approverCollaboratorId",
1218
- request_scope AS "requestScope",
1219
- effective_start_date AS "effectiveStartDate",
1220
- effective_end_date AS "effectiveEndDate",
1221
- status,
1222
- reason,
1223
- submitted_at AS "submittedAt",
1224
- reviewed_at AS "reviewedAt"
1225
- FROM operations_schedule_adjustment_request
1215
+ return this.querySingle(`SELECT id,
1216
+ collaborator_id AS "collaboratorId",
1217
+ approver_collaborator_id AS "approverCollaboratorId",
1218
+ request_scope AS "requestScope",
1219
+ effective_start_date AS "effectiveStartDate",
1220
+ effective_end_date AS "effectiveEndDate",
1221
+ status,
1222
+ reason,
1223
+ submitted_at AS "submittedAt",
1224
+ reviewed_at AS "reviewedAt"
1225
+ FROM operations_schedule_adjustment_request
1226
1226
  WHERE id = $1`, [created]);
1227
1227
  }
1228
1228
  async listApprovals(userId) {
@@ -1232,55 +1232,55 @@ let OperationsService = class OperationsService {
1232
1232
  const clause = actor.isDirector
1233
1233
  ? 'a.deleted_at IS NULL'
1234
1234
  : `a.deleted_at IS NULL AND a.approver_collaborator_id = ${this.param(params, actor.collaboratorId)}`;
1235
- return this.queryRows(`SELECT a.id,
1236
- a.target_type AS "targetType",
1237
- a.target_id AS "targetId",
1238
- a.requester_collaborator_id AS "requesterCollaboratorId",
1239
- requester.display_name AS "requesterName",
1240
- a.approver_collaborator_id AS "approverCollaboratorId",
1241
- approver.display_name AS "approverName",
1242
- a.status,
1243
- a.submitted_at AS "submittedAt",
1244
- a.decided_at AS "decidedAt",
1245
- a.decision_note AS "decisionNote",
1246
- t.week_start_date AS "timesheetWeekStartDate",
1247
- t.week_end_date AS "timesheetWeekEndDate",
1248
- t.total_hours AS "timesheetTotalHours",
1249
- COALESCE(
1250
- STRING_AGG(DISTINCT p.name, ', ') FILTER (WHERE p.name IS NOT NULL),
1251
- ''
1252
- ) AS "timesheetProjectNames",
1253
- tor.request_type AS "timeOffType",
1254
- tor.start_date AS "timeOffStartDate",
1255
- tor.end_date AS "timeOffEndDate",
1256
- tor.reason AS "timeOffReason",
1257
- sar.request_scope AS "scheduleRequestScope",
1258
- sar.effective_start_date AS "scheduleStartDate",
1259
- sar.effective_end_date AS "scheduleEndDate",
1260
- sar.reason AS "scheduleReason"
1261
- FROM operations_approval a
1262
- JOIN operations_collaborator requester
1263
- ON requester.id = a.requester_collaborator_id
1264
- LEFT JOIN operations_collaborator approver
1265
- ON approver.id = a.approver_collaborator_id
1266
- LEFT JOIN operations_timesheet t
1267
- ON a.target_type = 'timesheet'
1268
- AND t.id = a.target_id
1269
- LEFT JOIN operations_timesheet_entry te
1270
- ON te.timesheet_id = t.id
1271
- AND te.deleted_at IS NULL
1272
- LEFT JOIN operations_project_assignment pa
1273
- ON pa.id = te.project_assignment_id
1274
- LEFT JOIN operations_project p
1275
- ON p.id = pa.project_id
1276
- LEFT JOIN operations_time_off_request tor
1277
- ON a.target_type = 'time_off_request'
1278
- AND tor.id = a.target_id
1279
- LEFT JOIN operations_schedule_adjustment_request sar
1280
- ON a.target_type = 'schedule_adjustment_request'
1281
- AND sar.id = a.target_id
1282
- WHERE ${clause}
1283
- GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id
1235
+ return this.queryRows(`SELECT a.id,
1236
+ a.target_type AS "targetType",
1237
+ a.target_id AS "targetId",
1238
+ a.requester_collaborator_id AS "requesterCollaboratorId",
1239
+ requester.display_name AS "requesterName",
1240
+ a.approver_collaborator_id AS "approverCollaboratorId",
1241
+ approver.display_name AS "approverName",
1242
+ a.status,
1243
+ a.submitted_at AS "submittedAt",
1244
+ a.decided_at AS "decidedAt",
1245
+ a.decision_note AS "decisionNote",
1246
+ t.week_start_date AS "timesheetWeekStartDate",
1247
+ t.week_end_date AS "timesheetWeekEndDate",
1248
+ t.total_hours AS "timesheetTotalHours",
1249
+ COALESCE(
1250
+ STRING_AGG(DISTINCT p.name, ', ') FILTER (WHERE p.name IS NOT NULL),
1251
+ ''
1252
+ ) AS "timesheetProjectNames",
1253
+ tor.request_type AS "timeOffType",
1254
+ tor.start_date AS "timeOffStartDate",
1255
+ tor.end_date AS "timeOffEndDate",
1256
+ tor.reason AS "timeOffReason",
1257
+ sar.request_scope AS "scheduleRequestScope",
1258
+ sar.effective_start_date AS "scheduleStartDate",
1259
+ sar.effective_end_date AS "scheduleEndDate",
1260
+ sar.reason AS "scheduleReason"
1261
+ FROM operations_approval a
1262
+ JOIN operations_collaborator requester
1263
+ ON requester.id = a.requester_collaborator_id
1264
+ LEFT JOIN operations_collaborator approver
1265
+ ON approver.id = a.approver_collaborator_id
1266
+ LEFT JOIN operations_timesheet t
1267
+ ON a.target_type = 'timesheet'
1268
+ AND t.id = a.target_id
1269
+ LEFT JOIN operations_timesheet_entry te
1270
+ ON te.timesheet_id = t.id
1271
+ AND te.deleted_at IS NULL
1272
+ LEFT JOIN operations_project_assignment pa
1273
+ ON pa.id = te.project_assignment_id
1274
+ LEFT JOIN operations_project p
1275
+ ON p.id = pa.project_id
1276
+ LEFT JOIN operations_time_off_request tor
1277
+ ON a.target_type = 'time_off_request'
1278
+ AND tor.id = a.target_id
1279
+ LEFT JOIN operations_schedule_adjustment_request sar
1280
+ ON a.target_type = 'schedule_adjustment_request'
1281
+ AND sar.id = a.target_id
1282
+ WHERE ${clause}
1283
+ GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id
1284
1284
  ORDER BY a.submitted_at DESC, a.id DESC`, params);
1285
1285
  }
1286
1286
  async approve(userId, approvalId, data) {
@@ -1349,14 +1349,14 @@ let OperationsService = class OperationsService {
1349
1349
  async decideApproval(userId, approvalId, action, data) {
1350
1350
  const actor = await this.getActorContext(userId);
1351
1351
  this.ensureSupervisor(actor);
1352
- const approval = await this.querySingle(`SELECT id,
1353
- target_type AS "targetType",
1354
- target_id AS "targetId",
1355
- requester_collaborator_id AS "requesterCollaboratorId",
1356
- approver_collaborator_id AS "approverCollaboratorId",
1357
- status
1358
- FROM operations_approval
1359
- WHERE id = $1
1352
+ const approval = await this.querySingle(`SELECT id,
1353
+ target_type AS "targetType",
1354
+ target_id AS "targetId",
1355
+ requester_collaborator_id AS "requesterCollaboratorId",
1356
+ approver_collaborator_id AS "approverCollaboratorId",
1357
+ status
1358
+ FROM operations_approval
1359
+ WHERE id = $1
1360
1360
  AND deleted_at IS NULL`, [approvalId]);
1361
1361
  if (!approval) {
1362
1362
  throw new common_1.NotFoundException('Approval not found.');
@@ -1370,47 +1370,47 @@ let OperationsService = class OperationsService {
1370
1370
  const nextStatus = action === 'approve' ? 'approved' : 'rejected';
1371
1371
  await this.prisma.$transaction(async (tx) => {
1372
1372
  var _a, _b;
1373
- await tx.$executeRawUnsafe(`UPDATE operations_approval
1374
- SET status = $1,
1375
- decided_at = NOW(),
1376
- decision_note = $2,
1377
- updated_at = NOW()
1373
+ await tx.$executeRawUnsafe(`UPDATE operations_approval
1374
+ SET status = $1,
1375
+ decided_at = NOW(),
1376
+ decision_note = $2,
1377
+ updated_at = NOW()
1378
1378
  WHERE id = $3`, nextStatus, (_a = data.note) !== null && _a !== void 0 ? _a : null, approvalId);
1379
1379
  if (approval.targetType === 'timesheet') {
1380
- await tx.$executeRawUnsafe(`UPDATE operations_timesheet
1381
- SET status = $1,
1382
- reviewed_at = NOW(),
1383
- approver_collaborator_id = $2,
1384
- updated_at = NOW()
1380
+ await tx.$executeRawUnsafe(`UPDATE operations_timesheet
1381
+ SET status = $1,
1382
+ reviewed_at = NOW(),
1383
+ approver_collaborator_id = $2,
1384
+ updated_at = NOW()
1385
1385
  WHERE id = $3`, nextStatus, actor.collaboratorId, approval.targetId);
1386
1386
  }
1387
1387
  if (approval.targetType === 'time_off_request') {
1388
- await tx.$executeRawUnsafe(`UPDATE operations_time_off_request
1389
- SET status = $1,
1390
- reviewed_at = NOW(),
1391
- approver_collaborator_id = $2,
1392
- submitted_at = COALESCE(submitted_at, NOW()),
1393
- updated_at = NOW()
1388
+ await tx.$executeRawUnsafe(`UPDATE operations_time_off_request
1389
+ SET status = $1,
1390
+ reviewed_at = NOW(),
1391
+ approver_collaborator_id = $2,
1392
+ submitted_at = COALESCE(submitted_at, NOW()),
1393
+ updated_at = NOW()
1394
1394
  WHERE id = $3`, nextStatus, actor.collaboratorId, approval.targetId);
1395
1395
  }
1396
1396
  if (approval.targetType === 'schedule_adjustment_request') {
1397
- await tx.$executeRawUnsafe(`UPDATE operations_schedule_adjustment_request
1398
- SET status = $1,
1399
- reviewed_at = NOW(),
1400
- approver_collaborator_id = $2,
1401
- submitted_at = COALESCE(submitted_at, NOW()),
1402
- updated_at = NOW()
1397
+ await tx.$executeRawUnsafe(`UPDATE operations_schedule_adjustment_request
1398
+ SET status = $1,
1399
+ reviewed_at = NOW(),
1400
+ approver_collaborator_id = $2,
1401
+ submitted_at = COALESCE(submitted_at, NOW()),
1402
+ updated_at = NOW()
1403
1403
  WHERE id = $3`, nextStatus, actor.collaboratorId, approval.targetId);
1404
1404
  }
1405
1405
  await this.insertApprovalHistory(tx, approvalId, actor.collaboratorId, nextStatus === 'approved' ? 'approved' : 'rejected', (_b = data.note) !== null && _b !== void 0 ? _b : null);
1406
1406
  });
1407
- return this.querySingle(`SELECT id,
1408
- target_type AS "targetType",
1409
- target_id AS "targetId",
1410
- status,
1411
- decided_at AS "decidedAt",
1412
- decision_note AS "decisionNote"
1413
- FROM operations_approval
1407
+ return this.querySingle(`SELECT id,
1408
+ target_type AS "targetType",
1409
+ target_id AS "targetId",
1410
+ status,
1411
+ decided_at AS "decidedAt",
1412
+ decision_note AS "decisionNote"
1413
+ FROM operations_approval
1414
1414
  WHERE id = $1`, [approvalId]);
1415
1415
  }
1416
1416
  async getActorContext(userId) {
@@ -1459,25 +1459,25 @@ let OperationsService = class OperationsService {
1459
1459
  };
1460
1460
  }
1461
1461
  async getCollaboratorByUserId(userId) {
1462
- return this.querySingle(`SELECT c.id,
1463
- c.display_name AS "displayName",
1464
- s.id AS "supervisorId",
1465
- s.display_name AS "supervisorName"
1466
- FROM operations_collaborator c
1467
- LEFT JOIN operations_collaborator s
1468
- ON s.id = c.supervisor_collaborator_id
1469
- WHERE c.user_id = $1
1462
+ return this.querySingle(`SELECT c.id,
1463
+ c.display_name AS "displayName",
1464
+ s.id AS "supervisorId",
1465
+ s.display_name AS "supervisorName"
1466
+ FROM operations_collaborator c
1467
+ LEFT JOIN operations_collaborator s
1468
+ ON s.id = c.supervisor_collaborator_id
1469
+ WHERE c.user_id = $1
1470
1470
  AND c.deleted_at IS NULL`, [userId]);
1471
1471
  }
1472
1472
  async getCollaboratorById(collaboratorId) {
1473
- const collaborator = await this.querySingle(`SELECT c.id,
1474
- c.display_name AS "displayName",
1475
- s.id AS "supervisorId",
1476
- s.display_name AS "supervisorName"
1477
- FROM operations_collaborator c
1478
- LEFT JOIN operations_collaborator s
1479
- ON s.id = c.supervisor_collaborator_id
1480
- WHERE c.id = $1
1473
+ const collaborator = await this.querySingle(`SELECT c.id,
1474
+ c.display_name AS "displayName",
1475
+ s.id AS "supervisorId",
1476
+ s.display_name AS "supervisorName"
1477
+ FROM operations_collaborator c
1478
+ LEFT JOIN operations_collaborator s
1479
+ ON s.id = c.supervisor_collaborator_id
1480
+ WHERE c.id = $1
1481
1481
  AND c.deleted_at IS NULL`, [collaboratorId]);
1482
1482
  if (!collaborator) {
1483
1483
  throw new common_1.NotFoundException('Collaborator not found.');
@@ -1486,91 +1486,91 @@ let OperationsService = class OperationsService {
1486
1486
  }
1487
1487
  async getProjectDetails(projectId, actorCollaboratorId) {
1488
1488
  var _a, _b, _c, _d, _e, _f, _g;
1489
- const project = await this.querySingle(`SELECT p.id,
1490
- p.contract_id AS "contractId",
1491
- p.manager_collaborator_id AS "managerCollaboratorId",
1492
- p.code,
1493
- p.name,
1494
- p.client_name AS "clientName",
1495
- p.summary,
1496
- p.status,
1497
- p.progress_percent AS "progressPercent",
1498
- p.delivery_model AS "deliveryModel",
1499
- p.budget_amount AS "budgetAmount",
1500
- p.start_date AS "startDate",
1501
- p.end_date AS "endDate",
1502
- c.name AS "contractName",
1503
- c.status AS "contractStatus",
1504
- c.contract_category AS "contractCategory",
1505
- m.display_name AS "managerName",
1506
- MAX(CASE WHEN pa.collaborator_id = $2 THEN pa.id END)::int AS "myAssignmentId",
1507
- MAX(CASE WHEN pa.collaborator_id = $2 THEN pa.role_label END) AS "myRoleLabel",
1508
- COUNT(DISTINCT pa.id)::int AS "teamSize"
1509
- FROM operations_project p
1510
- LEFT JOIN operations_contract c ON c.id = p.contract_id
1511
- LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
1512
- LEFT JOIN operations_project_assignment pa
1513
- ON pa.project_id = p.id
1514
- AND pa.deleted_at IS NULL
1515
- WHERE p.id = $1
1516
- AND p.deleted_at IS NULL
1489
+ const project = await this.querySingle(`SELECT p.id,
1490
+ p.contract_id AS "contractId",
1491
+ p.manager_collaborator_id AS "managerCollaboratorId",
1492
+ p.code,
1493
+ p.name,
1494
+ p.client_name AS "clientName",
1495
+ p.summary,
1496
+ p.status,
1497
+ p.progress_percent AS "progressPercent",
1498
+ p.delivery_model AS "deliveryModel",
1499
+ p.budget_amount AS "budgetAmount",
1500
+ p.start_date AS "startDate",
1501
+ p.end_date AS "endDate",
1502
+ c.name AS "contractName",
1503
+ c.status AS "contractStatus",
1504
+ c.contract_category AS "contractCategory",
1505
+ m.display_name AS "managerName",
1506
+ MAX(CASE WHEN pa.collaborator_id = $2 THEN pa.id END)::int AS "myAssignmentId",
1507
+ MAX(CASE WHEN pa.collaborator_id = $2 THEN pa.role_label END) AS "myRoleLabel",
1508
+ COUNT(DISTINCT pa.id)::int AS "teamSize"
1509
+ FROM operations_project p
1510
+ LEFT JOIN operations_contract c ON c.id = p.contract_id
1511
+ LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
1512
+ LEFT JOIN operations_project_assignment pa
1513
+ ON pa.project_id = p.id
1514
+ AND pa.deleted_at IS NULL
1515
+ WHERE p.id = $1
1516
+ AND p.deleted_at IS NULL
1517
1517
  GROUP BY p.id, c.id, m.id`, [projectId, actorCollaboratorId !== null && actorCollaboratorId !== void 0 ? actorCollaboratorId : null]);
1518
1518
  if (!project) {
1519
1519
  throw new common_1.NotFoundException('Project not found.');
1520
1520
  }
1521
1521
  const [assignments, relatedContract, timesheetSummary, operationalIndicators] = await Promise.all([
1522
- this.queryRows(`SELECT pa.id,
1523
- pa.collaborator_id AS "collaboratorId",
1524
- c.display_name AS "collaboratorName",
1525
- pa.role_label AS "roleLabel",
1526
- pa.allocation_percent AS "allocationPercent",
1527
- pa.weekly_hours AS "weeklyHours",
1528
- pa.is_billable AS "isBillable",
1529
- pa.start_date AS "startDate",
1530
- pa.end_date AS "endDate",
1531
- pa.status
1532
- FROM operations_project_assignment pa
1533
- JOIN operations_collaborator c ON c.id = pa.collaborator_id
1534
- WHERE pa.project_id = $1
1535
- AND pa.deleted_at IS NULL
1522
+ this.queryRows(`SELECT pa.id,
1523
+ pa.collaborator_id AS "collaboratorId",
1524
+ c.display_name AS "collaboratorName",
1525
+ pa.role_label AS "roleLabel",
1526
+ pa.allocation_percent AS "allocationPercent",
1527
+ pa.weekly_hours AS "weeklyHours",
1528
+ pa.is_billable AS "isBillable",
1529
+ pa.start_date AS "startDate",
1530
+ pa.end_date AS "endDate",
1531
+ pa.status
1532
+ FROM operations_project_assignment pa
1533
+ JOIN operations_collaborator c ON c.id = pa.collaborator_id
1534
+ WHERE pa.project_id = $1
1535
+ AND pa.deleted_at IS NULL
1536
1536
  ORDER BY c.display_name ASC`, [projectId]),
1537
1537
  project.contractId
1538
- ? this.querySingle(`SELECT id,
1539
- code,
1540
- name,
1541
- client_name AS "clientName",
1542
- contract_category AS "contractCategory",
1543
- billing_model AS "billingModel",
1544
- status,
1545
- start_date AS "startDate",
1546
- end_date AS "endDate",
1547
- budget_amount AS "budgetAmount",
1548
- monthly_hour_cap AS "monthlyHourCap",
1549
- description,
1550
- origin_type AS "originType",
1551
- origin_id AS "originId"
1552
- FROM operations_contract
1553
- WHERE id = $1
1538
+ ? this.querySingle(`SELECT id,
1539
+ code,
1540
+ name,
1541
+ client_name AS "clientName",
1542
+ contract_category AS "contractCategory",
1543
+ billing_model AS "billingModel",
1544
+ status,
1545
+ start_date AS "startDate",
1546
+ end_date AS "endDate",
1547
+ budget_amount AS "budgetAmount",
1548
+ monthly_hour_cap AS "monthlyHourCap",
1549
+ description,
1550
+ origin_type AS "originType",
1551
+ origin_id AS "originId"
1552
+ FROM operations_contract
1553
+ WHERE id = $1
1554
1554
  AND deleted_at IS NULL`, [project.contractId])
1555
1555
  : Promise.resolve(null),
1556
- this.querySingle(`SELECT COUNT(DISTINCT t.id)::text AS "totalTimesheets",
1557
- COUNT(DISTINCT t.id) FILTER (WHERE t.status = 'submitted')::text AS "pendingTimesheets",
1558
- COALESCE(SUM(e.hours), 0)::text AS "totalHours"
1559
- FROM operations_project_assignment pa
1560
- LEFT JOIN operations_timesheet_entry e
1561
- ON e.project_assignment_id = pa.id
1562
- AND e.deleted_at IS NULL
1563
- LEFT JOIN operations_timesheet t
1564
- ON t.id = e.timesheet_id
1565
- AND t.deleted_at IS NULL
1566
- WHERE pa.project_id = $1
1556
+ this.querySingle(`SELECT COUNT(DISTINCT t.id)::text AS "totalTimesheets",
1557
+ COUNT(DISTINCT t.id) FILTER (WHERE t.status = 'submitted')::text AS "pendingTimesheets",
1558
+ COALESCE(SUM(e.hours), 0)::text AS "totalHours"
1559
+ FROM operations_project_assignment pa
1560
+ LEFT JOIN operations_timesheet_entry e
1561
+ ON e.project_assignment_id = pa.id
1562
+ AND e.deleted_at IS NULL
1563
+ LEFT JOIN operations_timesheet t
1564
+ ON t.id = e.timesheet_id
1565
+ AND t.deleted_at IS NULL
1566
+ WHERE pa.project_id = $1
1567
1567
  AND pa.deleted_at IS NULL`, [projectId]),
1568
- this.querySingle(`SELECT COUNT(*) FILTER (WHERE status IN ('planned', 'active'))::text AS "activeAssignments",
1569
- COUNT(*) FILTER (WHERE is_billable = true AND status IN ('planned', 'active'))::text AS "billableAssignments",
1570
- COALESCE(AVG(allocation_percent), 0)::text AS "averageAllocation",
1571
- COALESCE(SUM(weekly_hours), 0)::text AS "totalWeeklyHours"
1572
- FROM operations_project_assignment
1573
- WHERE project_id = $1
1568
+ this.querySingle(`SELECT COUNT(*) FILTER (WHERE status IN ('planned', 'active'))::text AS "activeAssignments",
1569
+ COUNT(*) FILTER (WHERE is_billable = true AND status IN ('planned', 'active'))::text AS "billableAssignments",
1570
+ COALESCE(AVG(allocation_percent), 0)::text AS "averageAllocation",
1571
+ COALESCE(SUM(weekly_hours), 0)::text AS "totalWeeklyHours"
1572
+ FROM operations_project_assignment
1573
+ WHERE project_id = $1
1574
1574
  AND deleted_at IS NULL`, [projectId]),
1575
1575
  ]);
1576
1576
  return Object.assign(Object.assign({}, project), { assignments,
@@ -1587,110 +1587,110 @@ let OperationsService = class OperationsService {
1587
1587
  }
1588
1588
  async getCollaboratorDetails(collaboratorId) {
1589
1589
  var _a, _b, _c, _d, _e, _f;
1590
- const collaborator = await this.querySingle(`SELECT c.id,
1591
- c.user_id AS "userId",
1592
- c.code,
1593
- c.collaborator_type AS "collaboratorType",
1594
- c.display_name AS "displayName",
1595
- c.department,
1596
- c.title,
1597
- c.level_label AS "levelLabel",
1598
- c.weekly_capacity_hours AS "weeklyCapacityHours",
1599
- c.status,
1600
- c.joined_at AS "joinedAt",
1601
- c.left_at AS "leftAt",
1602
- c.notes,
1603
- s.id AS "supervisorId",
1604
- s.display_name AS "supervisorName",
1605
- hiring_contract.id AS "contractId",
1606
- hiring_contract.status AS "contractStatus",
1607
- COUNT(DISTINCT pa.id)::int AS "activeAssignments"
1608
- FROM operations_collaborator c
1609
- LEFT JOIN operations_collaborator s
1610
- ON s.id = c.supervisor_collaborator_id
1611
- LEFT JOIN operations_project_assignment pa
1612
- ON pa.collaborator_id = c.id
1613
- AND pa.deleted_at IS NULL
1614
- AND pa.status IN ('planned', 'active')
1615
- LEFT JOIN LATERAL (
1616
- SELECT oc.id, oc.status
1617
- FROM operations_contract oc
1618
- WHERE oc.related_collaborator_id = c.id
1619
- AND oc.deleted_at IS NULL
1620
- ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
1621
- oc.created_at DESC
1622
- LIMIT 1
1623
- ) hiring_contract ON TRUE
1624
- WHERE c.id = $1
1625
- AND c.deleted_at IS NULL
1590
+ const collaborator = await this.querySingle(`SELECT c.id,
1591
+ c.user_id AS "userId",
1592
+ c.code,
1593
+ c.collaborator_type AS "collaboratorType",
1594
+ c.display_name AS "displayName",
1595
+ c.department,
1596
+ c.title,
1597
+ c.level_label AS "levelLabel",
1598
+ c.weekly_capacity_hours AS "weeklyCapacityHours",
1599
+ c.status,
1600
+ c.joined_at AS "joinedAt",
1601
+ c.left_at AS "leftAt",
1602
+ c.notes,
1603
+ s.id AS "supervisorId",
1604
+ s.display_name AS "supervisorName",
1605
+ hiring_contract.id AS "contractId",
1606
+ hiring_contract.status AS "contractStatus",
1607
+ COUNT(DISTINCT pa.id)::int AS "activeAssignments"
1608
+ FROM operations_collaborator c
1609
+ LEFT JOIN operations_collaborator s
1610
+ ON s.id = c.supervisor_collaborator_id
1611
+ LEFT JOIN operations_project_assignment pa
1612
+ ON pa.collaborator_id = c.id
1613
+ AND pa.deleted_at IS NULL
1614
+ AND pa.status IN ('planned', 'active')
1615
+ LEFT JOIN LATERAL (
1616
+ SELECT oc.id, oc.status
1617
+ FROM operations_contract oc
1618
+ WHERE oc.related_collaborator_id = c.id
1619
+ AND oc.deleted_at IS NULL
1620
+ ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
1621
+ oc.created_at DESC
1622
+ LIMIT 1
1623
+ ) hiring_contract ON TRUE
1624
+ WHERE c.id = $1
1625
+ AND c.deleted_at IS NULL
1626
1626
  GROUP BY c.id, s.id, hiring_contract.id, hiring_contract.status`, [collaboratorId]);
1627
1627
  if (!collaborator) {
1628
1628
  throw new common_1.NotFoundException('Collaborator not found.');
1629
1629
  }
1630
1630
  const [assignedProjects, relatedContracts, weeklySchedule, timesheetSummary, timeOffSummary, scheduleAdjustmentRequests] = await Promise.all([
1631
- this.queryRows(`SELECT p.id,
1632
- p.code,
1633
- p.name,
1634
- p.status,
1635
- pa.role_label AS "roleLabel",
1636
- pa.allocation_percent AS "allocationPercent",
1637
- pa.weekly_hours AS "weeklyHours",
1638
- pa.start_date AS "startDate",
1639
- pa.end_date AS "endDate"
1640
- FROM operations_project_assignment pa
1641
- JOIN operations_project p ON p.id = pa.project_id
1642
- WHERE pa.collaborator_id = $1
1643
- AND pa.deleted_at IS NULL
1644
- AND p.deleted_at IS NULL
1631
+ this.queryRows(`SELECT p.id,
1632
+ p.code,
1633
+ p.name,
1634
+ p.status,
1635
+ pa.role_label AS "roleLabel",
1636
+ pa.allocation_percent AS "allocationPercent",
1637
+ pa.weekly_hours AS "weeklyHours",
1638
+ pa.start_date AS "startDate",
1639
+ pa.end_date AS "endDate"
1640
+ FROM operations_project_assignment pa
1641
+ JOIN operations_project p ON p.id = pa.project_id
1642
+ WHERE pa.collaborator_id = $1
1643
+ AND pa.deleted_at IS NULL
1644
+ AND p.deleted_at IS NULL
1645
1645
  ORDER BY p.name ASC`, [collaboratorId]),
1646
- this.queryRows(`SELECT c.id,
1647
- c.code,
1648
- c.name,
1649
- c.contract_category AS "contractCategory",
1650
- c.client_name AS "clientName",
1651
- c.billing_model AS "billingModel",
1652
- c.start_date AS "startDate",
1653
- c.end_date AS "endDate",
1654
- c.budget_amount AS "budgetAmount",
1655
- c.monthly_hour_cap AS "monthlyHourCap",
1656
- c.status,
1657
- c.origin_type AS "originType",
1658
- c.origin_id AS "originId",
1659
- c.description
1660
- FROM operations_contract c
1661
- WHERE c.related_collaborator_id = $1
1662
- AND c.deleted_at IS NULL
1646
+ this.queryRows(`SELECT c.id,
1647
+ c.code,
1648
+ c.name,
1649
+ c.contract_category AS "contractCategory",
1650
+ c.client_name AS "clientName",
1651
+ c.billing_model AS "billingModel",
1652
+ c.start_date AS "startDate",
1653
+ c.end_date AS "endDate",
1654
+ c.budget_amount AS "budgetAmount",
1655
+ c.monthly_hour_cap AS "monthlyHourCap",
1656
+ c.status,
1657
+ c.origin_type AS "originType",
1658
+ c.origin_id AS "originId",
1659
+ c.description
1660
+ FROM operations_contract c
1661
+ WHERE c.related_collaborator_id = $1
1662
+ AND c.deleted_at IS NULL
1663
1663
  ORDER BY c.created_at DESC`, [collaboratorId]),
1664
- this.queryRows(`SELECT weekday,
1665
- is_working_day AS "isWorkingDay",
1666
- start_time AS "startTime",
1667
- end_time AS "endTime",
1668
- break_minutes AS "breakMinutes"
1669
- FROM operations_collaborator_schedule_day
1670
- WHERE collaborator_id = $1
1671
- AND deleted_at IS NULL
1664
+ this.queryRows(`SELECT weekday,
1665
+ is_working_day AS "isWorkingDay",
1666
+ start_time AS "startTime",
1667
+ end_time AS "endTime",
1668
+ break_minutes AS "breakMinutes"
1669
+ FROM operations_collaborator_schedule_day
1670
+ WHERE collaborator_id = $1
1671
+ AND deleted_at IS NULL
1672
1672
  ORDER BY id ASC`, [collaboratorId]),
1673
- this.querySingle(`SELECT COUNT(*)::text AS "totalTimesheets",
1674
- COUNT(*) FILTER (WHERE status = 'submitted')::text AS "pendingTimesheets",
1675
- COALESCE(SUM(total_hours), 0)::text AS "totalHours"
1676
- FROM operations_timesheet
1677
- WHERE collaborator_id = $1
1673
+ this.querySingle(`SELECT COUNT(*)::text AS "totalTimesheets",
1674
+ COUNT(*) FILTER (WHERE status = 'submitted')::text AS "pendingTimesheets",
1675
+ COALESCE(SUM(total_hours), 0)::text AS "totalHours"
1676
+ FROM operations_timesheet
1677
+ WHERE collaborator_id = $1
1678
1678
  AND deleted_at IS NULL`, [collaboratorId]),
1679
- this.querySingle(`SELECT COUNT(*)::text AS "totalRequests",
1680
- COUNT(*) FILTER (WHERE status = 'submitted')::text AS "pendingRequests",
1681
- COUNT(*) FILTER (WHERE status = 'approved')::text AS "approvedRequests"
1682
- FROM operations_time_off_request
1683
- WHERE collaborator_id = $1
1679
+ this.querySingle(`SELECT COUNT(*)::text AS "totalRequests",
1680
+ COUNT(*) FILTER (WHERE status = 'submitted')::text AS "pendingRequests",
1681
+ COUNT(*) FILTER (WHERE status = 'approved')::text AS "approvedRequests"
1682
+ FROM operations_time_off_request
1683
+ WHERE collaborator_id = $1
1684
1684
  AND deleted_at IS NULL`, [collaboratorId]),
1685
- this.queryRows(`SELECT id,
1686
- request_scope AS "requestScope",
1687
- effective_start_date AS "effectiveStartDate",
1688
- effective_end_date AS "effectiveEndDate",
1689
- status,
1690
- reason
1691
- FROM operations_schedule_adjustment_request
1692
- WHERE collaborator_id = $1
1693
- AND deleted_at IS NULL
1685
+ this.queryRows(`SELECT id,
1686
+ request_scope AS "requestScope",
1687
+ effective_start_date AS "effectiveStartDate",
1688
+ effective_end_date AS "effectiveEndDate",
1689
+ status,
1690
+ reason
1691
+ FROM operations_schedule_adjustment_request
1692
+ WHERE collaborator_id = $1
1693
+ AND deleted_at IS NULL
1694
1694
  ORDER BY effective_start_date DESC, id DESC`, [collaboratorId]),
1695
1695
  ]);
1696
1696
  return Object.assign(Object.assign({}, collaborator), { assignedProjects,
@@ -1706,28 +1706,28 @@ let OperationsService = class OperationsService {
1706
1706
  }, scheduleAdjustmentRequests });
1707
1707
  }
1708
1708
  async getDirectReportIds(collaboratorId) {
1709
- return (await this.queryRows(`SELECT id
1710
- FROM operations_collaborator
1711
- WHERE supervisor_collaborator_id = $1
1712
- AND deleted_at IS NULL
1709
+ return (await this.queryRows(`SELECT id
1710
+ FROM operations_collaborator
1711
+ WHERE supervisor_collaborator_id = $1
1712
+ AND deleted_at IS NULL
1713
1713
  ORDER BY id ASC`, [collaboratorId])).map((row) => row.id);
1714
1714
  }
1715
1715
  async getAssignedProjectIds(collaboratorIds) {
1716
1716
  if (!collaboratorIds.length)
1717
1717
  return [];
1718
- return (await this.queryRows(`SELECT DISTINCT project_id AS "projectId"
1719
- FROM operations_project_assignment
1720
- WHERE deleted_at IS NULL
1721
- AND status IN ('planned', 'active')
1718
+ return (await this.queryRows(`SELECT DISTINCT project_id AS "projectId"
1719
+ FROM operations_project_assignment
1720
+ WHERE deleted_at IS NULL
1721
+ AND status IN ('planned', 'active')
1722
1722
  AND collaborator_id = ANY($1::int[])`, [collaboratorIds])).map((row) => row.projectId);
1723
1723
  }
1724
1724
  async getTimesheetById(timesheetId) {
1725
- const timesheet = await this.querySingle(`SELECT id,
1726
- collaborator_id AS "collaboratorId",
1727
- approver_collaborator_id AS "approverCollaboratorId",
1728
- status
1729
- FROM operations_timesheet
1730
- WHERE id = $1
1725
+ const timesheet = await this.querySingle(`SELECT id,
1726
+ collaborator_id AS "collaboratorId",
1727
+ approver_collaborator_id AS "approverCollaboratorId",
1728
+ status
1729
+ FROM operations_timesheet
1730
+ WHERE id = $1
1731
1731
  AND deleted_at IS NULL`, [timesheetId]);
1732
1732
  if (!timesheet) {
1733
1733
  throw new common_1.NotFoundException('Timesheet not found.');
@@ -1736,9 +1736,9 @@ let OperationsService = class OperationsService {
1736
1736
  }
1737
1737
  async replaceTimesheetEntries(client, timesheetId, entries, collaboratorId) {
1738
1738
  var _a, _b, _c;
1739
- await client.$executeRawUnsafe(`UPDATE operations_timesheet_entry
1740
- SET deleted_at = NOW()
1741
- WHERE timesheet_id = $1
1739
+ await client.$executeRawUnsafe(`UPDATE operations_timesheet_entry
1740
+ SET deleted_at = NOW()
1741
+ WHERE timesheet_id = $1
1742
1742
  AND deleted_at IS NULL`, timesheetId);
1743
1743
  if (!entries.length)
1744
1744
  return;
@@ -1746,9 +1746,9 @@ let OperationsService = class OperationsService {
1746
1746
  .map((entry) => entry.projectAssignmentId)
1747
1747
  .filter((value) => typeof value === 'number');
1748
1748
  if (assignmentIds.length) {
1749
- const assignments = (await client.$queryRawUnsafe(`SELECT id, collaborator_id AS "collaboratorId"
1750
- FROM operations_project_assignment
1751
- WHERE id = ANY($1::int[])
1749
+ const assignments = (await client.$queryRawUnsafe(`SELECT id, collaborator_id AS "collaboratorId"
1750
+ FROM operations_project_assignment
1751
+ WHERE id = ANY($1::int[])
1752
1752
  AND deleted_at IS NULL`, assignmentIds));
1753
1753
  const assignmentMap = new Map(assignments.map((assignment) => [
1754
1754
  assignment.id,
@@ -1761,73 +1761,73 @@ let OperationsService = class OperationsService {
1761
1761
  }
1762
1762
  }
1763
1763
  for (const entry of entries) {
1764
- await client.$executeRawUnsafe(`INSERT INTO operations_timesheet_entry (
1765
- timesheet_id,
1766
- project_assignment_id,
1767
- activity_label,
1768
- work_date,
1769
- hours,
1770
- description,
1771
- created_at,
1772
- updated_at
1764
+ await client.$executeRawUnsafe(`INSERT INTO operations_timesheet_entry (
1765
+ timesheet_id,
1766
+ project_assignment_id,
1767
+ activity_label,
1768
+ work_date,
1769
+ hours,
1770
+ description,
1771
+ created_at,
1772
+ updated_at
1773
1773
  ) VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())`, timesheetId, (_a = entry.projectAssignmentId) !== null && _a !== void 0 ? _a : null, (_b = entry.activityLabel) !== null && _b !== void 0 ? _b : null, entry.workDate, entry.hours, (_c = entry.description) !== null && _c !== void 0 ? _c : null);
1774
1774
  }
1775
1775
  }
1776
1776
  async refreshTimesheetTotal(client, timesheetId) {
1777
- await client.$executeRawUnsafe(`UPDATE operations_timesheet
1778
- SET total_hours = (
1779
- SELECT COALESCE(SUM(hours), 0)
1780
- FROM operations_timesheet_entry
1781
- WHERE timesheet_id = $1
1782
- AND deleted_at IS NULL
1783
- ),
1784
- updated_at = NOW()
1777
+ await client.$executeRawUnsafe(`UPDATE operations_timesheet
1778
+ SET total_hours = (
1779
+ SELECT COALESCE(SUM(hours), 0)
1780
+ FROM operations_timesheet_entry
1781
+ WHERE timesheet_id = $1
1782
+ AND deleted_at IS NULL
1783
+ ),
1784
+ updated_at = NOW()
1785
1785
  WHERE id = $1`, timesheetId);
1786
1786
  }
1787
1787
  async upsertApproval(client, input) {
1788
1788
  var _a, _b;
1789
- const existing = (await client.$queryRawUnsafe(`SELECT id
1790
- FROM operations_approval
1791
- WHERE target_type = $1
1792
- AND target_id = $2
1789
+ const existing = (await client.$queryRawUnsafe(`SELECT id
1790
+ FROM operations_approval
1791
+ WHERE target_type = $1
1792
+ AND target_id = $2
1793
1793
  AND deleted_at IS NULL`, input.targetType, input.targetId));
1794
1794
  let approvalId = (_a = existing[0]) === null || _a === void 0 ? void 0 : _a.id;
1795
1795
  if (!approvalId) {
1796
- const created = (await client.$queryRawUnsafe(`INSERT INTO operations_approval (
1797
- target_type,
1798
- target_id,
1799
- requester_collaborator_id,
1800
- approver_collaborator_id,
1801
- status,
1802
- submitted_at,
1803
- created_at,
1804
- updated_at
1805
- ) VALUES ($1, $2, $3, $4, 'pending', NOW(), NOW(), NOW())
1796
+ const created = (await client.$queryRawUnsafe(`INSERT INTO operations_approval (
1797
+ target_type,
1798
+ target_id,
1799
+ requester_collaborator_id,
1800
+ approver_collaborator_id,
1801
+ status,
1802
+ submitted_at,
1803
+ created_at,
1804
+ updated_at
1805
+ ) VALUES ($1, $2, $3, $4, 'pending', NOW(), NOW(), NOW())
1806
1806
  RETURNING id`, input.targetType, input.targetId, input.requesterCollaboratorId, input.approverCollaboratorId));
1807
1807
  approvalId = (_b = created[0]) === null || _b === void 0 ? void 0 : _b.id;
1808
1808
  await this.insertApprovalHistory(client, approvalId, input.requesterCollaboratorId, 'created', null);
1809
1809
  }
1810
1810
  else {
1811
- await client.$executeRawUnsafe(`UPDATE operations_approval
1812
- SET requester_collaborator_id = $1,
1813
- approver_collaborator_id = $2,
1814
- status = 'pending',
1815
- submitted_at = NOW(),
1816
- decided_at = NULL,
1817
- decision_note = NULL,
1818
- updated_at = NOW()
1811
+ await client.$executeRawUnsafe(`UPDATE operations_approval
1812
+ SET requester_collaborator_id = $1,
1813
+ approver_collaborator_id = $2,
1814
+ status = 'pending',
1815
+ submitted_at = NOW(),
1816
+ decided_at = NULL,
1817
+ decision_note = NULL,
1818
+ updated_at = NOW()
1819
1819
  WHERE id = $3`, input.requesterCollaboratorId, input.approverCollaboratorId, approvalId);
1820
1820
  await this.insertApprovalHistory(client, approvalId, input.requesterCollaboratorId, 'reopened', null);
1821
1821
  }
1822
1822
  await this.insertApprovalHistory(client, approvalId, input.requesterCollaboratorId, 'submitted', null);
1823
1823
  }
1824
1824
  async insertApprovalHistory(client, approvalId, actorCollaboratorId, action, note) {
1825
- await client.$executeRawUnsafe(`INSERT INTO operations_approval_history (
1826
- approval_id,
1827
- actor_collaborator_id,
1828
- action,
1829
- note,
1830
- created_at
1825
+ await client.$executeRawUnsafe(`INSERT INTO operations_approval_history (
1826
+ approval_id,
1827
+ actor_collaborator_id,
1828
+ action,
1829
+ note,
1830
+ created_at
1831
1831
  ) VALUES ($1, $2, $3, $4, NOW())`, approvalId, actorCollaboratorId, action, note);
1832
1832
  }
1833
1833
  async assertProjectAccess(actor, projectId) {
@@ -1926,46 +1926,46 @@ let OperationsService = class OperationsService {
1926
1926
  const schedule = (weeklySchedule === null || weeklySchedule === void 0 ? void 0 : weeklySchedule.length)
1927
1927
  ? weeklySchedule
1928
1928
  : this.defaultWeeklySchedule();
1929
- await client.$executeRawUnsafe(`UPDATE operations_collaborator_schedule_day
1930
- SET deleted_at = NOW(),
1931
- updated_at = NOW()
1932
- WHERE collaborator_id = $1
1929
+ await client.$executeRawUnsafe(`UPDATE operations_collaborator_schedule_day
1930
+ SET deleted_at = NOW(),
1931
+ updated_at = NOW()
1932
+ WHERE collaborator_id = $1
1933
1933
  AND deleted_at IS NULL`, collaboratorId);
1934
1934
  for (const day of schedule) {
1935
- await client.$executeRawUnsafe(`INSERT INTO operations_collaborator_schedule_day (
1936
- collaborator_id,
1937
- weekday,
1938
- is_working_day,
1939
- start_time,
1940
- end_time,
1941
- break_minutes,
1942
- created_at,
1943
- updated_at
1935
+ await client.$executeRawUnsafe(`INSERT INTO operations_collaborator_schedule_day (
1936
+ collaborator_id,
1937
+ weekday,
1938
+ is_working_day,
1939
+ start_time,
1940
+ end_time,
1941
+ break_minutes,
1942
+ created_at,
1943
+ updated_at
1944
1944
  ) VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())`, collaboratorId, day.weekday, (_a = day.isWorkingDay) !== null && _a !== void 0 ? _a : true, day.isWorkingDay === false ? null : (_b = day.startTime) !== null && _b !== void 0 ? _b : null, day.isWorkingDay === false ? null : (_c = day.endTime) !== null && _c !== void 0 ? _c : null, (_d = day.breakMinutes) !== null && _d !== void 0 ? _d : null);
1945
1945
  }
1946
1946
  }
1947
1947
  async replaceProjectAssignments(client, projectId, teamAssignments) {
1948
1948
  var _a, _b, _c, _d, _e, _f, _g;
1949
- await client.$executeRawUnsafe(`UPDATE operations_project_assignment
1950
- SET deleted_at = NOW(),
1951
- updated_at = NOW()
1952
- WHERE project_id = $1
1949
+ await client.$executeRawUnsafe(`UPDATE operations_project_assignment
1950
+ SET deleted_at = NOW(),
1951
+ updated_at = NOW()
1952
+ WHERE project_id = $1
1953
1953
  AND deleted_at IS NULL`, projectId);
1954
1954
  for (const assignment of teamAssignments !== null && teamAssignments !== void 0 ? teamAssignments : []) {
1955
- await client.$executeRawUnsafe(`INSERT INTO operations_project_assignment (
1956
- project_id,
1957
- collaborator_id,
1958
- role_label,
1959
- allocation_percent,
1960
- weekly_hours,
1961
- is_billable,
1962
- start_date,
1963
- end_date,
1964
- status,
1965
- created_at,
1966
- updated_at
1967
- ) VALUES (
1968
- $1, $2, $3, $4, $5, $6, $7, $8, COALESCE($9, 'active'), NOW(), NOW()
1955
+ await client.$executeRawUnsafe(`INSERT INTO operations_project_assignment (
1956
+ project_id,
1957
+ collaborator_id,
1958
+ role_label,
1959
+ allocation_percent,
1960
+ weekly_hours,
1961
+ is_billable,
1962
+ start_date,
1963
+ end_date,
1964
+ status,
1965
+ created_at,
1966
+ updated_at
1967
+ ) VALUES (
1968
+ $1, $2, $3, $4, $5, $6, $7, $8, COALESCE($9, 'active'), NOW(), NOW()
1969
1969
  )`, projectId, assignment.collaboratorId, (_a = assignment.roleLabel) !== null && _a !== void 0 ? _a : 'Team Member', (_b = assignment.allocationPercent) !== null && _b !== void 0 ? _b : null, (_c = assignment.weeklyHours) !== null && _c !== void 0 ? _c : null, (_d = assignment.isBillable) !== null && _d !== void 0 ? _d : true, (_e = assignment.startDate) !== null && _e !== void 0 ? _e : null, (_f = assignment.endDate) !== null && _f !== void 0 ? _f : null, (_g = assignment.status) !== null && _g !== void 0 ? _g : 'active');
1970
1970
  }
1971
1971
  }
@@ -2005,197 +2005,197 @@ let OperationsService = class OperationsService {
2005
2005
  const contractName = input.collaboratorType === 'clt'
2006
2006
  ? `${input.displayName} Employment Contract`
2007
2007
  : `${input.displayName} Service Contract`;
2008
- await client.$executeRawUnsafe(`INSERT INTO operations_contract (
2009
- code,
2010
- name,
2011
- contract_category,
2012
- contract_type,
2013
- client_name,
2014
- signature_status,
2015
- is_active,
2016
- billing_model,
2017
- account_manager_collaborator_id,
2018
- related_collaborator_id,
2019
- origin_type,
2020
- origin_id,
2021
- start_date,
2022
- end_date,
2023
- signed_at,
2024
- effective_date,
2025
- budget_amount,
2026
- monthly_hour_cap,
2027
- status,
2028
- description,
2029
- content_html,
2030
- created_by_user_id,
2031
- updated_by_user_id,
2032
- created_at,
2033
- updated_at
2034
- ) VALUES (
2035
- $1, $2, $3, $4, $5, 'not_started', true, $6, $7, $8, 'employee_hiring', $9, $10, NULL,
2036
- NULL, $10, $11, $12, 'draft', $13, NULL, $14, $14, NOW(), NOW()
2008
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract (
2009
+ code,
2010
+ name,
2011
+ contract_category,
2012
+ contract_type,
2013
+ client_name,
2014
+ signature_status,
2015
+ is_active,
2016
+ billing_model,
2017
+ account_manager_collaborator_id,
2018
+ related_collaborator_id,
2019
+ origin_type,
2020
+ origin_id,
2021
+ start_date,
2022
+ end_date,
2023
+ signed_at,
2024
+ effective_date,
2025
+ budget_amount,
2026
+ monthly_hour_cap,
2027
+ status,
2028
+ description,
2029
+ content_html,
2030
+ created_by_user_id,
2031
+ updated_by_user_id,
2032
+ created_at,
2033
+ updated_at
2034
+ ) VALUES (
2035
+ $1, $2, $3, $4, $5, 'not_started', true, $6, $7, $8, 'employee_hiring', $9, $10, NULL,
2036
+ NULL, $10, $11, $12, 'draft', $13, NULL, $14, $14, NOW(), NOW()
2037
2037
  )`, contractCode, contractName, this.mapContractCategoryForCollaboratorType(input.collaboratorType), this.mapContractTypeForCollaboratorType(input.collaboratorType), input.displayName, this.mapBillingModelForCollaboratorType(input.collaboratorType), input.supervisorCollaboratorId, input.collaboratorId, input.collaboratorId, (_a = input.startDate) !== null && _a !== void 0 ? _a : new Date().toISOString().slice(0, 10), (_b = input.compensationAmount) !== null && _b !== void 0 ? _b : null, input.weeklyCapacityHours
2038
2038
  ? Math.round(Number(input.weeklyCapacityHours) * 4)
2039
2039
  : null, (_c = input.description) !== null && _c !== void 0 ? _c : null, createdByUserId);
2040
2040
  }
2041
2041
  async createProjectContractDraft(client, createdByUserId, input) {
2042
2042
  var _a, _b, _c, _d, _e, _f, _g, _h;
2043
- const created = await client.$queryRawUnsafe(`INSERT INTO operations_contract (
2044
- code,
2045
- name,
2046
- contract_category,
2047
- contract_type,
2048
- client_name,
2049
- signature_status,
2050
- is_active,
2051
- billing_model,
2052
- account_manager_collaborator_id,
2053
- related_collaborator_id,
2054
- origin_type,
2055
- origin_id,
2056
- start_date,
2057
- end_date,
2058
- signed_at,
2059
- effective_date,
2060
- budget_amount,
2061
- monthly_hour_cap,
2062
- status,
2063
- description,
2064
- content_html,
2065
- created_by_user_id,
2066
- updated_by_user_id,
2067
- created_at,
2068
- updated_at
2069
- ) VALUES (
2070
- $1, $2, 'client', 'service_agreement', $3, 'not_started', true, $4, $5, NULL, 'client_project', $6,
2071
- $7, $8, NULL, $7, $9, $10, 'draft', $11, NULL, $12, $12, NOW(), NOW()
2072
- )
2043
+ const created = await client.$queryRawUnsafe(`INSERT INTO operations_contract (
2044
+ code,
2045
+ name,
2046
+ contract_category,
2047
+ contract_type,
2048
+ client_name,
2049
+ signature_status,
2050
+ is_active,
2051
+ billing_model,
2052
+ account_manager_collaborator_id,
2053
+ related_collaborator_id,
2054
+ origin_type,
2055
+ origin_id,
2056
+ start_date,
2057
+ end_date,
2058
+ signed_at,
2059
+ effective_date,
2060
+ budget_amount,
2061
+ monthly_hour_cap,
2062
+ status,
2063
+ description,
2064
+ content_html,
2065
+ created_by_user_id,
2066
+ updated_by_user_id,
2067
+ created_at,
2068
+ updated_at
2069
+ ) VALUES (
2070
+ $1, $2, 'client', 'service_agreement', $3, 'not_started', true, $4, $5, NULL, 'client_project', $6,
2071
+ $7, $8, NULL, $7, $9, $10, 'draft', $11, NULL, $12, $12, NOW(), NOW()
2072
+ )
2073
2073
  RETURNING id`, (_a = input.contractCode) !== null && _a !== void 0 ? _a : `PRJ-${input.projectCode}`, (_b = input.contractName) !== null && _b !== void 0 ? _b : `${input.projectName} Service Agreement`, input.clientName, input.billingModel, input.managerCollaboratorId, input.projectId, (_c = input.startDate) !== null && _c !== void 0 ? _c : new Date().toISOString().slice(0, 10), (_d = input.endDate) !== null && _d !== void 0 ? _d : null, (_e = input.budgetAmount) !== null && _e !== void 0 ? _e : null, (_f = input.monthlyHourCap) !== null && _f !== void 0 ? _f : null, (_g = input.description) !== null && _g !== void 0 ? _g : null, createdByUserId);
2074
2074
  return (_h = created[0]) === null || _h === void 0 ? void 0 : _h.id;
2075
2075
  }
2076
2076
  async replaceContractParties(client, contractId, parties) {
2077
2077
  var _a, _b, _c, _d, _e, _f;
2078
- await client.$executeRawUnsafe(`UPDATE operations_contract_party
2079
- SET deleted_at = NOW(),
2080
- updated_at = NOW()
2081
- WHERE contract_id = $1
2078
+ await client.$executeRawUnsafe(`UPDATE operations_contract_party
2079
+ SET deleted_at = NOW(),
2080
+ updated_at = NOW()
2081
+ WHERE contract_id = $1
2082
2082
  AND deleted_at IS NULL`, contractId);
2083
2083
  for (const party of parties !== null && parties !== void 0 ? parties : []) {
2084
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_party (
2085
- contract_id,
2086
- party_role,
2087
- party_type,
2088
- display_name,
2089
- document_number,
2090
- email,
2091
- phone,
2092
- is_primary,
2093
- created_at,
2094
- updated_at
2095
- ) VALUES (
2096
- $1, COALESCE($2, 'other'), COALESCE($3, 'company'), $4, $5, $6, $7, COALESCE($8, false), NOW(), NOW()
2084
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract_party (
2085
+ contract_id,
2086
+ party_role,
2087
+ party_type,
2088
+ display_name,
2089
+ document_number,
2090
+ email,
2091
+ phone,
2092
+ is_primary,
2093
+ created_at,
2094
+ updated_at
2095
+ ) VALUES (
2096
+ $1, COALESCE($2, 'other'), COALESCE($3, 'company'), $4, $5, $6, $7, COALESCE($8, false), NOW(), NOW()
2097
2097
  )`, contractId, (_a = party.partyRole) !== null && _a !== void 0 ? _a : 'other', (_b = party.partyType) !== null && _b !== void 0 ? _b : 'company', party.displayName, (_c = party.documentNumber) !== null && _c !== void 0 ? _c : null, (_d = party.email) !== null && _d !== void 0 ? _d : null, (_e = party.phone) !== null && _e !== void 0 ? _e : null, (_f = party.isPrimary) !== null && _f !== void 0 ? _f : false);
2098
2098
  }
2099
2099
  }
2100
2100
  async replaceContractSignatures(client, contractId, signatures) {
2101
2101
  var _a, _b, _c, _d;
2102
- await client.$executeRawUnsafe(`UPDATE operations_contract_signature
2103
- SET deleted_at = NOW(),
2104
- updated_at = NOW()
2105
- WHERE contract_id = $1
2102
+ await client.$executeRawUnsafe(`UPDATE operations_contract_signature
2103
+ SET deleted_at = NOW(),
2104
+ updated_at = NOW()
2105
+ WHERE contract_id = $1
2106
2106
  AND deleted_at IS NULL`, contractId);
2107
2107
  for (const signature of signatures !== null && signatures !== void 0 ? signatures : []) {
2108
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_signature (
2109
- contract_id,
2110
- signer_name,
2111
- signer_role,
2112
- signer_email,
2113
- signer_status,
2114
- signed_at,
2115
- created_at,
2116
- updated_at
2117
- ) VALUES (
2118
- $1, $2, $3, $4, COALESCE($5, 'pending'), $6, NOW(), NOW()
2108
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract_signature (
2109
+ contract_id,
2110
+ signer_name,
2111
+ signer_role,
2112
+ signer_email,
2113
+ signer_status,
2114
+ signed_at,
2115
+ created_at,
2116
+ updated_at
2117
+ ) VALUES (
2118
+ $1, $2, $3, $4, COALESCE($5, 'pending'), $6, NOW(), NOW()
2119
2119
  )`, contractId, signature.signerName, (_a = signature.signerRole) !== null && _a !== void 0 ? _a : null, (_b = signature.signerEmail) !== null && _b !== void 0 ? _b : null, (_c = signature.status) !== null && _c !== void 0 ? _c : 'pending', (_d = signature.signedAt) !== null && _d !== void 0 ? _d : null);
2120
2120
  }
2121
2121
  }
2122
2122
  async replaceContractFinancialTerms(client, contractId, financialTerms) {
2123
2123
  var _a, _b, _c, _d;
2124
- await client.$executeRawUnsafe(`UPDATE operations_contract_financial_term
2125
- SET deleted_at = NOW(),
2126
- updated_at = NOW()
2127
- WHERE contract_id = $1
2124
+ await client.$executeRawUnsafe(`UPDATE operations_contract_financial_term
2125
+ SET deleted_at = NOW(),
2126
+ updated_at = NOW()
2127
+ WHERE contract_id = $1
2128
2128
  AND deleted_at IS NULL`, contractId);
2129
2129
  for (const term of financialTerms !== null && financialTerms !== void 0 ? financialTerms : []) {
2130
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_financial_term (
2131
- contract_id,
2132
- term_type,
2133
- label,
2134
- amount,
2135
- recurrence,
2136
- due_day,
2137
- notes,
2138
- created_at,
2139
- updated_at
2140
- ) VALUES (
2141
- $1, COALESCE($2, 'value'), $3, $4, COALESCE($5, 'one_time'), $6, $7, NOW(), NOW()
2130
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract_financial_term (
2131
+ contract_id,
2132
+ term_type,
2133
+ label,
2134
+ amount,
2135
+ recurrence,
2136
+ due_day,
2137
+ notes,
2138
+ created_at,
2139
+ updated_at
2140
+ ) VALUES (
2141
+ $1, COALESCE($2, 'value'), $3, $4, COALESCE($5, 'one_time'), $6, $7, NOW(), NOW()
2142
2142
  )`, contractId, (_a = term.termType) !== null && _a !== void 0 ? _a : 'value', term.label, term.amount, (_b = term.recurrence) !== null && _b !== void 0 ? _b : 'one_time', (_c = term.dueDay) !== null && _c !== void 0 ? _c : null, (_d = term.notes) !== null && _d !== void 0 ? _d : null);
2143
2143
  }
2144
2144
  }
2145
2145
  async replaceContractRevisions(client, contractId, revisions) {
2146
2146
  var _a, _b, _c, _d;
2147
- await client.$executeRawUnsafe(`UPDATE operations_contract_revision
2148
- SET deleted_at = NOW(),
2149
- updated_at = NOW()
2150
- WHERE contract_id = $1
2147
+ await client.$executeRawUnsafe(`UPDATE operations_contract_revision
2148
+ SET deleted_at = NOW(),
2149
+ updated_at = NOW()
2150
+ WHERE contract_id = $1
2151
2151
  AND deleted_at IS NULL`, contractId);
2152
2152
  for (const revision of revisions !== null && revisions !== void 0 ? revisions : []) {
2153
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_revision (
2154
- contract_id,
2155
- revision_type,
2156
- title,
2157
- effective_date,
2158
- status,
2159
- summary,
2160
- created_at,
2161
- updated_at
2162
- ) VALUES (
2163
- $1, COALESCE($2, 'revision'), $3, $4, COALESCE($5, 'draft'), $6, NOW(), NOW()
2153
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract_revision (
2154
+ contract_id,
2155
+ revision_type,
2156
+ title,
2157
+ effective_date,
2158
+ status,
2159
+ summary,
2160
+ created_at,
2161
+ updated_at
2162
+ ) VALUES (
2163
+ $1, COALESCE($2, 'revision'), $3, $4, COALESCE($5, 'draft'), $6, NOW(), NOW()
2164
2164
  )`, contractId, (_a = revision.revisionType) !== null && _a !== void 0 ? _a : 'revision', revision.title, (_b = revision.effectiveDate) !== null && _b !== void 0 ? _b : null, (_c = revision.status) !== null && _c !== void 0 ? _c : 'draft', (_d = revision.summary) !== null && _d !== void 0 ? _d : null);
2165
2165
  }
2166
2166
  }
2167
2167
  async replaceContractPdfDocument(client, contractId, document) {
2168
2168
  var _a;
2169
- await client.$executeRawUnsafe(`UPDATE operations_contract_document
2170
- SET is_current = false,
2171
- updated_at = NOW()
2172
- WHERE contract_id = $1
2173
- AND deleted_at IS NULL
2169
+ await client.$executeRawUnsafe(`UPDATE operations_contract_document
2170
+ SET is_current = false,
2171
+ updated_at = NOW()
2172
+ WHERE contract_id = $1
2173
+ AND deleted_at IS NULL
2174
2174
  AND document_type IN ('uploaded_pdf', 'generated_pdf')`, contractId);
2175
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_document (
2176
- contract_id,
2177
- document_type,
2178
- file_name,
2179
- mime_type,
2180
- file_content_base64,
2181
- is_current,
2182
- notes,
2183
- created_at,
2184
- updated_at
2185
- ) VALUES (
2186
- $1, 'uploaded_pdf', $2, $3, $4, true, $5, NOW(), NOW()
2175
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract_document (
2176
+ contract_id,
2177
+ document_type,
2178
+ file_name,
2179
+ mime_type,
2180
+ file_content_base64,
2181
+ is_current,
2182
+ notes,
2183
+ created_at,
2184
+ updated_at
2185
+ ) VALUES (
2186
+ $1, 'uploaded_pdf', $2, $3, $4, true, $5, NOW(), NOW()
2187
2187
  )`, contractId, document.fileName, document.mimeType, document.fileContentBase64, (_a = document.notes) !== null && _a !== void 0 ? _a : null);
2188
2188
  }
2189
2189
  async insertContractHistory(client, contractId, actorUserId, action, note, metadataJson) {
2190
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_history (
2191
- contract_id,
2192
- actor_user_id,
2193
- action,
2194
- note,
2195
- metadata_json,
2196
- created_at
2197
- ) VALUES (
2198
- $1, $2, $3, $4, $5, NOW()
2190
+ await client.$executeRawUnsafe(`INSERT INTO operations_contract_history (
2191
+ contract_id,
2192
+ actor_user_id,
2193
+ action,
2194
+ note,
2195
+ metadata_json,
2196
+ created_at
2197
+ ) VALUES (
2198
+ $1, $2, $3, $4, $5, NOW()
2199
2199
  )`, contractId, actorUserId, action, note, metadataJson !== null && metadataJson !== void 0 ? metadataJson : null);
2200
2200
  }
2201
2201
  requireFields(input, required) {