@hed-hog/operations 0.0.304 → 0.0.306

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 (81) hide show
  1. package/dist/controllers/operations-projects.controller.d.ts +15 -0
  2. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-tasks.controller.d.ts +41 -10
  4. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  5. package/dist/controllers/operations-tasks.controller.js +11 -0
  6. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  7. package/dist/controllers/operations-timesheets.controller.d.ts +21 -0
  8. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  9. package/dist/controllers/operations-timesheets.controller.js +12 -0
  10. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  11. package/dist/dto/create-task.dto.d.ts +7 -1
  12. package/dist/dto/create-task.dto.d.ts.map +1 -1
  13. package/dist/dto/create-task.dto.js +38 -5
  14. package/dist/dto/create-task.dto.js.map +1 -1
  15. package/dist/dto/list-tasks.dto.d.ts +1 -1
  16. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  17. package/dist/dto/list-tasks.dto.js +2 -2
  18. package/dist/dto/list-tasks.dto.js.map +1 -1
  19. package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
  20. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
  21. package/dist/dto/update-collaborator-type.dto.js +2 -1
  22. package/dist/dto/update-collaborator-type.dto.js.map +1 -1
  23. package/dist/dto/update-task.dto.d.ts +7 -1
  24. package/dist/dto/update-task.dto.d.ts.map +1 -1
  25. package/dist/dto/update-task.dto.js +38 -5
  26. package/dist/dto/update-task.dto.js.map +1 -1
  27. package/dist/operations.service.d.ts +90 -12
  28. package/dist/operations.service.d.ts.map +1 -1
  29. package/dist/operations.service.js +560 -148
  30. package/dist/operations.service.js.map +1 -1
  31. package/dist/operations.service.spec.js +73 -0
  32. package/dist/operations.service.spec.js.map +1 -1
  33. package/hedhog/data/menu.yaml +26 -26
  34. package/hedhog/data/operations_collaborator_type.yaml +76 -76
  35. package/hedhog/data/route.yaml +26 -0
  36. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
  37. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +44 -44
  38. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +168 -213
  39. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  40. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  41. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  42. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  43. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  44. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  45. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +528 -403
  46. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  47. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  48. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
  49. package/hedhog/frontend/app/_lib/types.ts.ejs +5 -0
  50. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +7 -7
  51. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  52. package/hedhog/frontend/app/approvals/page.tsx.ejs +2 -2
  53. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +513 -502
  54. package/hedhog/frontend/app/collaborators/page.tsx.ejs +10 -7
  55. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  56. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  57. package/hedhog/frontend/app/projects/page.tsx.ejs +360 -133
  58. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +235 -72
  59. package/hedhog/frontend/app/timesheets/page.tsx.ejs +344 -134
  60. package/hedhog/frontend/messages/en.json +32 -4
  61. package/hedhog/frontend/messages/pt.json +34 -6
  62. package/hedhog/table/operations_collaborator.yaml +18 -18
  63. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
  64. package/hedhog/table/operations_collaborator_type.yaml +33 -33
  65. package/hedhog/table/operations_contract_document.yaml +33 -33
  66. package/hedhog/table/operations_project.yaml +9 -0
  67. package/hedhog/table/operations_task.yaml +43 -4
  68. package/package.json +6 -6
  69. package/src/controllers/operations-tasks.controller.ts +11 -0
  70. package/src/controllers/operations-timesheets.controller.ts +13 -0
  71. package/src/dto/create-collaborator-type.dto.ts +43 -43
  72. package/src/dto/create-collaborator.dto.ts +223 -223
  73. package/src/dto/create-task.dto.ts +47 -7
  74. package/src/dto/list-collaborator-types.dto.ts +15 -15
  75. package/src/dto/list-collaborators.dto.ts +30 -30
  76. package/src/dto/list-tasks.dto.ts +3 -3
  77. package/src/dto/update-collaborator-type.dto.ts +4 -3
  78. package/src/dto/update-collaborator.dto.ts +3 -3
  79. package/src/dto/update-task.dto.ts +47 -7
  80. package/src/operations.service.spec.ts +96 -0
  81. package/src/operations.service.ts +813 -135
@@ -107,7 +107,7 @@ const FINANCIAL_TERM_TYPE_VALUES = ['value', 'payment', 'revenue', 'fine', 'othe
107
107
  const RECURRENCE_VALUES = ['one_time', 'monthly', 'quarterly', 'yearly', 'other'];
108
108
  const REVISION_TYPE_VALUES = ['amendment', 'renewal', 'revision', 'addendum', 'other'];
109
109
  const REVISION_STATUS_VALUES = ['draft', 'active', 'completed', 'cancelled'];
110
- const TASK_STATUS_VALUES = ['active', 'completed', 'archived'];
110
+ const TASK_STATUS_VALUES = ['todo', 'doing', 'review', 'done'];
111
111
  let OperationsService = OperationsService_1 = class OperationsService {
112
112
  constructor(prisma, aiService, integrationApi, fileService, settingService, accessService, localeService) {
113
113
  this.prisma = prisma;
@@ -834,6 +834,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
834
834
  if (data.equityParticipation !== undefined) {
835
835
  await this.replaceCollaboratorEquityParticipation(tx, collaboratorId, data.equityParticipation);
836
836
  }
837
+ if (data.compensationAmount !== undefined ||
838
+ data.contractDescription !== undefined ||
839
+ data.autoGenerateContractDraft !== undefined ||
840
+ data.joinedAt !== undefined ||
841
+ data.weeklyCapacityHours !== undefined ||
842
+ data.supervisorCollaboratorId !== undefined ||
843
+ data.collaboratorType !== undefined ||
844
+ data.collaboratorTypeId !== undefined ||
845
+ data.collaboratorTypeSlug !== undefined ||
846
+ data.code !== undefined ||
847
+ data.personId !== undefined ||
848
+ data.displayName !== undefined) {
849
+ await this.syncHiringContractDraft(tx, actor.userId, collaboratorId, data);
850
+ }
837
851
  });
838
852
  return this.getCollaboratorByIdForUser(userId, collaboratorId);
839
853
  }
@@ -1021,8 +1035,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1021
1035
  p.progress_percent AS "progressPercent",
1022
1036
  p.delivery_model AS "deliveryModel",
1023
1037
  p.budget_amount AS "budgetAmount",
1024
- p.start_date AS "startDate",
1025
- p.end_date AS "endDate",
1038
+ TO_CHAR(p.start_date, 'YYYY-MM-DD') AS "startDate",
1039
+ TO_CHAR(p.end_date, 'YYYY-MM-DD') AS "endDate",
1026
1040
  c.name AS "contractName",
1027
1041
  c.status AS "contractStatus",
1028
1042
  m.display_name AS "managerName",
@@ -1043,20 +1057,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
1043
1057
  var _a, _b;
1044
1058
  const actor = await this.getActorContext(userId);
1045
1059
  this.ensureCollaborator(actor);
1046
- if (!actor.collaboratorId) {
1047
- throw new common_1.BadRequestException('Collaborator context is required.');
1048
- }
1049
1060
  const pagination = this.normalizePaginationParams(paginationParams, {
1050
1061
  defaultSortField: 'name',
1051
1062
  defaultSortOrder: 'asc',
1052
1063
  allowedSortFields: ['name', 'code', 'clientName', 'startDate', 'endDate'],
1053
1064
  });
1054
- const params = [actor.collaboratorId];
1065
+ const filter = this.buildIdFilter(actor.visibleProjectIds, 'p.id', actor.isDirector);
1066
+ const params = [...filter.params];
1055
1067
  const filters = [
1056
1068
  'p.deleted_at IS NULL',
1057
1069
  'pa.deleted_at IS NULL',
1058
- `pa.collaborator_id = $1`,
1059
1070
  `pa.status IN ('planned', 'active')`,
1071
+ filter.clause,
1060
1072
  ];
1061
1073
  if (pagination.search) {
1062
1074
  const searchPlaceholder = this.param(params, `%${pagination.search}%`);
@@ -1090,8 +1102,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1090
1102
  MAX(pa.id)::int AS "projectAssignmentId",
1091
1103
  MAX(pa.role_label) AS "roleLabel",
1092
1104
  p.status,
1093
- p.start_date AS "startDate",
1094
- p.end_date AS "endDate"
1105
+ TO_CHAR(p.start_date, 'YYYY-MM-DD') AS "startDate",
1106
+ TO_CHAR(p.end_date, 'YYYY-MM-DD') AS "endDate"
1095
1107
  FROM operations_project_assignment pa
1096
1108
  JOIN operations_project p
1097
1109
  ON p.id = pa.project_id
@@ -1106,21 +1118,25 @@ let OperationsService = OperationsService_1 = class OperationsService {
1106
1118
  var _a, _b;
1107
1119
  const actor = await this.getActorContext(userId);
1108
1120
  this.ensureCollaborator(actor);
1109
- if (!actor.collaboratorId) {
1110
- throw new common_1.BadRequestException('Collaborator context is required.');
1111
- }
1112
1121
  const pagination = this.normalizePaginationParams(paginationParams, {
1113
1122
  defaultSortField: 'name',
1114
1123
  defaultSortOrder: 'asc',
1115
1124
  allowedSortFields: ['name', 'projectName', 'status', 'createdAt'],
1116
1125
  });
1117
- const params = [actor.collaboratorId];
1126
+ const projectFilter = this.buildIdFilter(actor.visibleProjectIds, 'COALESCE(t.project_id, pa.project_id)', actor.isDirector);
1127
+ const params = [...projectFilter.params];
1118
1128
  const filters = [
1119
1129
  't.deleted_at IS NULL',
1120
- 'pa.deleted_at IS NULL',
1121
1130
  'p.deleted_at IS NULL',
1122
- `pa.collaborator_id = $1`,
1123
- `pa.status IN ('planned', 'active')`,
1131
+ projectFilter.clause,
1132
+ `(
1133
+ t.project_id IS NOT NULL
1134
+ OR (
1135
+ pa.id IS NOT NULL
1136
+ AND pa.deleted_at IS NULL
1137
+ AND pa.status IN ('planned', 'active')
1138
+ )
1139
+ )`,
1124
1140
  ];
1125
1141
  if (pagination.search) {
1126
1142
  const searchPlaceholder = this.param(params, `%${pagination.search}%`);
@@ -1135,7 +1151,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1135
1151
  filters.push(`pa.id = ${this.param(params, paginationParams.projectAssignmentId)}`);
1136
1152
  }
1137
1153
  if (paginationParams.projectId) {
1138
- filters.push(`pa.project_id = ${this.param(params, paginationParams.projectId)}`);
1154
+ filters.push(`COALESCE(t.project_id, pa.project_id) = ${this.param(params, paginationParams.projectId)}`);
1139
1155
  }
1140
1156
  if (paginationParams.status) {
1141
1157
  filters.push(`t.status = ${this.param(params, paginationParams.status)}`);
@@ -1143,10 +1159,10 @@ let OperationsService = OperationsService_1 = class OperationsService {
1143
1159
  const whereClause = filters.join(' AND ');
1144
1160
  const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
1145
1161
  FROM operations_task t
1146
- JOIN operations_project_assignment pa
1162
+ LEFT JOIN operations_project_assignment pa
1147
1163
  ON pa.id = t.project_assignment_id
1148
1164
  JOIN operations_project p
1149
- ON p.id = pa.project_id
1165
+ ON p.id = COALESCE(t.project_id, pa.project_id)
1150
1166
  WHERE ${whereClause}`, params);
1151
1167
  const sortColumn = (_a = {
1152
1168
  name: 't.name',
@@ -1161,16 +1177,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
1161
1177
  t.name,
1162
1178
  t.description,
1163
1179
  t.status,
1164
- pa.project_id AS "projectId",
1180
+ COALESCE(t.project_id, pa.project_id) AS "projectId",
1165
1181
  pa.id AS "projectAssignmentId",
1166
1182
  p.name AS "projectName",
1167
1183
  p.code AS "projectCode",
1168
1184
  t.created_at AS "createdAt"
1169
1185
  FROM operations_task t
1170
- JOIN operations_project_assignment pa
1186
+ LEFT JOIN operations_project_assignment pa
1171
1187
  ON pa.id = t.project_assignment_id
1172
1188
  JOIN operations_project p
1173
- ON p.id = pa.project_id
1189
+ ON p.id = COALESCE(t.project_id, pa.project_id)
1174
1190
  WHERE ${whereClause}
1175
1191
  ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, t.id ASC
1176
1192
  LIMIT ${limitPlaceholder}
@@ -1178,52 +1194,84 @@ let OperationsService = OperationsService_1 = class OperationsService {
1178
1194
  return this.buildPaginationResult(rows.map((row) => (Object.assign(Object.assign({}, row), { label: [row.name, row.projectName].filter(Boolean).join(' • ') }))), Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
1179
1195
  }
1180
1196
  async createTask(userId, data) {
1181
- var _a, _b, _c, _d;
1197
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
1182
1198
  const actor = await this.getActorContext(userId);
1183
- this.ensureCollaborator(actor);
1199
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1200
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1201
+ }
1184
1202
  this.requireFields(data, ['name']);
1185
- if (!actor.collaboratorId) {
1186
- throw new common_1.BadRequestException('Collaborator context is required.');
1203
+ let assignmentId = null;
1204
+ let projectId = null;
1205
+ if (data.projectId || data.projectAssignmentId) {
1206
+ const assignment = await this.resolveProjectAssignmentForActor(this.prisma, actor, {
1207
+ projectId: (_a = data.projectId) !== null && _a !== void 0 ? _a : null,
1208
+ projectAssignmentId: (_b = data.projectAssignmentId) !== null && _b !== void 0 ? _b : null,
1209
+ });
1210
+ await this.assertProjectAccess(actor, assignment.projectId);
1211
+ assignmentId = assignment.id;
1212
+ projectId = assignment.projectId;
1213
+ }
1214
+ else if (data.projectId) {
1215
+ projectId = data.projectId;
1216
+ await this.assertProjectAccess(actor, projectId);
1217
+ }
1218
+ else {
1219
+ throw new common_1.BadRequestException('Either projectId or projectAssignmentId is required.');
1220
+ }
1221
+ if (!projectId) {
1222
+ projectId = (_c = data.projectId) !== null && _c !== void 0 ? _c : null;
1187
1223
  }
1188
- const assignment = await this.resolveOwnedProjectAssignment(this.prisma, actor.collaboratorId, {
1189
- projectId: (_a = data.projectId) !== null && _a !== void 0 ? _a : null,
1190
- projectAssignmentId: (_b = data.projectAssignmentId) !== null && _b !== void 0 ? _b : null,
1191
- });
1192
- await this.assertProjectAccess(actor, assignment.projectId);
1193
1224
  const name = this.normalizeOptionalText(data.name);
1194
1225
  if (!name) {
1195
1226
  throw new common_1.BadRequestException('Field "name" is required.');
1196
1227
  }
1228
+ const maxPositionRow = await this.querySingle(`SELECT MAX(position) AS max_pos
1229
+ FROM operations_task
1230
+ WHERE project_id = $1
1231
+ AND status = $2::operations_task_status_574c143dbe_enum
1232
+ AND deleted_at IS NULL`, [projectId, (_d = data.status) !== null && _d !== void 0 ? _d : 'todo']);
1233
+ const nextPosition = ((_e = maxPositionRow === null || maxPositionRow === void 0 ? void 0 : maxPositionRow.max_pos) !== null && _e !== void 0 ? _e : -1) + 1;
1197
1234
  const created = await this.querySingle(`INSERT INTO operations_task (
1235
+ project_id,
1198
1236
  project_assignment_id,
1237
+ assignee_collaborator_id,
1199
1238
  name,
1200
1239
  description,
1240
+ priority,
1201
1241
  status,
1242
+ due_date,
1243
+ estimate_hours,
1244
+ position,
1245
+ tags,
1202
1246
  created_at,
1203
1247
  updated_at
1204
1248
  ) VALUES (
1205
- $1,
1206
- $2,
1207
- $3,
1208
- $4,
1209
- NOW(),
1210
- NOW()
1249
+ $1, $2, $3, $4, $5,
1250
+ $6::operations_task_priority_394ab327eb_enum,
1251
+ $7::operations_task_status_574c143dbe_enum,
1252
+ $8::date, $9::decimal, $10, $11, NOW(), NOW()
1211
1253
  )
1212
1254
  RETURNING id`, [
1213
- assignment.id,
1255
+ projectId,
1256
+ assignmentId,
1257
+ (_f = data.assigneeCollaboratorId) !== null && _f !== void 0 ? _f : null,
1214
1258
  name,
1215
1259
  this.normalizeOptionalText(data.description),
1216
- (_c = data.status) !== null && _c !== void 0 ? _c : 'active',
1260
+ (_g = data.priority) !== null && _g !== void 0 ? _g : 'medium',
1261
+ (_h = data.status) !== null && _h !== void 0 ? _h : 'todo',
1262
+ (_j = data.dueDate) !== null && _j !== void 0 ? _j : null,
1263
+ (_k = data.estimateHours) !== null && _k !== void 0 ? _k : null,
1264
+ (_l = data.position) !== null && _l !== void 0 ? _l : nextPosition,
1265
+ (_m = data.tags) !== null && _m !== void 0 ? _m : null,
1217
1266
  ]);
1218
- return this.getTaskOptionById(actor.collaboratorId, (_d = created === null || created === void 0 ? void 0 : created.id) !== null && _d !== void 0 ? _d : 0);
1267
+ return this.getProjectBoardTask((_o = created === null || created === void 0 ? void 0 : created.id) !== null && _o !== void 0 ? _o : 0);
1219
1268
  }
1220
1269
  async updateTask(userId, taskId, data) {
1221
1270
  const actor = await this.getActorContext(userId);
1222
- this.ensureCollaborator(actor);
1223
- if (!actor.collaboratorId) {
1224
- throw new common_1.BadRequestException('Collaborator context is required.');
1271
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1272
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1225
1273
  }
1226
- const current = await this.getOwnedTaskRecord(this.prisma, actor.collaboratorId, taskId);
1274
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1227
1275
  await this.assertProjectAccess(actor, current.projectId);
1228
1276
  const nextName = data.name !== undefined
1229
1277
  ? this.normalizeOptionalText(data.name)
@@ -1232,42 +1280,50 @@ let OperationsService = OperationsService_1 = class OperationsService {
1232
1280
  throw new common_1.BadRequestException('Field "name" is required.');
1233
1281
  }
1234
1282
  await this.prisma.$transaction(async (tx) => {
1235
- var _a, _b, _c, _d;
1236
- const nextAssignment = data.projectId !== undefined || data.projectAssignmentId !== undefined
1237
- ? await this.resolveOwnedProjectAssignment(tx, actor.collaboratorId, {
1283
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1284
+ let nextAssignmentId = current.projectAssignmentId;
1285
+ let nextProjectId = current.projectId;
1286
+ if (data.projectId !== undefined || data.projectAssignmentId !== undefined) {
1287
+ const nextAssignment = await this.resolveProjectAssignmentForActor(tx, actor, {
1238
1288
  projectId: (_a = data.projectId) !== null && _a !== void 0 ? _a : null,
1239
1289
  projectAssignmentId: (_b = data.projectAssignmentId) !== null && _b !== void 0 ? _b : null,
1240
- })
1241
- : {
1242
- id: current.projectAssignmentId,
1243
- projectId: current.projectId,
1244
- };
1245
- await this.assertProjectAccess(actor, nextAssignment.projectId);
1290
+ });
1291
+ await this.assertProjectAccess(actor, nextAssignment.projectId);
1292
+ nextAssignmentId = nextAssignment.id;
1293
+ nextProjectId = nextAssignment.projectId;
1294
+ }
1246
1295
  await tx.$executeRawUnsafe(`UPDATE operations_task
1247
- SET project_assignment_id = $1,
1248
- name = $2,
1249
- description = $3,
1250
- status = $4,
1296
+ SET project_id = $1,
1297
+ project_assignment_id = $2,
1298
+ assignee_collaborator_id = $3,
1299
+ name = $4,
1300
+ description = $5,
1301
+ priority = $6::operations_task_priority_394ab327eb_enum,
1302
+ status = $7::operations_task_status_574c143dbe_enum,
1303
+ due_date = $8::date,
1304
+ estimate_hours = $9::decimal,
1305
+ position = $10,
1306
+ tags = $11,
1251
1307
  updated_at = NOW()
1252
- WHERE id = $5
1253
- AND deleted_at IS NULL`, nextAssignment.id, nextName, data.description !== undefined
1308
+ WHERE id = $12
1309
+ AND deleted_at IS NULL`, nextProjectId, nextAssignmentId, data.assigneeCollaboratorId !== undefined
1310
+ ? ((_c = data.assigneeCollaboratorId) !== null && _c !== void 0 ? _c : null)
1311
+ : current.assigneeCollaboratorId, nextName, data.description !== undefined
1254
1312
  ? this.normalizeOptionalText(data.description)
1255
- : ((_c = current.description) !== null && _c !== void 0 ? _c : null), (_d = data.status) !== null && _d !== void 0 ? _d : current.status, taskId);
1313
+ : ((_d = current.description) !== null && _d !== void 0 ? _d : null), (_e = data.priority) !== null && _e !== void 0 ? _e : current.priority, (_f = data.status) !== null && _f !== void 0 ? _f : current.status, data.dueDate !== undefined ? ((_g = data.dueDate) !== null && _g !== void 0 ? _g : null) : current.dueDate, data.estimateHours !== undefined ? ((_h = data.estimateHours) !== null && _h !== void 0 ? _h : null) : current.estimateHours, data.position !== undefined ? data.position : current.position, data.tags !== undefined ? ((_j = data.tags) !== null && _j !== void 0 ? _j : null) : current.tags, taskId);
1256
1314
  });
1257
- return this.getTaskOptionById(actor.collaboratorId, taskId);
1315
+ return this.getProjectBoardTask(taskId);
1258
1316
  }
1259
1317
  async removeTask(userId, taskId) {
1260
1318
  const actor = await this.getActorContext(userId);
1261
- this.ensureCollaborator(actor);
1262
- if (!actor.collaboratorId) {
1263
- throw new common_1.BadRequestException('Collaborator context is required.');
1319
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1320
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1264
1321
  }
1265
- const current = await this.getOwnedTaskRecord(this.prisma, actor.collaboratorId, taskId);
1322
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1266
1323
  await this.assertProjectAccess(actor, current.projectId);
1267
1324
  await this.prisma.$transaction(async (tx) => {
1268
1325
  await tx.$executeRawUnsafe(`UPDATE operations_task
1269
1326
  SET deleted_at = COALESCE(deleted_at, NOW()),
1270
- status = 'archived',
1271
1327
  updated_at = NOW()
1272
1328
  WHERE id = $1
1273
1329
  AND deleted_at IS NULL`, taskId);
@@ -1409,8 +1465,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1409
1465
  const resolvedTask = data.taskId
1410
1466
  ? await this.getOwnedTaskRecord(tx, actor.collaboratorId, data.taskId)
1411
1467
  : null;
1412
- if (resolvedTask && resolvedTask.projectAssignmentId !== assignment.id) {
1413
- throw new common_1.BadRequestException('The selected task does not belong to the chosen project assignment.');
1468
+ if (resolvedTask && resolvedTask.projectId !== assignment.projectId) {
1469
+ throw new common_1.BadRequestException('The selected task does not belong to the chosen project.');
1414
1470
  }
1415
1471
  const activityLabel = (_e = (_d = (_c = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _c !== void 0 ? _c : taskLabel) !== null && _d !== void 0 ? _d : assignment.roleLabel) !== null && _e !== void 0 ? _e : assignment.projectName;
1416
1472
  if (!activityLabel) {
@@ -1446,6 +1502,68 @@ let OperationsService = OperationsService_1 = class OperationsService {
1446
1502
  });
1447
1503
  return this.getTimesheetEntryByIdForActor(actor, createdEntryId);
1448
1504
  }
1505
+ async updateTimesheetEntry(userId, entryId, data) {
1506
+ var _a;
1507
+ const actor = await this.getActorContext(userId);
1508
+ this.ensureCollaborator(actor);
1509
+ this.requireFields(data, ['workDate', 'duration']);
1510
+ if (!actor.collaboratorId && !actor.isDirector) {
1511
+ throw new common_1.BadRequestException('Collaborator context is required.');
1512
+ }
1513
+ const entry = await this.getTimesheetEntryByIdForActor(actor, entryId);
1514
+ if (!actor.isDirector && entry.collaboratorId !== actor.collaboratorId) {
1515
+ throw new common_1.ForbiddenException('Only the entry owner can update this timesheet entry.');
1516
+ }
1517
+ if (!['draft', 'rejected'].includes(entry.status)) {
1518
+ throw new common_1.BadRequestException('Only draft or rejected timesheet entries can be edited.');
1519
+ }
1520
+ const collaboratorId = actor.isDirector
1521
+ ? entry.collaboratorId
1522
+ : actor.collaboratorId;
1523
+ const durationMinutes = this.normalizeDurationMinutes(data.duration, data.unit);
1524
+ const taskLabel = (_a = this.normalizeOptionalText(data.taskName)) !== null && _a !== void 0 ? _a : this.normalizeOptionalText(data.activityLabel);
1525
+ const targetWeek = this.getWorkWeekRange(data.workDate);
1526
+ const isSameWeek = entry.weekStartDate === targetWeek.weekStartDate &&
1527
+ entry.weekEndDate === targetWeek.weekEndDate;
1528
+ await this.prisma.$transaction(async (tx) => {
1529
+ var _a, _b, _c, _d, _e, _f, _g;
1530
+ const assignment = await this.resolveOwnedProjectAssignment(tx, collaboratorId, {
1531
+ projectId: (_a = data.projectId) !== null && _a !== void 0 ? _a : null,
1532
+ projectAssignmentId: (_b = data.projectAssignmentId) !== null && _b !== void 0 ? _b : null,
1533
+ });
1534
+ const resolvedTask = data.taskId
1535
+ ? await this.getOwnedTaskRecord(tx, collaboratorId, data.taskId)
1536
+ : null;
1537
+ if (resolvedTask && resolvedTask.projectId !== assignment.projectId) {
1538
+ throw new common_1.BadRequestException('The selected task does not belong to the chosen project.');
1539
+ }
1540
+ const activityLabel = (_e = (_d = (_c = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _c !== void 0 ? _c : taskLabel) !== null && _d !== void 0 ? _d : assignment.roleLabel) !== null && _e !== void 0 ? _e : assignment.projectName;
1541
+ if (!activityLabel) {
1542
+ throw new common_1.BadRequestException('A task is required for the timesheet entry.');
1543
+ }
1544
+ const targetTimesheetId = isSameWeek
1545
+ ? entry.timesheetId
1546
+ : await this.getOrCreateTimesheetForWorkDate(tx, collaboratorId, data.workDate);
1547
+ await tx.$executeRawUnsafe(`UPDATE operations_timesheet_entry
1548
+ SET timesheet_id = $1,
1549
+ project_assignment_id = $2,
1550
+ task_id = $3,
1551
+ activity_label = $4,
1552
+ work_date = $5::date,
1553
+ duration_minutes = $6,
1554
+ hours = $7,
1555
+ description = $8,
1556
+ updated_at = NOW()
1557
+ WHERE id = $9
1558
+ AND deleted_at IS NULL`, targetTimesheetId, assignment.id, (_g = (_f = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.id) !== null && _f !== void 0 ? _f : data.taskId) !== null && _g !== void 0 ? _g : null, activityLabel, data.workDate, durationMinutes, Number((durationMinutes / 60).toFixed(2)), this.normalizeOptionalText(data.description), entryId);
1559
+ await this.refreshTimesheetTotal(tx, entry.timesheetId);
1560
+ if (targetTimesheetId !== entry.timesheetId) {
1561
+ await this.refreshTimesheetTotal(tx, targetTimesheetId);
1562
+ }
1563
+ await this.cleanupEmptyEditableTimesheet(tx, entry.timesheetId);
1564
+ });
1565
+ return this.getTimesheetEntryByIdForActor(actor, entryId);
1566
+ }
1449
1567
  async removeTimesheetEntry(userId, entryId) {
1450
1568
  const actor = await this.getActorContext(userId);
1451
1569
  this.ensureCollaborator(actor);
@@ -1466,6 +1584,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1466
1584
  WHERE id = $1
1467
1585
  AND deleted_at IS NULL`, entryId);
1468
1586
  await this.refreshTimesheetTotal(tx, entry.timesheetId);
1587
+ await this.cleanupEmptyEditableTimesheet(tx, entry.timesheetId);
1469
1588
  });
1470
1589
  return { success: true };
1471
1590
  }
@@ -1479,10 +1598,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
1479
1598
  this.ensureDirector(actor);
1480
1599
  this.requireFields(data, ['code', 'name']);
1481
1600
  const createdProjectId = await this.prisma.$transaction(async (tx) => {
1482
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
1601
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
1483
1602
  const created = await tx.$queryRawUnsafe(`INSERT INTO operations_project (
1484
1603
  contract_id,
1485
1604
  manager_collaborator_id,
1605
+ client_person_id,
1486
1606
  code,
1487
1607
  name,
1488
1608
  client_name,
@@ -1496,33 +1616,33 @@ let OperationsService = OperationsService_1 = class OperationsService {
1496
1616
  created_at,
1497
1617
  updated_at
1498
1618
  ) VALUES (
1499
- $1, $2, $3, $4, $5, $6,
1500
- $7::operations_project_status_965e8d4b2d_enum,
1501
- $8,
1502
- $9::operations_project_delivery_model_75ee11b3b7_enum,
1503
- $10, $11::date, $12::date, NOW(), NOW()
1619
+ $1, $2, $3, $4, $5, $6, $7,
1620
+ $8::operations_project_status_965e8d4b2d_enum,
1621
+ $9,
1622
+ $10::operations_project_delivery_model_75ee11b3b7_enum,
1623
+ $11, $12::date, $13::date, NOW(), NOW()
1504
1624
  )
1505
- 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);
1506
- const projectId = (_l = created[0]) === null || _l === void 0 ? void 0 : _l.id;
1507
- if ((_m = data.teamAssignments) === null || _m === void 0 ? void 0 : _m.length) {
1625
+ RETURNING id`, (_a = data.contractId) !== null && _a !== void 0 ? _a : null, (_b = data.managerCollaboratorId) !== null && _b !== void 0 ? _b : null, (_c = data.clientPersonId) !== null && _c !== void 0 ? _c : null, data.code, data.name, (_d = data.clientName) !== null && _d !== void 0 ? _d : null, (_e = data.summary) !== null && _e !== void 0 ? _e : null, (_f = data.status) !== null && _f !== void 0 ? _f : 'planning', (_g = data.progressPercent) !== null && _g !== void 0 ? _g : null, (_h = data.deliveryModel) !== null && _h !== void 0 ? _h : 'project_delivery', (_j = data.budgetAmount) !== null && _j !== void 0 ? _j : null, (_k = data.startDate) !== null && _k !== void 0 ? _k : null, (_l = data.endDate) !== null && _l !== void 0 ? _l : null);
1626
+ const projectId = (_m = created[0]) === null || _m === void 0 ? void 0 : _m.id;
1627
+ if ((_o = data.teamAssignments) === null || _o === void 0 ? void 0 : _o.length) {
1508
1628
  await this.replaceProjectAssignments(tx, projectId, data.teamAssignments);
1509
1629
  }
1510
1630
  if (!data.contractId && data.autoGenerateContractDraft !== false) {
1511
1631
  const contractId = await this.createProjectContractDraft(tx, actor.userId, {
1512
1632
  projectId,
1513
- contractTemplateId: (_o = data.contractTemplateId) !== null && _o !== void 0 ? _o : null,
1633
+ contractTemplateId: (_p = data.contractTemplateId) !== null && _p !== void 0 ? _p : null,
1514
1634
  projectCode: data.code,
1515
1635
  projectName: data.name,
1516
- clientName: (_p = data.clientName) !== null && _p !== void 0 ? _p : data.name,
1517
- managerCollaboratorId: (_q = data.managerCollaboratorId) !== null && _q !== void 0 ? _q : null,
1518
- startDate: (_r = data.startDate) !== null && _r !== void 0 ? _r : null,
1519
- endDate: (_s = data.endDate) !== null && _s !== void 0 ? _s : null,
1520
- budgetAmount: (_t = data.budgetAmount) !== null && _t !== void 0 ? _t : null,
1521
- monthlyHourCap: (_u = data.monthlyHourCap) !== null && _u !== void 0 ? _u : null,
1522
- billingModel: (_v = data.billingModel) !== null && _v !== void 0 ? _v : 'time_and_material',
1523
- contractCode: (_w = data.contractCode) !== null && _w !== void 0 ? _w : null,
1524
- contractName: (_x = data.contractName) !== null && _x !== void 0 ? _x : null,
1525
- description: (_z = (_y = data.contractDescription) !== null && _y !== void 0 ? _y : data.summary) !== null && _z !== void 0 ? _z : null,
1636
+ clientName: (_q = data.clientName) !== null && _q !== void 0 ? _q : data.name,
1637
+ managerCollaboratorId: (_r = data.managerCollaboratorId) !== null && _r !== void 0 ? _r : null,
1638
+ startDate: (_s = data.startDate) !== null && _s !== void 0 ? _s : null,
1639
+ endDate: (_t = data.endDate) !== null && _t !== void 0 ? _t : null,
1640
+ budgetAmount: (_u = data.budgetAmount) !== null && _u !== void 0 ? _u : null,
1641
+ monthlyHourCap: (_v = data.monthlyHourCap) !== null && _v !== void 0 ? _v : null,
1642
+ billingModel: (_w = data.billingModel) !== null && _w !== void 0 ? _w : 'time_and_material',
1643
+ contractCode: (_x = data.contractCode) !== null && _x !== void 0 ? _x : null,
1644
+ contractName: (_y = data.contractName) !== null && _y !== void 0 ? _y : null,
1645
+ description: (_0 = (_z = data.contractDescription) !== null && _z !== void 0 ? _z : data.summary) !== null && _0 !== void 0 ? _0 : null,
1526
1646
  });
1527
1647
  await tx.$executeRawUnsafe(`UPDATE operations_project
1528
1648
  SET contract_id = $1,
@@ -1541,18 +1661,19 @@ let OperationsService = OperationsService_1 = class OperationsService {
1541
1661
  const params = [];
1542
1662
  this.pushUpdate(updates, params, 'contract_id', data.contractId);
1543
1663
  this.pushUpdate(updates, params, 'manager_collaborator_id', data.managerCollaboratorId);
1664
+ this.pushUpdate(updates, params, 'client_person_id', data.clientPersonId);
1544
1665
  this.pushUpdate(updates, params, 'code', data.code);
1545
1666
  this.pushUpdate(updates, params, 'name', data.name);
1546
1667
  this.pushUpdate(updates, params, 'client_name', data.clientName);
1547
1668
  this.pushUpdate(updates, params, 'summary', data.summary);
1548
1669
  this.pushUpdate(updates, params, 'status', data.status, 'operations_project_status_965e8d4b2d_enum');
1549
1670
  this.pushUpdate(updates, params, 'progress_percent', data.progressPercent);
1550
- this.pushUpdate(updates, params, 'delivery_model', data.deliveryModel, 'operations_project_delivery_model_75ee11b3b7_enum');
1671
+ this.pushUpdate(updates, params, 'delivery_model', data.deliveryModel === '' ? null : data.deliveryModel, 'operations_project_delivery_model_75ee11b3b7_enum');
1551
1672
  this.pushUpdate(updates, params, 'budget_amount', data.budgetAmount);
1552
1673
  this.pushUpdate(updates, params, 'start_date', data.startDate, 'date');
1553
1674
  this.pushUpdate(updates, params, 'end_date', data.endDate, 'date');
1554
1675
  await this.prisma.$transaction(async (tx) => {
1555
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
1676
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
1556
1677
  if (updates.length) {
1557
1678
  params.push(projectId);
1558
1679
  await tx.$executeRawUnsafe(`UPDATE operations_project
@@ -1565,30 +1686,46 @@ let OperationsService = OperationsService_1 = class OperationsService {
1565
1686
  }
1566
1687
  const nextContractId = data.contractId !== undefined
1567
1688
  ? data.contractId
1568
- : ((_a = currentProject.contractId) !== null && _a !== void 0 ? _a : null);
1689
+ : ((_b = (_a = currentProject.relatedContract) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null);
1569
1690
  const shouldGenerateDraft = !nextContractId && data.autoGenerateContractDraft === true;
1570
1691
  if (shouldGenerateDraft) {
1571
1692
  const contractId = await this.createProjectContractDraft(tx, actor.userId, {
1572
1693
  projectId,
1573
- contractTemplateId: (_b = data.contractTemplateId) !== null && _b !== void 0 ? _b : null,
1574
- projectCode: (_c = data.code) !== null && _c !== void 0 ? _c : currentProject.code,
1575
- projectName: (_d = data.name) !== null && _d !== void 0 ? _d : currentProject.name,
1576
- clientName: (_f = (_e = data.clientName) !== null && _e !== void 0 ? _e : currentProject.clientName) !== null && _f !== void 0 ? _f : currentProject.name,
1577
- managerCollaboratorId: (_h = (_g = data.managerCollaboratorId) !== null && _g !== void 0 ? _g : currentProject.managerCollaboratorId) !== null && _h !== void 0 ? _h : null,
1578
- startDate: (_k = (_j = data.startDate) !== null && _j !== void 0 ? _j : currentProject.startDate) !== null && _k !== void 0 ? _k : null,
1579
- endDate: (_m = (_l = data.endDate) !== null && _l !== void 0 ? _l : currentProject.endDate) !== null && _m !== void 0 ? _m : null,
1580
- budgetAmount: (_p = (_o = data.budgetAmount) !== null && _o !== void 0 ? _o : currentProject.budgetAmount) !== null && _p !== void 0 ? _p : null,
1581
- monthlyHourCap: (_s = (_q = data.monthlyHourCap) !== null && _q !== void 0 ? _q : (_r = currentProject.relatedContract) === null || _r === void 0 ? void 0 : _r.monthlyHourCap) !== null && _s !== void 0 ? _s : null,
1582
- billingModel: (_v = (_t = data.billingModel) !== null && _t !== void 0 ? _t : (_u = currentProject.relatedContract) === null || _u === void 0 ? void 0 : _u.billingModel) !== null && _v !== void 0 ? _v : 'time_and_material',
1583
- contractCode: (_y = (_w = data.contractCode) !== null && _w !== void 0 ? _w : (_x = currentProject.relatedContract) === null || _x === void 0 ? void 0 : _x.code) !== null && _y !== void 0 ? _y : null,
1584
- contractName: (_1 = (_z = data.contractName) !== null && _z !== void 0 ? _z : (_0 = currentProject.relatedContract) === null || _0 === void 0 ? void 0 : _0.name) !== null && _1 !== void 0 ? _1 : null,
1585
- description: (_6 = (_5 = (_4 = (_2 = data.contractDescription) !== null && _2 !== void 0 ? _2 : (_3 = currentProject.relatedContract) === null || _3 === void 0 ? void 0 : _3.description) !== null && _4 !== void 0 ? _4 : data.summary) !== null && _5 !== void 0 ? _5 : currentProject.summary) !== null && _6 !== void 0 ? _6 : null,
1694
+ contractTemplateId: (_c = data.contractTemplateId) !== null && _c !== void 0 ? _c : null,
1695
+ projectCode: (_d = data.code) !== null && _d !== void 0 ? _d : currentProject.code,
1696
+ projectName: (_e = data.name) !== null && _e !== void 0 ? _e : currentProject.name,
1697
+ clientName: (_g = (_f = data.clientName) !== null && _f !== void 0 ? _f : currentProject.clientName) !== null && _g !== void 0 ? _g : currentProject.name,
1698
+ managerCollaboratorId: (_j = (_h = data.managerCollaboratorId) !== null && _h !== void 0 ? _h : currentProject.managerCollaboratorId) !== null && _j !== void 0 ? _j : null,
1699
+ startDate: (_l = (_k = data.startDate) !== null && _k !== void 0 ? _k : currentProject.startDate) !== null && _l !== void 0 ? _l : null,
1700
+ endDate: (_o = (_m = data.endDate) !== null && _m !== void 0 ? _m : currentProject.endDate) !== null && _o !== void 0 ? _o : null,
1701
+ budgetAmount: (_q = (_p = data.budgetAmount) !== null && _p !== void 0 ? _p : currentProject.budgetAmount) !== null && _q !== void 0 ? _q : null,
1702
+ monthlyHourCap: (_t = (_r = data.monthlyHourCap) !== null && _r !== void 0 ? _r : (_s = currentProject.relatedContract) === null || _s === void 0 ? void 0 : _s.monthlyHourCap) !== null && _t !== void 0 ? _t : null,
1703
+ billingModel: (_w = (_u = data.billingModel) !== null && _u !== void 0 ? _u : (_v = currentProject.relatedContract) === null || _v === void 0 ? void 0 : _v.billingModel) !== null && _w !== void 0 ? _w : 'time_and_material',
1704
+ contractCode: (_z = (_x = data.contractCode) !== null && _x !== void 0 ? _x : (_y = currentProject.relatedContract) === null || _y === void 0 ? void 0 : _y.code) !== null && _z !== void 0 ? _z : null,
1705
+ contractName: (_2 = (_0 = data.contractName) !== null && _0 !== void 0 ? _0 : (_1 = currentProject.relatedContract) === null || _1 === void 0 ? void 0 : _1.name) !== null && _2 !== void 0 ? _2 : null,
1706
+ description: (_7 = (_6 = (_5 = (_3 = data.contractDescription) !== null && _3 !== void 0 ? _3 : (_4 = currentProject.relatedContract) === null || _4 === void 0 ? void 0 : _4.description) !== null && _5 !== void 0 ? _5 : data.summary) !== null && _6 !== void 0 ? _6 : currentProject.summary) !== null && _7 !== void 0 ? _7 : null,
1586
1707
  });
1587
1708
  await tx.$executeRawUnsafe(`UPDATE operations_project
1588
1709
  SET contract_id = $1,
1589
1710
  updated_at = NOW()
1590
1711
  WHERE id = $2`, contractId, projectId);
1591
1712
  }
1713
+ else if (nextContractId &&
1714
+ (data.monthlyHourCap !== undefined || data.billingModel !== undefined)) {
1715
+ const contractUpdates = [];
1716
+ const contractParams = [];
1717
+ this.pushUpdate(contractUpdates, contractParams, 'monthly_hour_cap', data.monthlyHourCap);
1718
+ this.pushUpdate(contractUpdates, contractParams, 'billing_model', data.billingModel === ''
1719
+ ? null
1720
+ : data.billingModel, 'operations_contract_billing_model_409dc7fea2_enum');
1721
+ if (contractUpdates.length) {
1722
+ contractParams.push(nextContractId);
1723
+ await tx.$executeRawUnsafe(`UPDATE operations_contract
1724
+ SET ${contractUpdates.join(', ')},
1725
+ updated_at = NOW()
1726
+ WHERE id = $${contractParams.length}`, ...contractParams);
1727
+ }
1728
+ }
1592
1729
  });
1593
1730
  return this.getProjectById(userId, projectId);
1594
1731
  }
@@ -1661,7 +1798,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
1661
1798
  updated_at
1662
1799
  ) VALUES (
1663
1800
  $1, $2, $3, $4,
1664
- $5, $6, $7, $8, $9, $10, $11,
1801
+ $5::text::operations_contract_template_contract_category_49bb07a713_enum,
1802
+ $6::text::operations_contract_template_contract_type_3962dbda6a_enum,
1803
+ $7::text::operations_contract_template_billing_model_384a7c60e2_enum,
1804
+ $8::text::operations_contract_template_signature_status_56cb6d625b_enum,
1805
+ $9, $10::text::operations_contract_template_status_c9d2e90231_enum, $11,
1665
1806
  NOW(), NOW()
1666
1807
  )
1667
1808
  RETURNING id`, await this.generateUniqueContractTemplateSlug(tx, name), nextCode, name, this.normalizeOptionalText(data.description), (_e = data.contractCategory) !== null && _e !== void 0 ? _e : 'client', (_f = data.contractType) !== null && _f !== void 0 ? _f : 'service_agreement', (_g = data.billingModel) !== null && _g !== void 0 ? _g : 'time_and_material', (_h = data.signatureStatus) !== null && _h !== void 0 ? _h : 'not_started', isActive, nextStatus, this.normalizeOptionalText(data.contentHtml)));
@@ -1703,12 +1844,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
1703
1844
  code = $2,
1704
1845
  name = $3,
1705
1846
  description = $4,
1706
- contract_category = $5,
1707
- contract_type = $6,
1708
- billing_model = $7,
1709
- signature_status = $8,
1847
+ contract_category = $5::text::operations_contract_template_contract_category_49bb07a713_enum,
1848
+ contract_type = $6::text::operations_contract_template_contract_type_3962dbda6a_enum,
1849
+ billing_model = $7::text::operations_contract_template_billing_model_384a7c60e2_enum,
1850
+ signature_status = $8::text::operations_contract_template_signature_status_56cb6d625b_enum,
1710
1851
  is_active = $9,
1711
- status = $10,
1852
+ status = $10::text::operations_contract_template_status_c9d2e90231_enum,
1712
1853
  content_html = $11,
1713
1854
  updated_at = NOW()
1714
1855
  WHERE id = $12`, nextSlug, nextCode, nextName, data.description !== undefined
@@ -2954,7 +3095,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2954
3095
  return this.listSingleTimesheet(actor, timesheetId);
2955
3096
  }
2956
3097
  async submitTimesheet(userId, timesheetId) {
2957
- var _a, _b;
3098
+ var _a, _b, _c;
2958
3099
  const actor = await this.getActorContext(userId);
2959
3100
  const current = await this.getTimesheetById(timesheetId);
2960
3101
  if (!actor.isDirector && current.collaboratorId !== actor.collaboratorId) {
@@ -2964,7 +3105,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
2964
3105
  throw new common_1.BadRequestException('Only draft or rejected timesheets can be submitted.');
2965
3106
  }
2966
3107
  const collaborator = await this.getCollaboratorById(current.collaboratorId);
2967
- const approverId = (_b = (_a = current.approverCollaboratorId) !== null && _a !== void 0 ? _a : collaborator.supervisorId) !== null && _b !== void 0 ? _b : null;
3108
+ const projectManagerRow = await this.querySingle(`SELECT p.manager_collaborator_id AS "managerCollaboratorId"
3109
+ FROM operations_timesheet_entry e
3110
+ LEFT JOIN operations_project_assignment pa ON pa.id = e.project_assignment_id
3111
+ LEFT JOIN operations_project p ON p.id = pa.project_id
3112
+ WHERE e.timesheet_id = $1
3113
+ AND e.deleted_at IS NULL
3114
+ AND p.manager_collaborator_id IS NOT NULL
3115
+ LIMIT 1`, [timesheetId]);
3116
+ const approverId = (_c = (_b = (_a = projectManagerRow === null || projectManagerRow === void 0 ? void 0 : projectManagerRow.managerCollaboratorId) !== null && _a !== void 0 ? _a : current.approverCollaboratorId) !== null && _b !== void 0 ? _b : collaborator.supervisorId) !== null && _c !== void 0 ? _c : null;
2968
3117
  if (!approverId) {
2969
3118
  throw new common_1.BadRequestException('An approver is required before submitting a timesheet.');
2970
3119
  }
@@ -3120,7 +3269,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
3120
3269
  FROM operations_schedule_adjustment_day
3121
3270
  WHERE schedule_adjustment_request_id = ANY($1::int[])
3122
3271
  ORDER BY id ASC`, [requests.map((item) => item.id)]);
3123
- const currentSchedule = await this.queryRows(`SELECT collaborator_id AS "collaboratorId",
3272
+ const currentSchedule = await this.queryRows(`SELECT DISTINCT ON (collaborator_id, weekday)
3273
+ collaborator_id AS "collaboratorId",
3124
3274
  weekday,
3125
3275
  is_working_day AS "isWorkingDay",
3126
3276
  start_time AS "startTime",
@@ -3128,7 +3278,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
3128
3278
  break_minutes AS "breakMinutes"
3129
3279
  FROM operations_collaborator_schedule_day
3130
3280
  WHERE collaborator_id = ANY($1::int[])
3131
- ORDER BY id ASC`, [this.uniqueNumbers(requests.map((item) => item.collaboratorId))]);
3281
+ ORDER BY collaborator_id, weekday, id DESC`, [this.uniqueNumbers(requests.map((item) => item.collaboratorId))]);
3132
3282
  const grouped = this.groupBy(days, 'requestId');
3133
3283
  const currentScheduleByCollaborator = this.groupBy(currentSchedule, 'collaboratorId');
3134
3284
  return requests.map((request) => {
@@ -3452,17 +3602,44 @@ let OperationsService = OperationsService_1 = class OperationsService {
3452
3602
  };
3453
3603
  }
3454
3604
  async getCollaboratorByUserId(userId) {
3455
- return this.querySingle(`SELECT c.id,
3605
+ return this.querySingle(`WITH linked_collaborator AS (
3606
+ SELECT person_id
3607
+ FROM operations_collaborator
3608
+ WHERE user_id = $1
3609
+ AND deleted_at IS NULL
3610
+ ORDER BY id DESC
3611
+ LIMIT 1
3612
+ )
3613
+ SELECT c.id,
3614
+ c.user_id AS "userId",
3615
+ c.person_id AS "personId",
3456
3616
  COALESCE(NULLIF(c.display_name, ''), person_record.name) AS "displayName",
3457
3617
  s.id AS "supervisorId",
3458
- s.display_name AS "supervisorName"
3618
+ s.display_name AS "supervisorName",
3619
+ COUNT(pa.id) FILTER (
3620
+ WHERE pa.deleted_at IS NULL
3621
+ AND pa.status IN ('planned', 'active')
3622
+ )::int AS "activeAssignments"
3459
3623
  FROM operations_collaborator c
3460
3624
  LEFT JOIN person person_record
3461
3625
  ON person_record.id = c.person_id
3462
3626
  LEFT JOIN operations_collaborator s
3463
3627
  ON s.id = c.supervisor_collaborator_id
3464
- WHERE c.user_id = $1
3465
- AND c.deleted_at IS NULL`, [userId]);
3628
+ LEFT JOIN operations_project_assignment pa
3629
+ ON pa.collaborator_id = c.id
3630
+ WHERE c.deleted_at IS NULL
3631
+ AND (
3632
+ c.user_id = $1
3633
+ OR (
3634
+ c.person_id IS NOT NULL
3635
+ AND c.person_id = (SELECT person_id FROM linked_collaborator)
3636
+ )
3637
+ )
3638
+ GROUP BY c.id, person_record.id, s.id
3639
+ ORDER BY "activeAssignments" DESC,
3640
+ CASE WHEN c.user_id = $1 THEN 0 ELSE 1 END,
3641
+ c.id ASC
3642
+ LIMIT 1`, [userId]);
3466
3643
  }
3467
3644
  async getCollaboratorById(collaboratorId) {
3468
3645
  const collaborator = await this.querySingle(`SELECT c.id,
@@ -3930,6 +4107,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
3930
4107
  const project = await this.querySingle(`SELECT p.id,
3931
4108
  p.contract_id AS "contractId",
3932
4109
  p.manager_collaborator_id AS "managerCollaboratorId",
4110
+ p.client_person_id AS "clientPersonId",
4111
+ client_person.avatar_id AS "clientAvatarId",
3933
4112
  p.code,
3934
4113
  p.name,
3935
4114
  p.client_name AS "clientName",
@@ -3938,8 +4117,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
3938
4117
  p.progress_percent AS "progressPercent",
3939
4118
  p.delivery_model AS "deliveryModel",
3940
4119
  p.budget_amount AS "budgetAmount",
3941
- p.start_date AS "startDate",
3942
- p.end_date AS "endDate",
4120
+ TO_CHAR(p.start_date, 'YYYY-MM-DD') AS "startDate",
4121
+ TO_CHAR(p.end_date, 'YYYY-MM-DD') AS "endDate",
3943
4122
  c.name AS "contractName",
3944
4123
  c.status AS "contractStatus",
3945
4124
  c.contract_category AS "contractCategory",
@@ -3950,6 +4129,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
3950
4129
  FROM operations_project p
3951
4130
  LEFT JOIN operations_contract c ON c.id = p.contract_id
3952
4131
  LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
4132
+ LEFT JOIN person client_person ON client_person.id = p.client_person_id
3953
4133
  LEFT JOIN operations_project_assignment pa
3954
4134
  ON pa.project_id = p.id
3955
4135
  AND pa.deleted_at IS NULL
@@ -3965,24 +4145,29 @@ let OperationsService = OperationsService_1 = class OperationsService {
3965
4145
  ) project_role_locale ON TRUE
3966
4146
  WHERE p.id = $1
3967
4147
  AND p.deleted_at IS NULL
3968
- GROUP BY p.id, c.id, m.id`, [projectId, actorCollaboratorId !== null && actorCollaboratorId !== void 0 ? actorCollaboratorId : null]);
4148
+ GROUP BY p.id, c.id, m.id, client_person.id`, [projectId, actorCollaboratorId !== null && actorCollaboratorId !== void 0 ? actorCollaboratorId : null]);
3969
4149
  if (!project) {
3970
4150
  throw new common_1.NotFoundException('Project not found.');
3971
4151
  }
3972
4152
  const [assignments, relatedContract, timesheetSummary, operationalIndicators] = await Promise.all([
3973
4153
  this.queryRows(`SELECT pa.id,
3974
4154
  pa.collaborator_id AS "collaboratorId",
4155
+ c.user_id AS "userId",
4156
+ person_record.avatar_id AS "personAvatarId",
4157
+ collaborator_user.photo_id AS "userPhotoId",
3975
4158
  c.display_name AS "collaboratorName",
3976
4159
  pa.project_role_id AS "projectRoleId",
3977
4160
  COALESCE(project_role_locale.name, pa.role_label) AS "roleLabel",
3978
4161
  pa.allocation_percent AS "allocationPercent",
3979
4162
  pa.weekly_hours AS "weeklyHours",
3980
4163
  pa.is_billable AS "isBillable",
3981
- pa.start_date AS "startDate",
3982
- pa.end_date AS "endDate",
4164
+ TO_CHAR(pa.start_date, 'YYYY-MM-DD') AS "startDate",
4165
+ TO_CHAR(pa.end_date, 'YYYY-MM-DD') AS "endDate",
3983
4166
  pa.status
3984
4167
  FROM operations_project_assignment pa
3985
4168
  JOIN operations_collaborator c ON c.id = pa.collaborator_id
4169
+ LEFT JOIN person person_record ON person_record.id = c.person_id
4170
+ LEFT JOIN "user" collaborator_user ON collaborator_user.id = c.user_id
3986
4171
  LEFT JOIN operations_project_role project_role
3987
4172
  ON project_role.id = pa.project_role_id
3988
4173
  AND project_role.deleted_at IS NULL
@@ -4004,8 +4189,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
4004
4189
  contract_category AS "contractCategory",
4005
4190
  billing_model AS "billingModel",
4006
4191
  status,
4007
- start_date AS "startDate",
4008
- end_date AS "endDate",
4192
+ TO_CHAR(start_date, 'YYYY-MM-DD') AS "startDate",
4193
+ TO_CHAR(end_date, 'YYYY-MM-DD') AS "endDate",
4009
4194
  budget_amount AS "budgetAmount",
4010
4195
  monthly_hour_cap AS "monthlyHourCap",
4011
4196
  description,
@@ -4152,7 +4337,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
4152
4337
  FROM operations_contract c
4153
4338
  WHERE c.related_collaborator_id = $1
4154
4339
  AND c.deleted_at IS NULL
4155
- ORDER BY c.created_at DESC`, [collaboratorId]),
4340
+ ORDER BY CASE WHEN c.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
4341
+ c.created_at DESC`, [collaboratorId]),
4156
4342
  this.queryRows(`SELECT weekday,
4157
4343
  is_working_day AS "isWorkingDay",
4158
4344
  start_time AS "startTime",
@@ -4260,34 +4446,148 @@ let OperationsService = OperationsService_1 = class OperationsService {
4260
4446
  }
4261
4447
  return assignment[0];
4262
4448
  }
4449
+ async resolveProjectAssignmentForActor(client, actor, input) {
4450
+ if (actor.collaboratorId && !actor.isDirector && !actor.isSupervisor) {
4451
+ return this.resolveOwnedProjectAssignment(client, actor.collaboratorId, input);
4452
+ }
4453
+ if (!input.projectId && !input.projectAssignmentId) {
4454
+ throw new common_1.BadRequestException('Either projectId or projectAssignmentId is required.');
4455
+ }
4456
+ const params = [];
4457
+ const filters = ['pa.deleted_at IS NULL', 'p.deleted_at IS NULL'];
4458
+ if (input.projectAssignmentId) {
4459
+ filters.push(`pa.id = ${this.param(params, input.projectAssignmentId)}`);
4460
+ }
4461
+ if (input.projectId) {
4462
+ filters.push(`pa.project_id = ${this.param(params, input.projectId)}`);
4463
+ }
4464
+ const assignment = (await client.$queryRawUnsafe(`SELECT pa.id,
4465
+ pa.project_id AS "projectId",
4466
+ p.name AS "projectName",
4467
+ p.code AS "projectCode",
4468
+ pa.role_label AS "roleLabel"
4469
+ FROM operations_project_assignment pa
4470
+ JOIN operations_project p
4471
+ ON p.id = pa.project_id
4472
+ WHERE ${filters.join(' AND ')}
4473
+ ORDER BY CASE WHEN pa.status = 'active' THEN 0 ELSE 1 END,
4474
+ pa.start_date DESC NULLS LAST,
4475
+ pa.id DESC
4476
+ LIMIT 1`, ...params));
4477
+ if (!assignment[0]) {
4478
+ throw new common_1.NotFoundException('Project assignment not found.');
4479
+ }
4480
+ return assignment[0];
4481
+ }
4263
4482
  async getOwnedTaskRecord(client, collaboratorId, taskId) {
4264
4483
  const task = (await client.$queryRawUnsafe(`SELECT t.id,
4265
4484
  t.name,
4266
4485
  t.description,
4486
+ t.priority,
4267
4487
  t.status,
4488
+ t.due_date AS "dueDate",
4489
+ t.estimate_hours AS "estimateHours",
4490
+ t.position,
4491
+ t.tags,
4492
+ t.assignee_collaborator_id AS "assigneeCollaboratorId",
4268
4493
  t.project_assignment_id AS "projectAssignmentId",
4269
- pa.project_id AS "projectId",
4494
+ COALESCE(t.project_id, pa.project_id) AS "projectId",
4270
4495
  p.name AS "projectName",
4271
4496
  p.code AS "projectCode"
4272
4497
  FROM operations_task t
4273
- JOIN operations_project_assignment pa
4498
+ LEFT JOIN operations_project_assignment pa
4274
4499
  ON pa.id = t.project_assignment_id
4275
4500
  AND pa.deleted_at IS NULL
4276
- JOIN operations_project p
4277
- ON p.id = pa.project_id
4501
+ LEFT JOIN operations_project p
4502
+ ON p.id = COALESCE(t.project_id, pa.project_id)
4278
4503
  AND p.deleted_at IS NULL
4279
4504
  WHERE t.id = $1
4280
4505
  AND t.deleted_at IS NULL
4281
- AND pa.collaborator_id = $2
4506
+ AND (
4507
+ pa.collaborator_id = $2
4508
+ OR t.project_id IN (
4509
+ SELECT pa2.project_id FROM operations_project_assignment pa2
4510
+ WHERE pa2.collaborator_id = $2 AND pa2.deleted_at IS NULL
4511
+ )
4512
+ )
4282
4513
  LIMIT 1`, taskId, collaboratorId));
4283
4514
  if (!task[0]) {
4284
4515
  throw new common_1.ForbiddenException('The selected task is not assigned to the authenticated collaborator.');
4285
4516
  }
4286
4517
  return task[0];
4287
4518
  }
4288
- async getTaskOptionById(collaboratorId, taskId) {
4289
- const task = await this.getOwnedTaskRecord(this.prisma, collaboratorId, taskId);
4290
- return Object.assign(Object.assign({}, task), { label: [task.name, task.projectName].filter(Boolean).join(' • ') });
4519
+ async getTaskRecordForActor(client, actor, taskId) {
4520
+ if (actor.collaboratorId && !actor.isDirector && !actor.isSupervisor) {
4521
+ return this.getOwnedTaskRecord(client, actor.collaboratorId, taskId);
4522
+ }
4523
+ const task = await this.getProjectBoardTask(taskId);
4524
+ if (!(task === null || task === void 0 ? void 0 : task.projectId)) {
4525
+ throw new common_1.NotFoundException('Task not found.');
4526
+ }
4527
+ return task;
4528
+ }
4529
+ async listProjectBoardTasks(userId, projectId) {
4530
+ const actor = await this.getActorContext(userId);
4531
+ this.ensureCollaborator(actor);
4532
+ await this.assertProjectAccess(actor, projectId);
4533
+ const rows = await this.queryRows(`SELECT t.id,
4534
+ t.name,
4535
+ t.description,
4536
+ t.priority,
4537
+ t.status,
4538
+ t.due_date AS "dueDate",
4539
+ t.estimate_hours AS "estimateHours",
4540
+ t.position,
4541
+ t.tags,
4542
+ t.assignee_collaborator_id AS "assigneeCollaboratorId",
4543
+ ac.display_name AS "assigneeName",
4544
+ au.photo_id AS "assigneeUserPhotoId",
4545
+ ap.avatar_id AS "assigneePersonAvatarId",
4546
+ t.project_assignment_id AS "projectAssignmentId",
4547
+ t.created_at AS "createdAt"
4548
+ FROM operations_task t
4549
+ LEFT JOIN operations_collaborator ac
4550
+ ON ac.id = t.assignee_collaborator_id AND ac.deleted_at IS NULL
4551
+ LEFT JOIN "user" au
4552
+ ON au.id = ac.user_id
4553
+ LEFT JOIN person ap
4554
+ ON ap.id = ac.person_id
4555
+ WHERE COALESCE(t.project_id, (
4556
+ SELECT pa.project_id FROM operations_project_assignment pa
4557
+ WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
4558
+ LIMIT 1
4559
+ )) = $1
4560
+ AND t.deleted_at IS NULL
4561
+ ORDER BY t.status ASC, t.position ASC, t.id ASC`, [projectId]);
4562
+ return rows;
4563
+ }
4564
+ async getProjectBoardTask(taskId) {
4565
+ var _a;
4566
+ const rows = await this.queryRows(`SELECT t.id,
4567
+ t.name,
4568
+ t.description,
4569
+ t.priority,
4570
+ t.status,
4571
+ t.due_date AS "dueDate",
4572
+ t.estimate_hours AS "estimateHours",
4573
+ t.position,
4574
+ t.tags,
4575
+ t.assignee_collaborator_id AS "assigneeCollaboratorId",
4576
+ ac.display_name AS "assigneeName",
4577
+ au.photo_id AS "assigneeUserPhotoId",
4578
+ ap.avatar_id AS "assigneePersonAvatarId",
4579
+ t.project_assignment_id AS "projectAssignmentId",
4580
+ COALESCE(t.project_id, pa.project_id) AS "projectId",
4581
+ t.created_at AS "createdAt"
4582
+ FROM operations_task t
4583
+ LEFT JOIN operations_collaborator ac
4584
+ ON ac.id = t.assignee_collaborator_id AND ac.deleted_at IS NULL
4585
+ LEFT JOIN "user" au ON au.id = ac.user_id
4586
+ LEFT JOIN person ap ON ap.id = ac.person_id
4587
+ LEFT JOIN operations_project_assignment pa
4588
+ ON pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
4589
+ WHERE t.id = $1`, [taskId]);
4590
+ return (_a = rows[0]) !== null && _a !== void 0 ? _a : null;
4291
4591
  }
4292
4592
  async getOrCreateTimesheetForWorkDate(client, collaboratorId, workDate) {
4293
4593
  var _a, _b, _c;
@@ -4389,7 +4689,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
4389
4689
  return timesheet;
4390
4690
  }
4391
4691
  async replaceTimesheetEntries(client, timesheetId, entries, collaboratorId) {
4392
- var _a, _b, _c, _d, _e, _f;
4692
+ var _a, _b, _c, _d, _e, _f, _g, _h;
4393
4693
  await client.$executeRawUnsafe(`UPDATE operations_timesheet_entry
4394
4694
  SET deleted_at = NOW()
4395
4695
  WHERE timesheet_id = $1
@@ -4399,17 +4699,19 @@ let OperationsService = OperationsService_1 = class OperationsService {
4399
4699
  const assignmentIds = entries
4400
4700
  .map((entry) => entry.projectAssignmentId)
4401
4701
  .filter((value) => typeof value === 'number');
4702
+ const assignmentMap = new Map();
4402
4703
  if (assignmentIds.length) {
4403
- const assignments = (await client.$queryRawUnsafe(`SELECT id, collaborator_id AS "collaboratorId"
4704
+ const assignments = (await client.$queryRawUnsafe(`SELECT id,
4705
+ collaborator_id AS "collaboratorId",
4706
+ project_id AS "projectId"
4404
4707
  FROM operations_project_assignment
4405
4708
  WHERE id = ANY($1::int[])
4406
4709
  AND deleted_at IS NULL`, assignmentIds));
4407
- const assignmentMap = new Map(assignments.map((assignment) => [
4408
- assignment.id,
4409
- assignment.collaboratorId,
4410
- ]));
4710
+ assignments.forEach((assignment) => {
4711
+ assignmentMap.set(assignment.id, assignment);
4712
+ });
4411
4713
  for (const assignmentId of assignmentIds) {
4412
- if (assignmentMap.get(assignmentId) !== collaboratorId) {
4714
+ if (((_a = assignmentMap.get(assignmentId)) === null || _a === void 0 ? void 0 : _a.collaboratorId) !== collaboratorId) {
4413
4715
  throw new common_1.ForbiddenException('Timesheet entries must use assignments owned by the target collaborator.');
4414
4716
  }
4415
4717
  }
@@ -4420,16 +4722,19 @@ let OperationsService = OperationsService_1 = class OperationsService {
4420
4722
  const resolvedTask = entry.taskId
4421
4723
  ? await this.getOwnedTaskRecord(client, collaboratorId, entry.taskId)
4422
4724
  : null;
4725
+ const selectedAssignment = entry.projectAssignmentId
4726
+ ? (_b = assignmentMap.get(entry.projectAssignmentId)) !== null && _b !== void 0 ? _b : null
4727
+ : null;
4423
4728
  if (resolvedTask &&
4424
- entry.projectAssignmentId &&
4425
- resolvedTask.projectAssignmentId !== entry.projectAssignmentId) {
4426
- throw new common_1.BadRequestException('The selected task does not belong to the chosen project assignment.');
4729
+ selectedAssignment &&
4730
+ resolvedTask.projectId !== selectedAssignment.projectId) {
4731
+ throw new common_1.BadRequestException('The selected task does not belong to the chosen project.');
4427
4732
  }
4428
- const resolvedAssignmentId = (_b = (_a = entry.projectAssignmentId) !== null && _a !== void 0 ? _a : resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.projectAssignmentId) !== null && _b !== void 0 ? _b : null;
4733
+ const resolvedAssignmentId = (_d = (_c = entry.projectAssignmentId) !== null && _c !== void 0 ? _c : resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.projectAssignmentId) !== null && _d !== void 0 ? _d : null;
4429
4734
  if (entry.taskId && !resolvedAssignmentId) {
4430
4735
  throw new common_1.BadRequestException('The selected task must belong to a project assignment.');
4431
4736
  }
4432
- const activityLabel = (_d = (_c = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _c !== void 0 ? _c : this.normalizeOptionalText(entry.taskName)) !== null && _d !== void 0 ? _d : this.normalizeOptionalText(entry.activityLabel);
4737
+ const activityLabel = (_f = (_e = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _e !== void 0 ? _e : this.normalizeOptionalText(entry.taskName)) !== null && _f !== void 0 ? _f : this.normalizeOptionalText(entry.activityLabel);
4433
4738
  if (!entry.workDate) {
4434
4739
  throw new common_1.BadRequestException('Timesheet entry workDate is required.');
4435
4740
  }
@@ -4444,7 +4749,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
4444
4749
  description,
4445
4750
  created_at,
4446
4751
  updated_at
4447
- ) VALUES ($1, $2, $3, $4, $5::date, $6, $7, $8, NOW(), NOW())`, timesheetId, resolvedAssignmentId, (_f = (_e = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.id) !== null && _e !== void 0 ? _e : entry.taskId) !== null && _f !== void 0 ? _f : null, activityLabel !== null && activityLabel !== void 0 ? activityLabel : null, entry.workDate, durationMinutes, hours, this.normalizeOptionalText(entry.description));
4752
+ ) VALUES ($1, $2, $3, $4, $5::date, $6, $7, $8, NOW(), NOW())`, timesheetId, resolvedAssignmentId, (_h = (_g = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.id) !== null && _g !== void 0 ? _g : entry.taskId) !== null && _h !== void 0 ? _h : null, activityLabel !== null && activityLabel !== void 0 ? activityLabel : null, entry.workDate, durationMinutes, hours, this.normalizeOptionalText(entry.description));
4448
4753
  }
4449
4754
  }
4450
4755
  async refreshTimesheetTotal(client, timesheetId) {
@@ -4470,6 +4775,29 @@ let OperationsService = OperationsService_1 = class OperationsService {
4470
4775
  updated_at = NOW()
4471
4776
  WHERE id = $1`, timesheetId);
4472
4777
  }
4778
+ async cleanupEmptyEditableTimesheet(client, timesheetId) {
4779
+ var _a;
4780
+ const candidate = (await client.$queryRawUnsafe(`SELECT t.id
4781
+ FROM operations_timesheet t
4782
+ WHERE t.id = $1
4783
+ AND t.deleted_at IS NULL
4784
+ AND t.status IN ('draft', 'rejected')
4785
+ AND NOT EXISTS (
4786
+ SELECT 1
4787
+ FROM operations_timesheet_entry e
4788
+ WHERE e.timesheet_id = t.id
4789
+ AND e.deleted_at IS NULL
4790
+ )
4791
+ LIMIT 1`, timesheetId));
4792
+ if (!((_a = candidate[0]) === null || _a === void 0 ? void 0 : _a.id)) {
4793
+ return;
4794
+ }
4795
+ await client.$executeRawUnsafe(`UPDATE operations_timesheet
4796
+ SET deleted_at = NOW(),
4797
+ updated_at = NOW()
4798
+ WHERE id = $1
4799
+ AND deleted_at IS NULL`, timesheetId);
4800
+ }
4473
4801
  async upsertApproval(client, input) {
4474
4802
  var _a, _b;
4475
4803
  const existing = (await client.$queryRawUnsafe(`SELECT id
@@ -4846,6 +5174,90 @@ let OperationsService = OperationsService_1 = class OperationsService {
4846
5174
  ? Math.round(Number(input.weeklyCapacityHours) * 4)
4847
5175
  : null, (_c = input.description) !== null && _c !== void 0 ? _c : null, createdByUserId);
4848
5176
  }
5177
+ async syncHiringContractDraft(client, updatedByUserId, collaboratorId, data) {
5178
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
5179
+ const collaborator = await client.$queryRawUnsafe(`SELECT c.id,
5180
+ c.code,
5181
+ COALESCE(NULLIF(c.display_name, ''), person_record.name) AS "displayName",
5182
+ collaborator_type.slug AS "collaboratorTypeSlug",
5183
+ c.supervisor_collaborator_id AS "supervisorCollaboratorId",
5184
+ c.joined_at AS "joinedAt",
5185
+ c.weekly_capacity_hours AS "weeklyCapacityHours"
5186
+ FROM operations_collaborator c
5187
+ LEFT JOIN person person_record
5188
+ ON person_record.id = c.person_id
5189
+ LEFT JOIN operations_collaborator_type collaborator_type
5190
+ ON collaborator_type.id = c.collaborator_type_id
5191
+ AND collaborator_type.deleted_at IS NULL
5192
+ WHERE c.id = $1
5193
+ AND c.deleted_at IS NULL
5194
+ LIMIT 1`, collaboratorId);
5195
+ const currentCollaborator = (_a = collaborator[0]) !== null && _a !== void 0 ? _a : null;
5196
+ if (!currentCollaborator) {
5197
+ throw new common_1.NotFoundException('Collaborator not found.');
5198
+ }
5199
+ const hiringContracts = (await client.$queryRawUnsafe(`SELECT id,
5200
+ budget_amount AS "budgetAmount",
5201
+ description
5202
+ FROM operations_contract
5203
+ WHERE related_collaborator_id = $1
5204
+ AND origin_type = 'employee_hiring'
5205
+ AND deleted_at IS NULL
5206
+ ORDER BY created_at DESC
5207
+ LIMIT 1`, collaboratorId));
5208
+ const hiringContract = (_b = hiringContracts[0]) !== null && _b !== void 0 ? _b : null;
5209
+ const collaboratorCode = (_c = this.normalizeOptionalText(currentCollaborator.code)) !== null && _c !== void 0 ? _c : `COL-${collaboratorId}`;
5210
+ const displayName = (_d = this.normalizeOptionalText(currentCollaborator.displayName)) !== null && _d !== void 0 ? _d : `Collaborator ${collaboratorId}`;
5211
+ const collaboratorTypeSlug = (_e = this.normalizeOptionalText(currentCollaborator.collaboratorTypeSlug)) !== null && _e !== void 0 ? _e : 'other';
5212
+ const startDate = data.joinedAt !== undefined
5213
+ ? (_f = data.joinedAt) !== null && _f !== void 0 ? _f : null
5214
+ : (_g = currentCollaborator.joinedAt) !== null && _g !== void 0 ? _g : null;
5215
+ const weeklyCapacityHours = data.weeklyCapacityHours !== undefined
5216
+ ? (_h = data.weeklyCapacityHours) !== null && _h !== void 0 ? _h : null
5217
+ : (_j = currentCollaborator.weeklyCapacityHours) !== null && _j !== void 0 ? _j : null;
5218
+ const compensationAmount = data.compensationAmount !== undefined
5219
+ ? (_k = data.compensationAmount) !== null && _k !== void 0 ? _k : null
5220
+ : (_l = hiringContract === null || hiringContract === void 0 ? void 0 : hiringContract.budgetAmount) !== null && _l !== void 0 ? _l : null;
5221
+ const description = data.contractDescription !== undefined
5222
+ ? this.normalizeOptionalText(data.contractDescription)
5223
+ : (_m = hiringContract === null || hiringContract === void 0 ? void 0 : hiringContract.description) !== null && _m !== void 0 ? _m : null;
5224
+ const supervisorCollaboratorId = data.supervisorCollaboratorId !== undefined
5225
+ ? (_o = data.supervisorCollaboratorId) !== null && _o !== void 0 ? _o : null
5226
+ : (_p = currentCollaborator.supervisorCollaboratorId) !== null && _p !== void 0 ? _p : null;
5227
+ if (!hiringContract) {
5228
+ if (data.autoGenerateContractDraft === false) {
5229
+ return;
5230
+ }
5231
+ await this.createHiringContractDraft(client, updatedByUserId, {
5232
+ collaboratorId,
5233
+ collaboratorCode,
5234
+ displayName,
5235
+ collaboratorType: collaboratorTypeSlug,
5236
+ supervisorCollaboratorId,
5237
+ startDate,
5238
+ weeklyCapacityHours,
5239
+ compensationAmount,
5240
+ description,
5241
+ });
5242
+ return;
5243
+ }
5244
+ await client.$executeRawUnsafe(`UPDATE operations_contract
5245
+ SET code = $1,
5246
+ name = $2,
5247
+ contract_category = $3::operations_contract_contract_category_70d553ea09_enum,
5248
+ contract_type = $4::operations_contract_contract_type_48331e2ebf_enum,
5249
+ client_name = $5,
5250
+ billing_model = $6::operations_contract_billing_model_409dc7fea2_enum,
5251
+ account_manager_collaborator_id = $7,
5252
+ start_date = $8::date,
5253
+ effective_date = $8::date,
5254
+ budget_amount = $9,
5255
+ monthly_hour_cap = $10,
5256
+ description = $11,
5257
+ updated_by_user_id = $12,
5258
+ updated_at = NOW()
5259
+ WHERE id = $13`, `HIR-${collaboratorCode}`, this.buildHiringContractName(displayName, collaboratorTypeSlug), this.mapContractCategoryForCollaboratorType(collaboratorTypeSlug), this.mapContractTypeForCollaboratorType(collaboratorTypeSlug), displayName, this.mapBillingModelForCollaboratorType(collaboratorTypeSlug), supervisorCollaboratorId, startDate !== null && startDate !== void 0 ? startDate : new Date().toISOString().slice(0, 10), compensationAmount, weeklyCapacityHours ? Math.round(Number(weeklyCapacityHours) * 4) : null, description, updatedByUserId, hiringContract.id);
5260
+ }
4849
5261
  async createProjectContractDraft(client, createdByUserId, input) {
4850
5262
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
4851
5263
  const templateRows = input.contractTemplateId