@hed-hog/operations 0.0.306 → 0.0.310

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 (123) hide show
  1. package/dist/controllers/operations-approvals.controller.d.ts +114 -1
  2. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-approvals.controller.js +16 -3
  4. package/dist/controllers/operations-approvals.controller.js.map +1 -1
  5. package/dist/controllers/operations-collaborators.controller.d.ts +16 -1
  6. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-collaborators.controller.js +16 -3
  8. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  9. package/dist/controllers/operations-contracts.controller.d.ts +14 -453
  10. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -1
  11. package/dist/controllers/operations-contracts.controller.js +11 -112
  12. package/dist/controllers/operations-contracts.controller.js.map +1 -1
  13. package/dist/controllers/operations-org-structure.controller.d.ts +65 -2
  14. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -1
  15. package/dist/controllers/operations-org-structure.controller.js +18 -5
  16. package/dist/controllers/operations-org-structure.controller.js.map +1 -1
  17. package/dist/controllers/operations-projects.controller.d.ts +28 -4
  18. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  19. package/dist/controllers/operations-projects.controller.js +17 -5
  20. package/dist/controllers/operations-projects.controller.js.map +1 -1
  21. package/dist/controllers/operations-timesheets.controller.d.ts +31 -4
  22. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  23. package/dist/controllers/operations-timesheets.controller.js +16 -11
  24. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  25. package/dist/dto/list-approvals.dto.d.ts +6 -0
  26. package/dist/dto/list-approvals.dto.d.ts.map +1 -0
  27. package/dist/dto/list-approvals.dto.js +28 -0
  28. package/dist/dto/list-approvals.dto.js.map +1 -0
  29. package/dist/dto/list-collaborator-types.dto.d.ts +3 -1
  30. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -1
  31. package/dist/dto/list-collaborator-types.dto.js +7 -1
  32. package/dist/dto/list-collaborator-types.dto.js.map +1 -1
  33. package/dist/dto/list-collaborators.dto.d.ts +1 -0
  34. package/dist/dto/list-collaborators.dto.d.ts.map +1 -1
  35. package/dist/dto/list-collaborators.dto.js +5 -0
  36. package/dist/dto/list-collaborators.dto.js.map +1 -1
  37. package/dist/dto/list-contracts.dto.d.ts +8 -0
  38. package/dist/dto/list-contracts.dto.d.ts.map +1 -0
  39. package/dist/dto/list-contracts.dto.js +38 -0
  40. package/dist/dto/list-contracts.dto.js.map +1 -0
  41. package/dist/dto/list-departments.dto.d.ts +5 -0
  42. package/dist/dto/list-departments.dto.d.ts.map +1 -0
  43. package/dist/dto/list-departments.dto.js +23 -0
  44. package/dist/dto/list-departments.dto.js.map +1 -0
  45. package/dist/dto/list-projects.dto.d.ts +5 -0
  46. package/dist/dto/list-projects.dto.d.ts.map +1 -0
  47. package/dist/dto/list-projects.dto.js +23 -0
  48. package/dist/dto/list-projects.dto.js.map +1 -0
  49. package/dist/dto/list-schedule-adjustments.dto.d.ts +5 -0
  50. package/dist/dto/list-schedule-adjustments.dto.d.ts.map +1 -0
  51. package/dist/dto/list-schedule-adjustments.dto.js +23 -0
  52. package/dist/dto/list-schedule-adjustments.dto.js.map +1 -0
  53. package/dist/dto/list-time-off-requests.dto.d.ts +5 -0
  54. package/dist/dto/list-time-off-requests.dto.d.ts.map +1 -0
  55. package/dist/dto/list-time-off-requests.dto.js +23 -0
  56. package/dist/dto/list-time-off-requests.dto.js.map +1 -0
  57. package/dist/dto/list-timesheets.dto.d.ts +5 -0
  58. package/dist/dto/list-timesheets.dto.d.ts.map +1 -0
  59. package/dist/dto/list-timesheets.dto.js +23 -0
  60. package/dist/dto/list-timesheets.dto.js.map +1 -0
  61. package/dist/dto/reorder-collaborator-types.dto.d.ts +4 -0
  62. package/dist/dto/reorder-collaborator-types.dto.d.ts.map +1 -0
  63. package/dist/dto/reorder-collaborator-types.dto.js +25 -0
  64. package/dist/dto/reorder-collaborator-types.dto.js.map +1 -0
  65. package/dist/operations.service.d.ts +340 -271
  66. package/dist/operations.service.d.ts.map +1 -1
  67. package/dist/operations.service.js +1007 -1043
  68. package/dist/operations.service.js.map +1 -1
  69. package/dist/operations.service.spec.js +0 -22
  70. package/dist/operations.service.spec.js.map +1 -1
  71. package/hedhog/data/menu.yaml +0 -36
  72. package/hedhog/data/route.yaml +42 -73
  73. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +8 -1
  74. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +15 -10
  75. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +108 -213
  76. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +251 -2039
  77. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +167 -60
  78. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +70 -301
  79. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +102 -51
  80. package/hedhog/frontend/app/_lib/types.ts.ejs +19 -24
  81. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +14 -9
  82. package/hedhog/frontend/app/approvals/page.tsx.ejs +842 -150
  83. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +445 -153
  84. package/hedhog/frontend/app/collaborators/page.tsx.ejs +118 -49
  85. package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +2 -2
  86. package/hedhog/frontend/app/contracts/page.tsx.ejs +215 -617
  87. package/hedhog/frontend/app/departments/page.tsx.ejs +257 -113
  88. package/hedhog/frontend/app/projects/page.tsx.ejs +90 -51
  89. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +412 -147
  90. package/hedhog/frontend/app/time-off/page.tsx.ejs +400 -123
  91. package/hedhog/frontend/app/timesheets/page.tsx.ejs +460 -365
  92. package/hedhog/frontend/messages/en.json +143 -14
  93. package/hedhog/frontend/messages/pt.json +192 -54
  94. package/hedhog/table/operations_contract.yaml +0 -9
  95. package/package.json +4 -4
  96. package/src/controllers/operations-approvals.controller.ts +9 -3
  97. package/src/controllers/operations-collaborators.controller.ts +15 -2
  98. package/src/controllers/operations-contracts.controller.ts +8 -92
  99. package/src/controllers/operations-org-structure.controller.ts +17 -4
  100. package/src/controllers/operations-projects.controller.ts +10 -4
  101. package/src/controllers/operations-timesheets.controller.ts +17 -8
  102. package/src/dto/list-approvals.dto.ts +12 -0
  103. package/src/dto/list-collaborator-types.dto.ts +7 -2
  104. package/src/dto/list-collaborators.dto.ts +4 -0
  105. package/src/dto/list-contracts.dto.ts +20 -0
  106. package/src/dto/list-departments.dto.ts +8 -0
  107. package/src/dto/list-projects.dto.ts +8 -0
  108. package/src/dto/list-schedule-adjustments.dto.ts +8 -0
  109. package/src/dto/list-time-off-requests.dto.ts +8 -0
  110. package/src/dto/list-timesheets.dto.ts +8 -0
  111. package/src/dto/reorder-collaborator-types.dto.ts +10 -0
  112. package/src/operations.service.spec.ts +0 -30
  113. package/src/operations.service.ts +1557 -1806
  114. package/hedhog/frontend/app/_components/contract-creation-wizard.tsx.ejs +0 -631
  115. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +0 -526
  116. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +0 -247
  117. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +0 -3520
  118. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +0 -380
  119. package/hedhog/frontend/app/team/page.tsx.ejs +0 -352
  120. package/hedhog/table/operations_contract_financial_term.yaml +0 -40
  121. package/hedhog/table/operations_contract_revision.yaml +0 -38
  122. package/hedhog/table/operations_contract_signature.yaml +0 -38
  123. package/hedhog/table/operations_contract_template.yaml +0 -58
@@ -102,11 +102,6 @@ const PARTY_ROLE_VALUES = [
102
102
  'other',
103
103
  ];
104
104
  const PARTY_TYPE_VALUES = ['individual', 'company', 'internal_team', 'other'];
105
- const SIGNATURE_ITEM_STATUS_VALUES = ['pending', 'signed', 'rejected'];
106
- const FINANCIAL_TERM_TYPE_VALUES = ['value', 'payment', 'revenue', 'fine', 'other'];
107
- const RECURRENCE_VALUES = ['one_time', 'monthly', 'quarterly', 'yearly', 'other'];
108
- const REVISION_TYPE_VALUES = ['amendment', 'renewal', 'revision', 'addendum', 'other'];
109
- const REVISION_STATUS_VALUES = ['draft', 'active', 'completed', 'cancelled'];
110
105
  const TASK_STATUS_VALUES = ['todo', 'doing', 'review', 'done'];
111
106
  let OperationsService = OperationsService_1 = class OperationsService {
112
107
  constructor(prisma, aiService, integrationApi, fileService, settingService, accessService, localeService) {
@@ -154,35 +149,82 @@ let OperationsService = OperationsService_1 = class OperationsService {
154
149
  LIMIT 1`));
155
150
  return (_b = (_a = fallbackLocales[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
156
151
  }
157
- async listCollaboratorTypes(userId, filters) {
152
+ async listCollaboratorTypes(userId, filters = {}) {
153
+ var _a, _b;
158
154
  const actor = await this.getActorContext(userId);
159
155
  this.ensureCollaborator(actor);
160
- return this.queryRows(`SELECT ct.id,
161
- ct.slug,
162
- ct.name,
163
- ct.description,
164
- ct.category,
165
- ct.is_active AS "isActive",
166
- ct.sort_order AS "sortOrder",
167
- CASE
168
- WHEN ct.deleted_at IS NULL AND ct.is_active THEN 'active'
169
- ELSE 'inactive'
170
- END AS status,
171
- COUNT(DISTINCT c.id)::int AS "collaboratorCount",
172
- ct.created_at AS "createdAt",
173
- ct.updated_at AS "updatedAt"
156
+ const params = [];
157
+ const where = [];
158
+ if (filters.active === true || filters.status === 'active') {
159
+ where.push('(ct.deleted_at IS NULL AND ct.is_active = true)');
160
+ }
161
+ else if (filters.status === 'inactive') {
162
+ where.push('(ct.deleted_at IS NOT NULL OR ct.is_active = false)');
163
+ }
164
+ const pagination = this.shouldPaginate(filters)
165
+ ? this.normalizePaginationParams(filters, {
166
+ defaultSortField: 'sortOrder',
167
+ defaultSortOrder: 'asc',
168
+ allowedSortFields: ['name', 'slug', 'category', 'sortOrder', 'createdAt'],
169
+ })
170
+ : null;
171
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
172
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
173
+ where.push(`(
174
+ COALESCE(ct.name, '') ILIKE ${searchPlaceholder}
175
+ OR COALESCE(ct.slug, '') ILIKE ${searchPlaceholder}
176
+ OR COALESCE(ct.description, '') ILIKE ${searchPlaceholder}
177
+ OR COALESCE(ct.category, '') ILIKE ${searchPlaceholder}
178
+ )`);
179
+ }
180
+ const whereClause = where.length ? `WHERE ${where.join(' AND ')}` : '';
181
+ const baseQuery = `SELECT ct.id,
182
+ ct.slug,
183
+ ct.name,
184
+ ct.description,
185
+ ct.category,
186
+ ct.is_active AS "isActive",
187
+ ct.sort_order AS "sortOrder",
188
+ CASE
189
+ WHEN ct.deleted_at IS NULL AND ct.is_active THEN 'active'
190
+ ELSE 'inactive'
191
+ END AS status,
192
+ COUNT(DISTINCT c.id)::int AS "collaboratorCount",
193
+ ct.created_at AS "createdAt",
194
+ ct.updated_at AS "updatedAt"
195
+ FROM operations_collaborator_type ct
196
+ LEFT JOIN operations_collaborator c
197
+ ON c.deleted_at IS NULL
198
+ AND c.collaborator_type_id = ct.id
199
+ ${whereClause}
200
+ GROUP BY ct.id`;
201
+ if (!pagination) {
202
+ return this.queryRows(`${baseQuery}
203
+ ORDER BY CASE
204
+ WHEN ct.deleted_at IS NULL AND ct.is_active THEN 0
205
+ ELSE 1
206
+ END ASC,
207
+ ct.sort_order ASC,
208
+ ct.name ASC`, params);
209
+ }
210
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
174
211
  FROM operations_collaborator_type ct
175
- LEFT JOIN operations_collaborator c
176
- ON c.deleted_at IS NULL
177
- AND c.collaborator_type_id = ct.id
178
- WHERE ($1::boolean = false OR (ct.deleted_at IS NULL AND ct.is_active = true))
179
- GROUP BY ct.id
180
- ORDER BY CASE
181
- WHEN ct.deleted_at IS NULL AND ct.is_active THEN 0
182
- ELSE 1
183
- END ASC,
184
- ct.sort_order ASC,
185
- ct.name ASC`, [Boolean(filters === null || filters === void 0 ? void 0 : filters.active)]);
212
+ ${whereClause}`, params);
213
+ const sortColumn = (_a = {
214
+ name: 'ct.name',
215
+ slug: 'ct.slug',
216
+ category: 'ct.category',
217
+ sortOrder: 'ct.sort_order',
218
+ createdAt: 'ct.created_at',
219
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : 'ct.name';
220
+ const queryParams = [...params];
221
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
222
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
223
+ const rows = await this.queryRows(`${baseQuery}
224
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, ct.id ASC
225
+ LIMIT ${limitPlaceholder}
226
+ OFFSET ${offsetPlaceholder}`, queryParams);
227
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
186
228
  }
187
229
  async createCollaboratorType(userId, data) {
188
230
  const actor = await this.getActorContext(userId);
@@ -196,6 +238,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
196
238
  await this.assertCollaboratorTypeNameAvailable(tx, name);
197
239
  const nextSlug = await this.buildCollaboratorTypeSlug(tx, name, data.slug);
198
240
  const nextIsActive = (_a = data.isActive) !== null && _a !== void 0 ? _a : (data.status ? data.status === 'active' : true);
241
+ const nextSortOrder = data.sortOrder !== undefined && Number.isFinite(Number(data.sortOrder))
242
+ ? Number(data.sortOrder)
243
+ : await this.getNextCollaboratorTypeSortOrder(tx);
199
244
  const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_collaborator_type (
200
245
  slug,
201
246
  name,
@@ -211,7 +256,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
211
256
  CASE WHEN $5::boolean THEN NULL ELSE NOW() END,
212
257
  NOW(), NOW()
213
258
  )
214
- RETURNING id`, nextSlug, name, this.normalizeOptionalText(data.description), this.normalizeOptionalText(data.category), nextIsActive, Number.isFinite(Number(data.sortOrder)) ? Number(data.sortOrder) : 0));
259
+ RETURNING id`, nextSlug, name, this.normalizeOptionalText(data.description), this.normalizeOptionalText(data.category), nextIsActive, nextSortOrder));
215
260
  const createdCollaboratorTypeId = (_b = created[0]) === null || _b === void 0 ? void 0 : _b.id;
216
261
  if (!createdCollaboratorTypeId) {
217
262
  throw new common_1.BadRequestException('Unable to create the collaborator type.');
@@ -261,6 +306,40 @@ let OperationsService = OperationsService_1 = class OperationsService {
261
306
  return this.getCollaboratorTypeById(tx, collaboratorTypeId, true);
262
307
  });
263
308
  }
309
+ async reorderCollaboratorTypes(userId, ids) {
310
+ const actor = await this.getActorContext(userId);
311
+ this.ensureDirector(actor);
312
+ const normalizedIds = [
313
+ ...new Set(ids
314
+ .map((value) => Number(value))
315
+ .filter((value) => Number.isInteger(value) && value > 0)),
316
+ ];
317
+ if (!normalizedIds.length) {
318
+ throw new common_1.BadRequestException('At least one collaborator type is required.');
319
+ }
320
+ return this.prisma.$transaction(async (tx) => {
321
+ const existingRows = (await tx.$queryRawUnsafe(`SELECT id
322
+ FROM operations_collaborator_type
323
+ WHERE id = ANY($1::int[])`, normalizedIds));
324
+ if (existingRows.length !== normalizedIds.length) {
325
+ throw new common_1.NotFoundException('One or more collaborator types were not found.');
326
+ }
327
+ await tx.$executeRawUnsafe(`UPDATE operations_collaborator_type AS ct
328
+ SET sort_order = updates.sort_order,
329
+ updated_at = NOW()
330
+ FROM (
331
+ SELECT UNNEST($1::int[]) AS id,
332
+ GENERATE_SERIES(1, array_length($1::int[], 1)) AS sort_order
333
+ ) AS updates
334
+ WHERE ct.id = updates.id`, normalizedIds);
335
+ return this.listCollaboratorTypes(userId, {
336
+ page: 1,
337
+ pageSize: Math.max(normalizedIds.length, 1),
338
+ sortField: 'sortOrder',
339
+ sortOrder: 'asc',
340
+ });
341
+ });
342
+ }
264
343
  async listProjectRoles(userId) {
265
344
  const actor = await this.getActorContext(userId);
266
345
  this.ensureCollaborator(actor);
@@ -409,11 +488,54 @@ let OperationsService = OperationsService_1 = class OperationsService {
409
488
  recentTimesheets,
410
489
  };
411
490
  }
412
- async listCollaborators(userId) {
491
+ async listCollaborators(userId, filters = {}) {
492
+ var _a, _b;
413
493
  const actor = await this.getActorContext(userId);
414
494
  this.ensureCollaborator(actor);
415
495
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'c.id', actor.isDirector);
416
- return this.queryRows(`SELECT c.id,
496
+ const pagination = this.shouldPaginate(filters)
497
+ ? this.normalizePaginationParams(filters, {
498
+ defaultSortField: 'displayName',
499
+ defaultSortOrder: 'asc',
500
+ allowedSortFields: ['displayName', 'code', 'joinedAt', 'status'],
501
+ })
502
+ : null;
503
+ const params = [...filter.params];
504
+ const where = ['c.deleted_at IS NULL', filter.clause];
505
+ if (filters.status && filters.status !== 'all') {
506
+ where.push(`c.status::text = ${this.param(params, filters.status)}`);
507
+ }
508
+ if (filters.collaboratorTypeId) {
509
+ where.push(`c.collaborator_type_id = ${this.param(params, Number(filters.collaboratorTypeId))}`);
510
+ }
511
+ else if (filters.collaboratorType && filters.collaboratorType !== 'all') {
512
+ const collaboratorTypePlaceholder = this.param(params, filters.collaboratorType);
513
+ where.push(`(
514
+ collaborator_type.slug = ${collaboratorTypePlaceholder}
515
+ OR collaborator_type.name = ${collaboratorTypePlaceholder}
516
+ )`);
517
+ }
518
+ if (filters.departmentId) {
519
+ where.push(`c.department_id = ${this.param(params, Number(filters.departmentId))}`);
520
+ }
521
+ if (filters.jobTitleId) {
522
+ where.push(`c.job_title_id = ${this.param(params, Number(filters.jobTitleId))}`);
523
+ }
524
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
525
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
526
+ where.push(`(
527
+ COALESCE(c.display_name, '') ILIKE ${searchPlaceholder}
528
+ OR COALESCE(person_record.name, '') ILIKE ${searchPlaceholder}
529
+ OR COALESCE(c.code, '') ILIKE ${searchPlaceholder}
530
+ OR COALESCE(department_record.name, '') ILIKE ${searchPlaceholder}
531
+ OR COALESCE(c.department, '') ILIKE ${searchPlaceholder}
532
+ OR COALESCE(job_title_record.name, '') ILIKE ${searchPlaceholder}
533
+ OR COALESCE(c.title, '') ILIKE ${searchPlaceholder}
534
+ OR COALESCE(s.display_name, '') ILIKE ${searchPlaceholder}
535
+ )`);
536
+ }
537
+ const whereClause = where.join(' AND ');
538
+ const baseQuery = `SELECT c.id,
417
539
  c.user_id AS "userId",
418
540
  c.person_id AS "personId",
419
541
  c.code,
@@ -466,10 +588,122 @@ let OperationsService = OperationsService_1 = class OperationsService {
466
588
  oc.created_at DESC
467
589
  LIMIT 1
468
590
  ) hiring_contract ON TRUE
469
- WHERE c.deleted_at IS NULL
470
- AND ${filter.clause}
591
+ WHERE ${whereClause}
471
592
  GROUP BY c.id, person_record.id, collaborator_type.id, department_record.id, job_title_record.id, s.id, hiring_contract.id, hiring_contract.status, hiring_contract.budget_amount
472
- ORDER BY COALESCE(NULLIF(c.display_name, ''), person_record.name) ASC`, filter.params);
593
+ `;
594
+ if (!pagination) {
595
+ return this.queryRows(`${baseQuery}
596
+ ORDER BY COALESCE(NULLIF(c.display_name, ''), person_record.name) ASC`, params);
597
+ }
598
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
599
+ FROM (
600
+ SELECT c.id
601
+ FROM operations_collaborator c
602
+ LEFT JOIN person person_record
603
+ ON person_record.id = c.person_id
604
+ LEFT JOIN operations_collaborator_type collaborator_type
605
+ ON collaborator_type.id = c.collaborator_type_id
606
+ AND collaborator_type.deleted_at IS NULL
607
+ LEFT JOIN operations_department department_record
608
+ ON department_record.id = c.department_id
609
+ AND department_record.deleted_at IS NULL
610
+ LEFT JOIN operations_job_title job_title_record
611
+ ON job_title_record.id = c.job_title_id
612
+ AND job_title_record.deleted_at IS NULL
613
+ LEFT JOIN operations_collaborator s
614
+ ON s.id = c.supervisor_collaborator_id
615
+ WHERE ${whereClause}
616
+ GROUP BY c.id, person_record.id, collaborator_type.id, department_record.id, job_title_record.id, s.id
617
+ ) collaborator_rows`, params);
618
+ const sortColumn = (_a = {
619
+ displayName: `COALESCE(NULLIF(c.display_name, ''), person_record.name)`,
620
+ code: 'c.code',
621
+ joinedAt: 'c.joined_at',
622
+ status: 'c.status',
623
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : `COALESCE(NULLIF(c.display_name, ''), person_record.name)`;
624
+ const queryParams = [...params];
625
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
626
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
627
+ const rows = await this.queryRows(`${baseQuery}
628
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, c.id ASC
629
+ LIMIT ${limitPlaceholder}
630
+ OFFSET ${offsetPlaceholder}`, queryParams);
631
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
632
+ }
633
+ async getCollaboratorStats(userId, filters = {}) {
634
+ var _a, _b, _c, _d, _e;
635
+ const actor = await this.getActorContext(userId);
636
+ this.ensureCollaborator(actor);
637
+ const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'c.id', actor.isDirector);
638
+ const params = [...filter.params];
639
+ const where = ['c.deleted_at IS NULL', filter.clause];
640
+ if (filters.status && filters.status !== 'all') {
641
+ where.push(`c.status::text = ${this.param(params, filters.status)}`);
642
+ }
643
+ if (filters.collaboratorTypeId) {
644
+ where.push(`c.collaborator_type_id = ${this.param(params, Number(filters.collaboratorTypeId))}`);
645
+ }
646
+ else if (filters.collaboratorType && filters.collaboratorType !== 'all') {
647
+ const collaboratorTypePlaceholder = this.param(params, filters.collaboratorType);
648
+ where.push(`(
649
+ collaborator_type.slug = ${collaboratorTypePlaceholder}
650
+ OR collaborator_type.name = ${collaboratorTypePlaceholder}
651
+ )`);
652
+ }
653
+ if (filters.departmentId) {
654
+ where.push(`c.department_id = ${this.param(params, Number(filters.departmentId))}`);
655
+ }
656
+ if (filters.jobTitleId) {
657
+ where.push(`c.job_title_id = ${this.param(params, Number(filters.jobTitleId))}`);
658
+ }
659
+ const normalizedSearch = String((_a = filters.search) !== null && _a !== void 0 ? _a : '').trim();
660
+ if (normalizedSearch) {
661
+ const searchPlaceholder = this.param(params, `%${normalizedSearch}%`);
662
+ where.push(`(
663
+ COALESCE(c.display_name, '') ILIKE ${searchPlaceholder}
664
+ OR COALESCE(person_record.name, '') ILIKE ${searchPlaceholder}
665
+ OR COALESCE(c.code, '') ILIKE ${searchPlaceholder}
666
+ OR COALESCE(department_record.name, '') ILIKE ${searchPlaceholder}
667
+ OR COALESCE(c.department, '') ILIKE ${searchPlaceholder}
668
+ OR COALESCE(job_title_record.name, '') ILIKE ${searchPlaceholder}
669
+ OR COALESCE(c.title, '') ILIKE ${searchPlaceholder}
670
+ OR COALESCE(s.display_name, '') ILIKE ${searchPlaceholder}
671
+ )`);
672
+ }
673
+ const result = await this.querySingle(`SELECT COUNT(DISTINCT c.id)::int AS total,
674
+ COUNT(DISTINCT c.id) FILTER (WHERE c.status = 'active')::int AS active,
675
+ COUNT(DISTINCT c.id) FILTER (WHERE c.status = 'on_leave')::int AS "onLeave",
676
+ COUNT(DISTINCT c.id) FILTER (WHERE hiring_contract.id IS NOT NULL)::int AS "withContracts"
677
+ FROM operations_collaborator c
678
+ LEFT JOIN person person_record
679
+ ON person_record.id = c.person_id
680
+ LEFT JOIN operations_collaborator_type collaborator_type
681
+ ON collaborator_type.id = c.collaborator_type_id
682
+ AND collaborator_type.deleted_at IS NULL
683
+ LEFT JOIN operations_department department_record
684
+ ON department_record.id = c.department_id
685
+ AND department_record.deleted_at IS NULL
686
+ LEFT JOIN operations_job_title job_title_record
687
+ ON job_title_record.id = c.job_title_id
688
+ AND job_title_record.deleted_at IS NULL
689
+ LEFT JOIN operations_collaborator s
690
+ ON s.id = c.supervisor_collaborator_id
691
+ LEFT JOIN LATERAL (
692
+ SELECT oc.id
693
+ FROM operations_contract oc
694
+ WHERE oc.related_collaborator_id = c.id
695
+ AND oc.deleted_at IS NULL
696
+ ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
697
+ oc.created_at DESC
698
+ LIMIT 1
699
+ ) hiring_contract ON TRUE
700
+ WHERE ${where.join(' AND ')}`, params);
701
+ return {
702
+ total: Number((_b = result === null || result === void 0 ? void 0 : result.total) !== null && _b !== void 0 ? _b : 0),
703
+ active: Number((_c = result === null || result === void 0 ? void 0 : result.active) !== null && _c !== void 0 ? _c : 0),
704
+ onLeave: Number((_d = result === null || result === void 0 ? void 0 : result.onLeave) !== null && _d !== void 0 ? _d : 0),
705
+ withContracts: Number((_e = result === null || result === void 0 ? void 0 : result.withContracts) !== null && _e !== void 0 ? _e : 0),
706
+ };
473
707
  }
474
708
  async getMyCollaborator(userId) {
475
709
  const actor = await this.getActorContext(userId);
@@ -596,12 +830,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
596
830
  ''
597
831
  ) AS "timesheetProjectNames",
598
832
  tor.request_type AS "timeOffType",
599
- tor.start_date AS "timeOffStartDate",
600
- tor.end_date AS "timeOffEndDate",
833
+ tor.start_date::text AS "timeOffStartDate",
834
+ tor.end_date::text AS "timeOffEndDate",
601
835
  tor.reason AS "timeOffReason",
602
836
  sar.request_scope AS "scheduleRequestScope",
603
- sar.effective_start_date AS "scheduleStartDate",
604
- sar.effective_end_date AS "scheduleEndDate",
837
+ sar.effective_start_date::text AS "scheduleStartDate",
838
+ sar.effective_end_date::text AS "scheduleEndDate",
605
839
  sar.reason AS "scheduleReason"
606
840
  FROM operations_approval a
607
841
  JOIN operations_collaborator requester
@@ -633,8 +867,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
633
867
  tor.collaborator_id AS "collaboratorId",
634
868
  c.display_name AS "collaboratorName",
635
869
  tor.request_type AS "requestType",
636
- tor.start_date AS "startDate",
637
- tor.end_date AS "endDate",
870
+ tor.start_date::text AS "startDate",
871
+ tor.end_date::text AS "endDate",
638
872
  tor.total_days AS "totalDays",
639
873
  tor.status,
640
874
  tor.reason,
@@ -655,8 +889,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
655
889
  sar.collaborator_id AS "collaboratorId",
656
890
  c.display_name AS "collaboratorName",
657
891
  sar.request_scope AS "requestScope",
658
- sar.effective_start_date AS "effectiveStartDate",
659
- sar.effective_end_date AS "effectiveEndDate",
892
+ sar.effective_start_date::text AS "effectiveStartDate",
893
+ sar.effective_end_date::text AS "effectiveEndDate",
660
894
  sar.status,
661
895
  sar.reason,
662
896
  sar.submitted_at AS "submittedAt",
@@ -851,31 +1085,77 @@ let OperationsService = OperationsService_1 = class OperationsService {
851
1085
  });
852
1086
  return this.getCollaboratorByIdForUser(userId, collaboratorId);
853
1087
  }
854
- async listDepartments(userId) {
1088
+ async listDepartments(userId, filters = {}) {
1089
+ var _a, _b;
855
1090
  const actor = await this.getActorContext(userId);
856
1091
  this.ensureCollaborator(actor);
857
- return this.queryRows(`SELECT d.id,
858
- d.slug,
859
- d.code,
860
- d.name,
861
- d.description,
862
- CASE WHEN d.deleted_at IS NULL THEN 'active' ELSE 'inactive' END AS status,
863
- COUNT(DISTINCT c.id)::int AS "collaboratorCount",
864
- d.created_at AS "createdAt",
865
- d.updated_at AS "updatedAt"
866
- FROM operations_department d
867
- LEFT JOIN operations_collaborator c
868
- ON c.deleted_at IS NULL
869
- AND (
870
- c.department_id = d.id
871
- OR (
872
- c.department_id IS NULL
873
- AND LOWER(COALESCE(c.department, '')) = LOWER(d.name)
874
- )
1092
+ const pagination = this.shouldPaginate(filters)
1093
+ ? this.normalizePaginationParams(filters, {
1094
+ defaultSortField: 'name',
1095
+ defaultSortOrder: 'asc',
1096
+ allowedSortFields: ['name', 'code', 'createdAt', 'updatedAt'],
1097
+ })
1098
+ : null;
1099
+ const params = [];
1100
+ const where = [];
1101
+ if (filters.status === 'active') {
1102
+ where.push('d.deleted_at IS NULL');
1103
+ }
1104
+ else if (filters.status === 'inactive') {
1105
+ where.push('d.deleted_at IS NOT NULL');
1106
+ }
1107
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
1108
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
1109
+ where.push(`(
1110
+ COALESCE(d.name, '') ILIKE ${searchPlaceholder}
1111
+ OR COALESCE(d.code, '') ILIKE ${searchPlaceholder}
1112
+ OR COALESCE(d.description, '') ILIKE ${searchPlaceholder}
1113
+ )`);
1114
+ }
1115
+ const whereClause = where.length ? `WHERE ${where.join(' AND ')}` : '';
1116
+ const baseQuery = `SELECT d.id,
1117
+ d.slug,
1118
+ d.code,
1119
+ d.name,
1120
+ d.description,
1121
+ CASE WHEN d.deleted_at IS NULL THEN 'active' ELSE 'inactive' END AS status,
1122
+ COUNT(DISTINCT c.id)::int AS "collaboratorCount",
1123
+ d.created_at AS "createdAt",
1124
+ d.updated_at AS "updatedAt"
1125
+ FROM operations_department d
1126
+ LEFT JOIN operations_collaborator c
1127
+ ON c.deleted_at IS NULL
1128
+ AND (
1129
+ c.department_id = d.id
1130
+ OR (
1131
+ c.department_id IS NULL
1132
+ AND LOWER(COALESCE(c.department, '')) = LOWER(d.name)
875
1133
  )
876
- GROUP BY d.id
877
- ORDER BY CASE WHEN d.deleted_at IS NULL THEN 0 ELSE 1 END ASC,
878
- d.name ASC`);
1134
+ )
1135
+ ${whereClause}
1136
+ GROUP BY d.id`;
1137
+ if (!pagination) {
1138
+ return this.queryRows(`${baseQuery}
1139
+ ORDER BY CASE WHEN d.deleted_at IS NULL THEN 0 ELSE 1 END ASC,
1140
+ d.name ASC`, params);
1141
+ }
1142
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
1143
+ FROM operations_department d
1144
+ ${whereClause}`, params);
1145
+ const sortColumn = (_a = {
1146
+ name: 'd.name',
1147
+ code: 'd.code',
1148
+ createdAt: 'd.created_at',
1149
+ updatedAt: 'd.updated_at',
1150
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : 'd.name';
1151
+ const queryParams = [...params];
1152
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
1153
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
1154
+ const rows = await this.queryRows(`${baseQuery}
1155
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, d.id ASC
1156
+ LIMIT ${limitPlaceholder}
1157
+ OFFSET ${offsetPlaceholder}`, queryParams);
1158
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
879
1159
  }
880
1160
  async listJobTitles(userId) {
881
1161
  const actor = await this.getActorContext(userId);
@@ -1015,7 +1295,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1015
1295
  return this.getDepartmentById(tx, departmentId, true);
1016
1296
  });
1017
1297
  }
1018
- async listProjects(userId) {
1298
+ async listProjects(userId, filters = {}) {
1299
+ var _a, _b;
1019
1300
  const actor = await this.getActorContext(userId);
1020
1301
  const filter = this.buildIdFilter(actor.visibleProjectIds, 'p.id', actor.isDirector);
1021
1302
  const assignmentParams = [];
@@ -1024,7 +1305,30 @@ let OperationsService = OperationsService_1 = class OperationsService {
1024
1305
  MAX(CASE WHEN pa.collaborator_id = ${this.param(assignmentParams, actor.collaboratorId)} THEN pa.role_label END) AS "myRoleLabel",`
1025
1306
  : `NULL::int AS "myAssignmentId",
1026
1307
  NULL::varchar AS "myRoleLabel",`;
1027
- return this.queryRows(`SELECT p.id,
1308
+ const pagination = this.shouldPaginate(filters)
1309
+ ? this.normalizePaginationParams(filters, {
1310
+ defaultSortField: 'name',
1311
+ defaultSortOrder: 'asc',
1312
+ allowedSortFields: ['name', 'code', 'clientName', 'startDate', 'endDate', 'status'],
1313
+ })
1314
+ : null;
1315
+ const params = [...assignmentParams, ...filter.params];
1316
+ const where = ['p.deleted_at IS NULL', filter.clause];
1317
+ if (filters.status && filters.status !== 'all') {
1318
+ where.push(`p.status::text = ${this.param(params, filters.status)}`);
1319
+ }
1320
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
1321
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
1322
+ where.push(`(
1323
+ COALESCE(p.name, '') ILIKE ${searchPlaceholder}
1324
+ OR COALESCE(p.code, '') ILIKE ${searchPlaceholder}
1325
+ OR COALESCE(p.client_name, '') ILIKE ${searchPlaceholder}
1326
+ OR COALESCE(c.name, '') ILIKE ${searchPlaceholder}
1327
+ OR COALESCE(m.display_name, '') ILIKE ${searchPlaceholder}
1328
+ )`);
1329
+ }
1330
+ const whereClause = where.join(' AND ');
1331
+ const baseQuery = `SELECT p.id,
1028
1332
  p.contract_id AS "contractId",
1029
1333
  p.manager_collaborator_id AS "managerCollaboratorId",
1030
1334
  p.code,
@@ -1049,9 +1353,32 @@ let OperationsService = OperationsService_1 = class OperationsService {
1049
1353
  ON pa.project_id = p.id
1050
1354
  AND pa.deleted_at IS NULL
1051
1355
  AND pa.status IN ('planned', 'active')
1052
- WHERE p.deleted_at IS NULL AND ${filter.clause}
1053
- GROUP BY p.id, c.id, m.id
1054
- ORDER BY p.name ASC`, [...assignmentParams, ...filter.params]);
1356
+ WHERE ${whereClause}
1357
+ GROUP BY p.id, c.id, m.id`;
1358
+ if (!pagination) {
1359
+ return this.queryRows(`${baseQuery} ORDER BY p.name ASC`, params);
1360
+ }
1361
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
1362
+ FROM operations_project p
1363
+ LEFT JOIN operations_contract c ON c.id = p.contract_id
1364
+ LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
1365
+ WHERE ${whereClause}`, params);
1366
+ const sortColumn = (_a = {
1367
+ name: 'p.name',
1368
+ code: 'p.code',
1369
+ clientName: 'p.client_name',
1370
+ startDate: 'p.start_date',
1371
+ endDate: 'p.end_date',
1372
+ status: 'p.status',
1373
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : 'p.name';
1374
+ const queryParams = [...params];
1375
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
1376
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
1377
+ const rows = await this.queryRows(`${baseQuery}
1378
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, p.id ASC
1379
+ LIMIT ${limitPlaceholder}
1380
+ OFFSET ${offsetPlaceholder}`, queryParams);
1381
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
1055
1382
  }
1056
1383
  async listProjectOptions(userId, paginationParams) {
1057
1384
  var _a, _b;
@@ -1154,7 +1481,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1154
1481
  filters.push(`COALESCE(t.project_id, pa.project_id) = ${this.param(params, paginationParams.projectId)}`);
1155
1482
  }
1156
1483
  if (paginationParams.status) {
1157
- filters.push(`t.status = ${this.param(params, paginationParams.status)}`);
1484
+ filters.push(`t.status::text = ${this.param(params, paginationParams.status)}`);
1158
1485
  }
1159
1486
  const whereClause = filters.join(' AND ');
1160
1487
  const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
@@ -1598,7 +1925,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1598
1925
  this.ensureDirector(actor);
1599
1926
  this.requireFields(data, ['code', 'name']);
1600
1927
  const createdProjectId = await this.prisma.$transaction(async (tx) => {
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;
1928
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
1602
1929
  const created = await tx.$queryRawUnsafe(`INSERT INTO operations_project (
1603
1930
  contract_id,
1604
1931
  manager_collaborator_id,
@@ -1627,28 +1954,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
1627
1954
  if ((_o = data.teamAssignments) === null || _o === void 0 ? void 0 : _o.length) {
1628
1955
  await this.replaceProjectAssignments(tx, projectId, data.teamAssignments);
1629
1956
  }
1630
- if (!data.contractId && data.autoGenerateContractDraft !== false) {
1631
- const contractId = await this.createProjectContractDraft(tx, actor.userId, {
1632
- projectId,
1633
- contractTemplateId: (_p = data.contractTemplateId) !== null && _p !== void 0 ? _p : null,
1634
- projectCode: data.code,
1635
- projectName: data.name,
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,
1646
- });
1647
- await tx.$executeRawUnsafe(`UPDATE operations_project
1648
- SET contract_id = $1,
1649
- updated_at = NOW()
1650
- WHERE id = $2`, contractId, projectId);
1651
- }
1652
1957
  return projectId;
1653
1958
  });
1654
1959
  return this.getProjectById(userId, createdProjectId);
@@ -1673,7 +1978,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1673
1978
  this.pushUpdate(updates, params, 'start_date', data.startDate, 'date');
1674
1979
  this.pushUpdate(updates, params, 'end_date', data.endDate, 'date');
1675
1980
  await this.prisma.$transaction(async (tx) => {
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;
1981
+ var _a, _b;
1677
1982
  if (updates.length) {
1678
1983
  params.push(projectId);
1679
1984
  await tx.$executeRawUnsafe(`UPDATE operations_project
@@ -1687,30 +1992,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1687
1992
  const nextContractId = data.contractId !== undefined
1688
1993
  ? data.contractId
1689
1994
  : ((_b = (_a = currentProject.relatedContract) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null);
1690
- const shouldGenerateDraft = !nextContractId && data.autoGenerateContractDraft === true;
1691
- if (shouldGenerateDraft) {
1692
- const contractId = await this.createProjectContractDraft(tx, actor.userId, {
1693
- projectId,
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,
1707
- });
1708
- await tx.$executeRawUnsafe(`UPDATE operations_project
1709
- SET contract_id = $1,
1710
- updated_at = NOW()
1711
- WHERE id = $2`, contractId, projectId);
1712
- }
1713
- else if (nextContractId &&
1995
+ if (nextContractId &&
1714
1996
  (data.monthlyHourCap !== undefined || data.billingModel !== undefined)) {
1715
1997
  const contractUpdates = [];
1716
1998
  const contractParams = [];
@@ -1729,138 +2011,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1729
2011
  });
1730
2012
  return this.getProjectById(userId, projectId);
1731
2013
  }
1732
- async listContractTemplates(userId) {
1733
- const actor = await this.getActorContext(userId);
1734
- this.ensureDirector(actor);
1735
- return this.queryRows(`SELECT t.id,
1736
- t.slug,
1737
- t.code,
1738
- t.name,
1739
- t.description,
1740
- t.contract_category AS "contractCategory",
1741
- t.contract_type AS "contractType",
1742
- t.billing_model AS "billingModel",
1743
- t.signature_status AS "signatureStatus",
1744
- t.is_active AS "isActive",
1745
- t.status,
1746
- t.content_html AS "contentHtml",
1747
- t.created_at AS "createdAt",
1748
- t.updated_at AS "updatedAt",
1749
- COUNT(DISTINCT c.id)::int AS "usageCount"
1750
- FROM operations_contract_template t
1751
- LEFT JOIN operations_contract c
1752
- ON c.contract_template_id = t.id
1753
- AND c.deleted_at IS NULL
1754
- WHERE t.deleted_at IS NULL
1755
- GROUP BY t.id
1756
- ORDER BY CASE
1757
- WHEN t.status = 'active' THEN 0
1758
- WHEN t.status = 'draft' THEN 1
1759
- WHEN t.status = 'inactive' THEN 2
1760
- ELSE 3
1761
- END,
1762
- t.name ASC`);
1763
- }
1764
- async getContractTemplateById(userId, templateId) {
1765
- const actor = await this.getActorContext(userId);
1766
- this.ensureDirector(actor);
1767
- return this.getContractTemplateRecord(this.prisma, templateId);
1768
- }
1769
- async createContractTemplate(userId, data) {
1770
- const actor = await this.getActorContext(userId);
1771
- this.ensureDirector(actor);
1772
- const name = this.normalizeOptionalText(data.name);
1773
- if (!name) {
1774
- throw new common_1.BadRequestException('Contract template name is required.');
1775
- }
1776
- return this.prisma.$transaction(async (tx) => {
1777
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1778
- await this.assertContractTemplateNameAvailable(tx, name);
1779
- const nextCode = (_b = (_a = this.normalizeOptionalText(data.code)) === null || _a === void 0 ? void 0 : _a.toUpperCase()) !== null && _b !== void 0 ? _b : null;
1780
- if (nextCode) {
1781
- await this.assertContractTemplateCodeAvailable(tx, nextCode);
1782
- }
1783
- const nextStatus = (_c = data.status) !== null && _c !== void 0 ? _c : 'active';
1784
- const isActive = (_d = data.isActive) !== null && _d !== void 0 ? _d : !['inactive', 'archived'].includes(nextStatus);
1785
- const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_contract_template (
1786
- slug,
1787
- code,
1788
- name,
1789
- description,
1790
- contract_category,
1791
- contract_type,
1792
- billing_model,
1793
- signature_status,
1794
- is_active,
1795
- status,
1796
- content_html,
1797
- created_at,
1798
- updated_at
1799
- ) VALUES (
1800
- $1, $2, $3, $4,
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,
1806
- NOW(), NOW()
1807
- )
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)));
1809
- const templateId = (_j = created[0]) === null || _j === void 0 ? void 0 : _j.id;
1810
- if (!templateId) {
1811
- throw new common_1.BadRequestException('Unable to create the contract template.');
1812
- }
1813
- return this.getContractTemplateRecord(tx, templateId, true);
1814
- });
1815
- }
1816
- async updateContractTemplate(userId, templateId, data) {
1817
- const actor = await this.getActorContext(userId);
1818
- this.ensureDirector(actor);
1819
- return this.prisma.$transaction(async (tx) => {
1820
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
1821
- const current = await this.getContractTemplateRecord(tx, templateId, true);
1822
- const nextName = data.name !== undefined
1823
- ? this.normalizeOptionalText(data.name)
1824
- : current.name;
1825
- if (!nextName) {
1826
- throw new common_1.BadRequestException('Contract template name is required.');
1827
- }
1828
- if (String(nextName).toLowerCase() !== String(current.name).toLowerCase()) {
1829
- await this.assertContractTemplateNameAvailable(tx, nextName, templateId);
1830
- }
1831
- const nextCode = data.code !== undefined
1832
- ? (_b = (_a = this.normalizeOptionalText(data.code)) === null || _a === void 0 ? void 0 : _a.toUpperCase()) !== null && _b !== void 0 ? _b : null
1833
- : ((_c = current.code) !== null && _c !== void 0 ? _c : null);
1834
- if (nextCode) {
1835
- await this.assertContractTemplateCodeAvailable(tx, nextCode, templateId);
1836
- }
1837
- const nextStatus = (_e = (_d = data.status) !== null && _d !== void 0 ? _d : current.status) !== null && _e !== void 0 ? _e : 'active';
1838
- const nextIsActive = (_f = data.isActive) !== null && _f !== void 0 ? _f : !['inactive', 'archived'].includes(nextStatus);
1839
- const nextSlug = String(nextName).toLowerCase() !== String(current.name).toLowerCase()
1840
- ? await this.generateUniqueContractTemplateSlug(tx, nextName, templateId)
1841
- : current.slug;
1842
- await tx.$executeRawUnsafe(`UPDATE operations_contract_template
1843
- SET slug = $1,
1844
- code = $2,
1845
- name = $3,
1846
- description = $4,
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,
1851
- is_active = $9,
1852
- status = $10::text::operations_contract_template_status_c9d2e90231_enum,
1853
- content_html = $11,
1854
- updated_at = NOW()
1855
- WHERE id = $12`, nextSlug, nextCode, nextName, data.description !== undefined
1856
- ? this.normalizeOptionalText(data.description)
1857
- : ((_g = current.description) !== null && _g !== void 0 ? _g : null), (_j = (_h = data.contractCategory) !== null && _h !== void 0 ? _h : current.contractCategory) !== null && _j !== void 0 ? _j : 'client', (_l = (_k = data.contractType) !== null && _k !== void 0 ? _k : current.contractType) !== null && _l !== void 0 ? _l : 'service_agreement', (_o = (_m = data.billingModel) !== null && _m !== void 0 ? _m : current.billingModel) !== null && _o !== void 0 ? _o : 'time_and_material', (_q = (_p = data.signatureStatus) !== null && _p !== void 0 ? _p : current.signatureStatus) !== null && _q !== void 0 ? _q : 'not_started', nextIsActive, nextStatus, data.contentHtml !== undefined
1858
- ? this.normalizeOptionalText(data.contentHtml)
1859
- : ((_r = current.contentHtml) !== null && _r !== void 0 ? _r : null), templateId);
1860
- return this.getContractTemplateRecord(tx, templateId, true);
1861
- });
1862
- }
1863
- async listContracts(userId) {
2014
+ async listContracts(userId, filters = {}) {
2015
+ var _a, _b;
1864
2016
  const actor = await this.getActorContext(userId);
1865
2017
  const params = [];
1866
2018
  const accessClause = actor.isDirector
@@ -1872,10 +2024,41 @@ let OperationsService = OperationsService_1 = class OperationsService {
1872
2024
  FROM operations_project p_access
1873
2025
  WHERE p_access.contract_id = c.id
1874
2026
  AND p_access.deleted_at IS NULL
1875
- AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
2027
+ AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
1876
2028
  )
1877
2029
  )`;
1878
- return this.queryRows(`SELECT c.id,
2030
+ const pagination = this.shouldPaginate(filters)
2031
+ ? this.normalizePaginationParams(filters, {
2032
+ defaultSortField: 'name',
2033
+ defaultSortOrder: 'asc',
2034
+ allowedSortFields: ['name', 'code', 'clientName', 'startDate', 'endDate', 'status'],
2035
+ })
2036
+ : null;
2037
+ const where = [accessClause];
2038
+ if (filters.status && filters.status !== 'all') {
2039
+ where.push(`c.status::text = ${this.param(params, filters.status)}`);
2040
+ }
2041
+ if (filters.contractCategory && filters.contractCategory !== 'all') {
2042
+ where.push(`c.contract_category = ${this.param(params, filters.contractCategory)}::text::operations_contract_contract_category_70d553ea09_enum`);
2043
+ }
2044
+ if (filters.originType && filters.originType !== 'all') {
2045
+ where.push(`c.origin_type = ${this.param(params, filters.originType)}::text::operations_contract_origin_type_07a7cc2b5d_enum`);
2046
+ }
2047
+ if (filters.isActive === 'true' || filters.isActive === 'false') {
2048
+ where.push(`c.is_active = ${this.param(params, filters.isActive === 'true')}`);
2049
+ }
2050
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
2051
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
2052
+ where.push(`(
2053
+ COALESCE(c.name, '') ILIKE ${searchPlaceholder}
2054
+ OR COALESCE(c.code, '') ILIKE ${searchPlaceholder}
2055
+ OR COALESCE(c.client_name, '') ILIKE ${searchPlaceholder}
2056
+ OR COALESCE(m.display_name, '') ILIKE ${searchPlaceholder}
2057
+ OR COALESCE(linked.display_name, '') ILIKE ${searchPlaceholder}
2058
+ )`);
2059
+ }
2060
+ const whereClause = where.join(' AND ');
2061
+ const baseQuery = `SELECT c.id,
1879
2062
  c.code,
1880
2063
  c.name,
1881
2064
  c.contract_category AS "contractCategory",
@@ -1886,10 +2069,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
1886
2069
  c.billing_model AS "billingModel",
1887
2070
  c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
1888
2071
  c.related_collaborator_id AS "relatedCollaboratorId",
1889
- c.contract_template_id AS "contractTemplateId",
1890
- template.name AS "contractTemplateName",
1891
- template.slug AS "contractTemplateSlug",
1892
- template.code AS "contractTemplateCode",
1893
2072
  c.origin_type AS "originType",
1894
2073
  c.origin_id AS "originId",
1895
2074
  c.start_date AS "startDate",
@@ -1905,17 +2084,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
1905
2084
  m.display_name AS "accountManagerName",
1906
2085
  linked.display_name AS "relatedCollaboratorName",
1907
2086
  MAX(COALESCE(primary_party.display_name, linked.display_name, c.client_name)) AS "mainRelatedPartyName",
1908
- MAX(COALESCE(financials.value_amount, 0)) AS "valueAmount",
1909
- MAX(COALESCE(financials.payment_amount, 0)) AS "paymentAmount",
1910
- MAX(COALESCE(financials.revenue_amount, 0)) AS "revenueAmount",
1911
- MAX(COALESCE(financials.fine_amount, 0)) AS "fineAmount",
1912
2087
  MAX(COALESCE(pdf_document.file_name, '')) AS "currentPdfFileName",
1913
2088
  COUNT(DISTINCT p.id)::int AS "projectCount"
1914
2089
  FROM operations_contract c
1915
2090
  LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
1916
2091
  LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
1917
- LEFT JOIN operations_contract_template template
1918
- ON template.id = c.contract_template_id
1919
2092
  LEFT JOIN LATERAL (
1920
2093
  SELECT cp.display_name
1921
2094
  FROM operations_contract_party cp
@@ -1924,16 +2097,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
1924
2097
  ORDER BY cp.is_primary DESC, cp.id ASC
1925
2098
  LIMIT 1
1926
2099
  ) primary_party ON TRUE
1927
- LEFT JOIN LATERAL (
1928
- SELECT
1929
- SUM(CASE WHEN term_type = 'value' THEN amount ELSE 0 END) AS value_amount,
1930
- SUM(CASE WHEN term_type = 'payment' THEN amount ELSE 0 END) AS payment_amount,
1931
- SUM(CASE WHEN term_type = 'revenue' THEN amount ELSE 0 END) AS revenue_amount,
1932
- SUM(CASE WHEN term_type = 'fine' THEN amount ELSE 0 END) AS fine_amount
1933
- FROM operations_contract_financial_term ft
1934
- WHERE ft.contract_id = c.id
1935
- AND ft.deleted_at IS NULL
1936
- ) financials ON TRUE
1937
2100
  LEFT JOIN LATERAL (
1938
2101
  SELECT cd.file_name
1939
2102
  FROM operations_contract_document cd
@@ -1947,9 +2110,71 @@ let OperationsService = OperationsService_1 = class OperationsService {
1947
2110
  LEFT JOIN operations_project p
1948
2111
  ON p.contract_id = c.id
1949
2112
  AND p.deleted_at IS NULL
1950
- WHERE ${accessClause}
1951
- GROUP BY c.id, m.id, linked.id, template.id
1952
- ORDER BY COALESCE(c.name, c.code, CONCAT('draft-', c.id)) ASC`, params);
2113
+ WHERE ${whereClause}
2114
+ GROUP BY c.id, m.id, linked.id`;
2115
+ if (!pagination) {
2116
+ return this.queryRows(`${baseQuery}
2117
+ ORDER BY COALESCE(c.name, c.code, CONCAT('draft-', c.id)) ASC`, params);
2118
+ }
2119
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
2120
+ FROM operations_contract c
2121
+ LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
2122
+ LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
2123
+ WHERE ${whereClause}`, params);
2124
+ const sortColumn = (_a = {
2125
+ name: `COALESCE(c.name, c.code, CONCAT('draft-', c.id))`,
2126
+ code: 'c.code',
2127
+ clientName: 'c.client_name',
2128
+ startDate: 'c.start_date',
2129
+ endDate: 'c.end_date',
2130
+ status: 'c.status',
2131
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : `COALESCE(c.name, c.code, CONCAT('draft-', c.id))`;
2132
+ const queryParams = [...params];
2133
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
2134
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
2135
+ const rows = await this.queryRows(`${baseQuery}
2136
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, c.id ASC
2137
+ LIMIT ${limitPlaceholder}
2138
+ OFFSET ${offsetPlaceholder}`, queryParams);
2139
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
2140
+ }
2141
+ async getContractStats(userId) {
2142
+ var _a, _b, _c, _d;
2143
+ const actor = await this.getActorContext(userId);
2144
+ const params = [];
2145
+ const accessClause = actor.isDirector
2146
+ ? 'c.deleted_at IS NULL'
2147
+ : `c.deleted_at IS NULL AND (
2148
+ c.related_collaborator_id = ANY(${this.param(params, actor.visibleCollaboratorIds)}::int[])
2149
+ OR EXISTS (
2150
+ SELECT 1
2151
+ FROM operations_project p_access
2152
+ WHERE p_access.contract_id = c.id
2153
+ AND p_access.deleted_at IS NULL
2154
+ AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
2155
+ )
2156
+ )`;
2157
+ const stats = await this.querySingle(`SELECT COUNT(*)::int AS total,
2158
+ COUNT(*) FILTER (WHERE c.is_active = true)::int AS active,
2159
+ COUNT(*) FILTER (WHERE c.is_active = false)::int AS inactive,
2160
+ COUNT(*) FILTER (
2161
+ WHERE EXISTS (
2162
+ SELECT 1
2163
+ FROM operations_contract_document cd
2164
+ WHERE cd.contract_id = c.id
2165
+ AND cd.deleted_at IS NULL
2166
+ AND cd.is_current = true
2167
+ AND cd.document_type IN ('source_upload', 'generated_pdf')
2168
+ )
2169
+ )::int AS "withFile"
2170
+ FROM operations_contract c
2171
+ WHERE ${accessClause}`, params);
2172
+ return {
2173
+ total: Number((_a = stats === null || stats === void 0 ? void 0 : stats.total) !== null && _a !== void 0 ? _a : 0),
2174
+ active: Number((_b = stats === null || stats === void 0 ? void 0 : stats.active) !== null && _b !== void 0 ? _b : 0),
2175
+ inactive: Number((_c = stats === null || stats === void 0 ? void 0 : stats.inactive) !== null && _c !== void 0 ? _c : 0),
2176
+ withFile: Number((_d = stats === null || stats === void 0 ? void 0 : stats.withFile) !== null && _d !== void 0 ? _d : 0),
2177
+ };
1953
2178
  }
1954
2179
  async getContractById(userId, contractId) {
1955
2180
  var _a, _b, _c;
@@ -1965,10 +2190,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
1965
2190
  c.billing_model AS "billingModel",
1966
2191
  c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
1967
2192
  c.related_collaborator_id AS "relatedCollaboratorId",
1968
- c.contract_template_id AS "contractTemplateId",
1969
- template.name AS "contractTemplateName",
1970
- template.slug AS "contractTemplateSlug",
1971
- template.code AS "contractTemplateCode",
1972
2193
  c.origin_type AS "originType",
1973
2194
  c.origin_id AS "originId",
1974
2195
  c.start_date AS "startDate",
@@ -1987,8 +2208,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
1987
2208
  FROM operations_contract c
1988
2209
  LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
1989
2210
  LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
1990
- LEFT JOIN operations_contract_template template
1991
- ON template.id = c.contract_template_id
1992
2211
  WHERE c.id = $1
1993
2212
  AND c.deleted_at IS NULL`, [contractId]);
1994
2213
  if (!contract) {
@@ -2015,7 +2234,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2015
2234
  throw new common_1.ForbiddenException('You do not have access to this contract.');
2016
2235
  }
2017
2236
  }
2018
- const [projects, scheduleSummary, parties, signatures, financialTerms, documents, revisions, history] = await Promise.all([
2237
+ const [projects, scheduleSummary, parties, documents, history] = await Promise.all([
2019
2238
  this.queryRows(`SELECT id, code, name, status
2020
2239
  FROM operations_project
2021
2240
  WHERE contract_id = $1
@@ -2044,27 +2263,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2044
2263
  WHERE contract_id = $1
2045
2264
  AND deleted_at IS NULL
2046
2265
  ORDER BY is_primary DESC, id ASC`, [contractId]),
2047
- this.queryRows(`SELECT id,
2048
- signer_name AS "signerName",
2049
- signer_role AS "signerRole",
2050
- signer_email AS "signerEmail",
2051
- signer_status AS status,
2052
- signed_at AS "signedAt"
2053
- FROM operations_contract_signature
2054
- WHERE contract_id = $1
2055
- AND deleted_at IS NULL
2056
- ORDER BY id ASC`, [contractId]),
2057
- this.queryRows(`SELECT id,
2058
- term_type AS "termType",
2059
- label,
2060
- amount,
2061
- recurrence,
2062
- due_day AS "dueDay",
2063
- notes
2064
- FROM operations_contract_financial_term
2065
- WHERE contract_id = $1
2066
- AND deleted_at IS NULL
2067
- ORDER BY id ASC`, [contractId]),
2068
2266
  this.queryRows(`SELECT id,
2069
2267
  document_type AS "documentType",
2070
2268
  file_id AS "fileId",
@@ -2080,16 +2278,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2080
2278
  WHERE contract_id = $1
2081
2279
  AND deleted_at IS NULL
2082
2280
  ORDER BY is_current DESC, id DESC`, [contractId]),
2083
- this.queryRows(`SELECT id,
2084
- revision_type AS "revisionType",
2085
- title,
2086
- effective_date AS "effectiveDate",
2087
- status,
2088
- summary
2089
- FROM operations_contract_revision
2090
- WHERE contract_id = $1
2091
- AND deleted_at IS NULL
2092
- ORDER BY effective_date DESC NULLS LAST, id DESC`, [contractId]),
2093
2281
  this.queryRows(`SELECT id,
2094
2282
  actor_user_id AS "actorUserId",
2095
2283
  action,
@@ -2103,17 +2291,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
2103
2291
  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,
2104
2292
  scheduleSummary,
2105
2293
  parties,
2106
- signatures,
2107
- financialTerms,
2108
2294
  documents,
2109
- revisions,
2110
2295
  history });
2111
2296
  }
2112
2297
  async createContract(userId, data) {
2113
2298
  const actor = await this.getActorContext(userId);
2114
2299
  this.ensureDirector(actor);
2115
2300
  const createdId = await this.prisma.$transaction(async (tx) => {
2116
- 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;
2301
+ 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;
2117
2302
  const normalizedCode = (_b = (_a = this.normalizeOptionalText(data.code)) === null || _a === void 0 ? void 0 : _a.toUpperCase()) !== null && _b !== void 0 ? _b : (await this.generateContractCode(tx));
2118
2303
  const created = await tx.$queryRawUnsafe(`INSERT INTO operations_contract (
2119
2304
  code,
@@ -2126,7 +2311,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2126
2311
  billing_model,
2127
2312
  account_manager_collaborator_id,
2128
2313
  related_collaborator_id,
2129
- contract_template_id,
2130
2314
  origin_type,
2131
2315
  origin_id,
2132
2316
  start_date,
@@ -2152,24 +2336,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
2152
2336
  $8::operations_contract_billing_model_409dc7fea2_enum,
2153
2337
  $9,
2154
2338
  $10,
2155
- $11,
2156
- $12::operations_contract_origin_type_07a7cc2b5d_enum,
2157
- $13,
2158
- $14::date,
2159
- $15::date, $16::date, $17::date, $18, $19,
2160
- $20::operations_contract_status_a0395962df_enum,
2161
- $21::operations_contract_creation_mode_98ba669209_enum,
2339
+ $11::operations_contract_origin_type_07a7cc2b5d_enum,
2340
+ $12,
2341
+ $13::date,
2342
+ $14::date, $15::date, $16::date, $17, $18,
2343
+ $19::operations_contract_status_a0395962df_enum,
2344
+ $20::operations_contract_creation_mode_98ba669209_enum,
2345
+ $21,
2162
2346
  $22,
2163
2347
  $23,
2164
- $24,
2165
2348
  NOW(), NOW()
2166
2349
  )
2167
- RETURNING id`, normalizedCode, this.normalizeOptionalText(data.name), (_c = data.contractCategory) !== null && _c !== void 0 ? _c : 'client', (_d = data.contractType) !== null && _d !== void 0 ? _d : 'service_agreement', this.normalizeOptionalText(data.clientName), (_e = data.signatureStatus) !== null && _e !== void 0 ? _e : 'not_started', (_f = data.isActive) !== null && _f !== void 0 ? _f : true, (_g = data.billingModel) !== null && _g !== void 0 ? _g : 'time_and_material', (_h = data.accountManagerCollaboratorId) !== null && _h !== void 0 ? _h : null, (_j = data.relatedCollaboratorId) !== null && _j !== void 0 ? _j : null, (_k = data.contractTemplateId) !== null && _k !== void 0 ? _k : null, (_l = data.originType) !== null && _l !== void 0 ? _l : 'manual', (_m = data.originId) !== null && _m !== void 0 ? _m : null, this.normalizeOptionalText((_o = data.startDate) !== null && _o !== void 0 ? _o : null), (_p = data.endDate) !== null && _p !== void 0 ? _p : null, (_q = data.signedAt) !== null && _q !== void 0 ? _q : null, (_s = (_r = data.effectiveDate) !== null && _r !== void 0 ? _r : data.startDate) !== null && _s !== void 0 ? _s : null, (_t = data.budgetAmount) !== null && _t !== void 0 ? _t : null, (_u = data.monthlyHourCap) !== null && _u !== void 0 ? _u : null, (_v = data.status) !== null && _v !== void 0 ? _v : 'draft', (_w = data.creationMode) !== null && _w !== void 0 ? _w : 'blank', (_x = data.wizardStep) !== null && _x !== void 0 ? _x : 0, (_y = data.description) !== null && _y !== void 0 ? _y : null, (_z = data.contentHtml) !== null && _z !== void 0 ? _z : null);
2168
- const contractId = (_0 = created[0]) === null || _0 === void 0 ? void 0 : _0.id;
2350
+ RETURNING id`, normalizedCode, this.normalizeOptionalText(data.name), (_c = data.contractCategory) !== null && _c !== void 0 ? _c : 'client', (_d = data.contractType) !== null && _d !== void 0 ? _d : 'service_agreement', this.normalizeOptionalText(data.clientName), (_e = data.signatureStatus) !== null && _e !== void 0 ? _e : 'not_started', (_f = data.isActive) !== null && _f !== void 0 ? _f : true, (_g = data.billingModel) !== null && _g !== void 0 ? _g : 'time_and_material', (_h = data.accountManagerCollaboratorId) !== null && _h !== void 0 ? _h : null, (_j = data.relatedCollaboratorId) !== null && _j !== void 0 ? _j : null, (_k = data.originType) !== null && _k !== void 0 ? _k : 'manual', (_l = data.originId) !== null && _l !== void 0 ? _l : null, this.normalizeOptionalText((_m = data.startDate) !== null && _m !== void 0 ? _m : null), (_o = data.endDate) !== null && _o !== void 0 ? _o : null, (_p = data.signedAt) !== null && _p !== void 0 ? _p : null, (_r = (_q = data.effectiveDate) !== null && _q !== void 0 ? _q : data.startDate) !== null && _r !== void 0 ? _r : null, (_s = data.budgetAmount) !== null && _s !== void 0 ? _s : null, (_t = data.monthlyHourCap) !== null && _t !== void 0 ? _t : null, (_u = data.status) !== null && _u !== void 0 ? _u : 'draft', (_v = data.creationMode) !== null && _v !== void 0 ? _v : 'blank', (_w = data.wizardStep) !== null && _w !== void 0 ? _w : 0, (_x = data.description) !== null && _x !== void 0 ? _x : null, (_y = data.contentHtml) !== null && _y !== void 0 ? _y : null);
2351
+ const contractId = (_z = created[0]) === null || _z === void 0 ? void 0 : _z.id;
2169
2352
  await this.replaceContractParties(tx, contractId, data.parties);
2170
- await this.replaceContractSignatures(tx, contractId, data.signatures);
2171
- await this.replaceContractFinancialTerms(tx, contractId, data.financialTerms);
2172
- await this.replaceContractRevisions(tx, contractId, data.revisions);
2173
2353
  if (data.replaceUploadedPdfDocument) {
2174
2354
  await this.replaceContractDocument(tx, contractId, 'source_upload', data.replaceUploadedPdfDocument);
2175
2355
  }
@@ -2180,258 +2360,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
2180
2360
  });
2181
2361
  return this.getContractById(userId, createdId);
2182
2362
  }
2183
- async createContractDraft(userId, data) {
2184
- const actor = await this.getActorContext(userId);
2185
- this.ensureDirector(actor);
2186
- const creationMode = this.normalizeEnumValue(data.creationMode, CONTRACT_CREATION_MODE_VALUES, 'blank');
2187
- const selectedTemplate = data.templateId && data.templateId > 0
2188
- ? await this.getContractTemplateById(userId, data.templateId)
2189
- : null;
2190
- const duplicateSource = data.duplicateFromId && data.duplicateFromId > 0
2191
- ? await this.getContractById(userId, data.duplicateFromId)
2192
- : null;
2193
- const storedSourceFile = data.sourceFileId && data.sourceFileId > 0
2194
- ? await this.prisma.file.findUnique({
2195
- where: { id: data.sourceFileId },
2196
- include: { file_mimetype: true },
2197
- })
2198
- : null;
2199
- if (data.sourceFileId && !storedSourceFile) {
2200
- throw new common_1.NotFoundException('Source contract file not found.');
2201
- }
2202
- const createdId = await this.prisma.$transaction(async (tx) => {
2203
- 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, _8, _9, _10, _11, _12, _13, _14, _15, _16;
2204
- const generatedCode = await this.generateContractCode(tx);
2205
- const duplicateNameBase = (_b = (_a = this.normalizeOptionalText(duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.name)) !== null && _a !== void 0 ? _a : this.normalizeOptionalText(duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.code)) !== null && _b !== void 0 ? _b : 'Contract';
2206
- const created = await tx.$queryRawUnsafe(`INSERT INTO operations_contract (
2207
- code,
2208
- name,
2209
- contract_category,
2210
- contract_type,
2211
- client_name,
2212
- signature_status,
2213
- is_active,
2214
- billing_model,
2215
- account_manager_collaborator_id,
2216
- related_collaborator_id,
2217
- contract_template_id,
2218
- origin_type,
2219
- origin_id,
2220
- start_date,
2221
- end_date,
2222
- signed_at,
2223
- effective_date,
2224
- budget_amount,
2225
- monthly_hour_cap,
2226
- status,
2227
- creation_mode,
2228
- wizard_step,
2229
- description,
2230
- content_html,
2231
- created_by_user_id,
2232
- updated_by_user_id,
2233
- created_at,
2234
- updated_at
2235
- ) VALUES (
2236
- $1,
2237
- $2,
2238
- $3::operations_contract_contract_category_70d553ea09_enum,
2239
- $4::operations_contract_contract_type_48331e2ebf_enum,
2240
- $5,
2241
- $6::operations_contract_signature_status_2cb7282a7b_enum,
2242
- $7,
2243
- $8::operations_contract_billing_model_409dc7fea2_enum,
2244
- $9,
2245
- $10,
2246
- $11,
2247
- $12::operations_contract_origin_type_07a7cc2b5d_enum,
2248
- $13,
2249
- $14::date,
2250
- $15::date,
2251
- $16::date,
2252
- $17::date,
2253
- $18,
2254
- $19,
2255
- $20::operations_contract_status_a0395962df_enum,
2256
- $21::operations_contract_creation_mode_98ba669209_enum,
2257
- $22,
2258
- $23,
2259
- $24,
2260
- $25,
2261
- $25,
2262
- NOW(),
2263
- NOW()
2264
- )
2265
- RETURNING id`, generatedCode, creationMode === 'duplicate'
2266
- ? `${duplicateNameBase} Copy`
2267
- : this.normalizeOptionalText(selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.name), (_d = (_c = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.contractCategory) !== null && _c !== void 0 ? _c : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contractCategory) !== null && _d !== void 0 ? _d : 'client', (_f = (_e = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.contractType) !== null && _e !== void 0 ? _e : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contractType) !== null && _f !== void 0 ? _f : 'service_agreement', this.normalizeOptionalText(duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.clientName), (_h = (_g = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.signatureStatus) !== null && _g !== void 0 ? _g : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.signatureStatus) !== null && _h !== void 0 ? _h : 'not_started', (_j = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.isActive) !== null && _j !== void 0 ? _j : true, (_l = (_k = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.billingModel) !== null && _k !== void 0 ? _k : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.billingModel) !== null && _l !== void 0 ? _l : 'time_and_material', (_m = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.accountManagerCollaboratorId) !== null && _m !== void 0 ? _m : null, (_o = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.relatedCollaboratorId) !== null && _o !== void 0 ? _o : null, (_q = (_p = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.contractTemplateId) !== null && _p !== void 0 ? _p : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.id) !== null && _q !== void 0 ? _q : null, (_r = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.originType) !== null && _r !== void 0 ? _r : 'manual', (_s = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.originId) !== null && _s !== void 0 ? _s : null, (_t = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.startDate) !== null && _t !== void 0 ? _t : null, (_u = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.endDate) !== null && _u !== void 0 ? _u : null, (_v = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.signedAt) !== null && _v !== void 0 ? _v : null, (_w = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.effectiveDate) !== null && _w !== void 0 ? _w : null, (_x = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.budgetAmount) !== null && _x !== void 0 ? _x : null, (_y = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.monthlyHourCap) !== null && _y !== void 0 ? _y : null, 'draft', creationMode, creationMode === 'upload' ? 0 : creationMode === 'blank' ? 0 : 1, (_0 = (_z = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.description) !== null && _z !== void 0 ? _z : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.description) !== null && _0 !== void 0 ? _0 : null, (_2 = (_1 = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.contentHtml) !== null && _1 !== void 0 ? _1 : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contentHtml) !== null && _2 !== void 0 ? _2 : null, userId);
2268
- const contractId = (_3 = created[0]) === null || _3 === void 0 ? void 0 : _3.id;
2269
- if (duplicateSource) {
2270
- await this.replaceContractParties(tx, contractId, duplicateSource.parties);
2271
- await this.replaceContractSignatures(tx, contractId, duplicateSource.signatures);
2272
- await this.replaceContractFinancialTerms(tx, contractId, duplicateSource.financialTerms);
2273
- await this.replaceContractRevisions(tx, contractId, duplicateSource.revisions);
2274
- const currentSourceDocument = (_4 = duplicateSource.documents.find((document) => document.isCurrent && document.documentType === 'source_upload')) !== null && _4 !== void 0 ? _4 : null;
2275
- if (currentSourceDocument) {
2276
- await this.replaceContractDocument(tx, contractId, 'source_upload', {
2277
- fileId: (_5 = currentSourceDocument.fileId) !== null && _5 !== void 0 ? _5 : null,
2278
- fileName: currentSourceDocument.fileName,
2279
- mimeType: currentSourceDocument.mimeType,
2280
- fileContentBase64: (_6 = currentSourceDocument.fileContentBase64) !== null && _6 !== void 0 ? _6 : null,
2281
- notes: (_7 = currentSourceDocument.notes) !== null && _7 !== void 0 ? _7 : null,
2282
- extractionStatus: (_8 = currentSourceDocument.extractionStatus) !== null && _8 !== void 0 ? _8 : 'skipped',
2283
- extractionSummary: (_9 = currentSourceDocument.extractionSummary) !== null && _9 !== void 0 ? _9 : null,
2284
- });
2285
- }
2286
- }
2287
- if (storedSourceFile) {
2288
- await this.replaceContractDocument(tx, contractId, 'source_upload', {
2289
- fileId: storedSourceFile.id,
2290
- fileName: (_10 = this.normalizeOptionalText(data.sourceFileName)) !== null && _10 !== void 0 ? _10 : storedSourceFile.filename,
2291
- mimeType: (_13 = (_11 = this.normalizeOptionalText(data.sourceMimeType)) !== null && _11 !== void 0 ? _11 : (_12 = storedSourceFile.file_mimetype) === null || _12 === void 0 ? void 0 : _12.name) !== null && _13 !== void 0 ? _13 : 'application/pdf',
2292
- notes: 'Source contract document uploaded during draft creation.',
2293
- extractionStatus: 'pending',
2294
- extractionSummary: null,
2295
- });
2296
- }
2297
- await this.insertContractHistory(tx, contractId, userId, 'draft_created', `Contract draft created with mode ${creationMode}.`, JSON.stringify({
2298
- creationMode,
2299
- templateId: (_14 = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.id) !== null && _14 !== void 0 ? _14 : null,
2300
- duplicateFromId: (_15 = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.id) !== null && _15 !== void 0 ? _15 : null,
2301
- sourceFileId: (_16 = storedSourceFile === null || storedSourceFile === void 0 ? void 0 : storedSourceFile.id) !== null && _16 !== void 0 ? _16 : null,
2302
- }));
2303
- return contractId;
2304
- });
2305
- return this.getContractById(userId, createdId);
2306
- }
2307
- async extractContractSource(userId, contractId, data) {
2308
- var _a, _b;
2309
- const actor = await this.getActorContext(userId);
2310
- this.ensureDirector(actor);
2311
- const contract = await this.getContractById(userId, contractId);
2312
- const sourceDocument = contract.documents.find((document) => document.isCurrent && document.documentType === 'source_upload');
2313
- if (!sourceDocument) {
2314
- throw new common_1.BadRequestException('No source document is attached to this contract draft.');
2315
- }
2316
- await this.prisma.$executeRawUnsafe(`UPDATE operations_contract_document
2317
- SET extraction_status = 'processing',
2318
- updated_at = NOW()
2319
- WHERE id = $1`, sourceDocument.id);
2320
- try {
2321
- const extracted = await this.extractContractDraft(userId, {
2322
- contractId,
2323
- provider: (_a = data.provider) !== null && _a !== void 0 ? _a : null,
2324
- promptMessage: (_b = data.promptMessage) !== null && _b !== void 0 ? _b : null,
2325
- });
2326
- await this.prisma.$executeRawUnsafe(`UPDATE operations_contract_document
2327
- SET extraction_status = 'completed',
2328
- extraction_summary = $2,
2329
- updated_at = NOW()
2330
- WHERE id = $1`, sourceDocument.id, extracted.summary || extracted.description || extracted.name || null);
2331
- await this.insertContractHistory(this.prisma, contractId, userId, 'source_extracted', 'Source contract document extracted with AI.');
2332
- return extracted;
2333
- }
2334
- catch (error) {
2335
- await this.prisma.$executeRawUnsafe(`UPDATE operations_contract_document
2336
- SET extraction_status = 'failed',
2337
- updated_at = NOW()
2338
- WHERE id = $1`, sourceDocument.id);
2339
- throw error;
2340
- }
2341
- }
2342
- async generateContractContent(userId, contractId, data = {}) {
2343
- const actor = await this.getActorContext(userId);
2344
- this.ensureDirector(actor);
2345
- const contract = await this.getContractById(userId, contractId);
2346
- const contentHtml = await this.generateContractContentHtml(contract, data);
2347
- await this.prisma.$transaction(async (tx) => {
2348
- var _a;
2349
- await tx.$executeRawUnsafe(`UPDATE operations_contract
2350
- SET content_html = $2,
2351
- wizard_step = CASE
2352
- WHEN COALESCE(wizard_step, 0) < 4 THEN 4
2353
- ELSE wizard_step
2354
- END,
2355
- updated_by_user_id = $3,
2356
- updated_at = NOW()
2357
- WHERE id = $1`, contractId, contentHtml, userId);
2358
- await this.insertContractHistory(tx, contractId, userId, 'content_generated', 'Contract content generated automatically for editing.', JSON.stringify({
2359
- provider: (_a = data.provider) !== null && _a !== void 0 ? _a : null,
2360
- overwrite: Boolean(data.overwrite),
2361
- hasPrompt: Boolean(this.normalizeOptionalText(data.promptMessage)),
2362
- }));
2363
- });
2364
- return this.getContractById(userId, contractId);
2365
- }
2366
- async reviewContractLegally(userId, contractId, data = {}) {
2367
- const actor = await this.getActorContext(userId);
2368
- this.ensureDirector(actor);
2369
- const contract = await this.getContractById(userId, contractId);
2370
- const review = await this.buildContractLegalReview(contract, data);
2371
- await this.insertContractHistory(this.prisma, contractId, userId, 'legal_reviewed', 'Advisory legal checklist updated for this contract.', JSON.stringify(review));
2372
- return review;
2373
- }
2374
- async generateContractPdf(userId, contractId) {
2375
- const actor = await this.getActorContext(userId);
2376
- this.ensureDirector(actor);
2377
- const contract = await this.getContractById(userId, contractId);
2378
- const pdfBuffer = await this.renderContractPdfBuffer(contract);
2379
- const fileName = this.buildContractPdfFileName(contract);
2380
- const uploadedFile = await this.fileService.upload('operations/contracts/generated', {
2381
- fieldname: 'file',
2382
- originalname: fileName,
2383
- encoding: '7bit',
2384
- mimetype: 'application/pdf',
2385
- size: pdfBuffer.length,
2386
- destination: '',
2387
- filename: fileName,
2388
- path: '',
2389
- buffer: pdfBuffer,
2390
- });
2391
- await this.prisma.$transaction(async (tx) => {
2392
- await this.replaceContractDocument(tx, contractId, 'generated_pdf', {
2393
- fileId: uploadedFile.id,
2394
- fileName,
2395
- mimeType: 'application/pdf',
2396
- notes: 'PDF generated from contract rich text content.',
2397
- extractionStatus: 'skipped',
2398
- extractionSummary: null,
2399
- });
2400
- await this.insertContractHistory(tx, contractId, userId, 'pdf_generated', 'Generated a branded PDF version of the contract.');
2401
- });
2402
- return {
2403
- contractId,
2404
- fileId: uploadedFile.id,
2405
- fileName,
2406
- mimeType: 'application/pdf',
2407
- documentType: 'generated_pdf',
2408
- downloadUrl: `/file/open/${uploadedFile.id}`,
2409
- };
2410
- }
2411
- async extractContractDraft(userId, data) {
2412
- var _a;
2413
- const actor = await this.getActorContext(userId);
2414
- this.ensureDirector(actor);
2415
- const uploadFile = await this.resolveContractExtractionFile(userId, data);
2416
- const aiResult = await this.aiService.chat({
2417
- provider: data.provider === 'gemini' ? 'gemini' : 'openai',
2418
- model: data.provider === 'gemini' ? 'gemini-1.5-flash' : 'gpt-4o-mini',
2419
- message: this.normalizeExtractionString(data.promptMessage) ||
2420
- 'Analyze the attached contract and extract a structured draft for the contract form.',
2421
- systemPrompt: this.buildContractExtractionSystemPrompt(),
2422
- }, uploadFile);
2423
- const parsed = this.parseAiJsonPayload(String((_a = aiResult === null || aiResult === void 0 ? void 0 : aiResult.content) !== null && _a !== void 0 ? _a : ''));
2424
- const draft = this.normalizeContractExtractDraft(parsed);
2425
- const warnings = [...draft.warnings];
2426
- if (uploadFile.mimetype === 'application/msword' ||
2427
- uploadFile.mimetype ===
2428
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
2429
- warnings.push('Word extraction is best-effort. Review names, dates, and values before saving.');
2430
- }
2431
- return Object.assign(Object.assign({}, draft), { warnings: Array.from(new Set(warnings.filter(Boolean))) });
2432
- }
2433
2363
  async createContractFromProposalIntegration(payload) {
2434
- 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;
2364
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
2435
2365
  const sourceEntityId = String(payload.proposalId || '').trim();
2436
2366
  if (!sourceEntityId) {
2437
2367
  throw new common_1.BadRequestException('proposalId is required for CRM proposal integration.');
@@ -2455,30 +2385,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2455
2385
  const billingModel = (_u = proposal.billingModel) !== null && _u !== void 0 ? _u : 'fixed_price';
2456
2386
  const clientName = (_x = (_w = (_v = this.normalizeOptionalText(person.tradeName)) !== null && _v !== void 0 ? _v : this.normalizeOptionalText(person.name)) !== null && _w !== void 0 ? _w : this.normalizeOptionalText(proposal.title)) !== null && _x !== void 0 ? _x : `Proposal ${sourceEntityId}`;
2457
2387
  const contractName = (_y = this.normalizeOptionalText(proposal.title)) !== null && _y !== void 0 ? _y : `Proposal ${sourceEntityId}`;
2458
- const financialTerms = items
2459
- .map((item) => {
2460
- var _a, _b, _c;
2461
- return ({
2462
- termType: this.normalizeEnumValue(item.termType, FINANCIAL_TERM_TYPE_VALUES, 'value'),
2463
- label: (_a = this.normalizeOptionalText(item.name)) !== null && _a !== void 0 ? _a : 'Commercial term',
2464
- amount: Number((_b = item.amount) !== null && _b !== void 0 ? _b : Number(item.totalAmountCents || 0) / 100),
2465
- recurrence: this.normalizeEnumValue(item.recurrence, RECURRENCE_VALUES, 'one_time'),
2466
- dueDay: (_c = item.dueDay) !== null && _c !== void 0 ? _c : null,
2467
- notes: this.normalizeOptionalText(item.description),
2468
- });
2469
- })
2470
- .filter((term) => Number.isFinite(term.amount) && term.amount > 0);
2471
- const fallbackAmount = Number((_z = proposal.totalAmount) !== null && _z !== void 0 ? _z : 0);
2472
- if (financialTerms.length === 0 && Number.isFinite(fallbackAmount) && fallbackAmount > 0) {
2473
- financialTerms.push({
2474
- termType: 'revenue',
2475
- label: contractName,
2476
- amount: fallbackAmount,
2477
- recurrence: 'one_time',
2478
- dueDay: null,
2479
- notes: this.normalizeOptionalText(proposal.notes),
2480
- });
2481
- }
2482
2388
  const primaryPartyRole = ['employee', 'contractor'].includes(contractCategory)
2483
2389
  ? 'employee'
2484
2390
  : ['supplier', 'vendor'].includes(contractCategory)
@@ -2487,7 +2393,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2487
2393
  ? 'partner'
2488
2394
  : 'client';
2489
2395
  return this.prisma.$transaction(async (tx) => {
2490
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
2396
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
2491
2397
  await tx.$queryRawUnsafe(`SELECT pg_advisory_xact_lock(hashtext($1))`, `operations:crm_proposal:${sourceEntityId}`);
2492
2398
  const existingContracts = await tx.$queryRawUnsafe(`SELECT id, code
2493
2399
  FROM operations_contract
@@ -2515,7 +2421,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2515
2421
  billing_model,
2516
2422
  account_manager_collaborator_id,
2517
2423
  related_collaborator_id,
2518
- contract_template_id,
2519
2424
  origin_type,
2520
2425
  origin_id,
2521
2426
  start_date,
@@ -2563,8 +2468,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
2563
2468
  NOW(),
2564
2469
  NOW()
2565
2470
  )
2566
- RETURNING id`, generatedCode, contractName, contractCategory, contractType, clientName, 'not_started', true, billingModel, null, null, null, 'crm_proposal', sourceEntityId, (_b = proposal.validFrom) !== null && _b !== void 0 ? _b : null, (_c = proposal.validUntil) !== null && _c !== void 0 ? _c : null, null, (_d = proposal.validFrom) !== null && _d !== void 0 ? _d : null, fallbackAmount > 0 ? fallbackAmount : null, null, 'draft', 'blank', 1, this.normalizeOptionalText(proposal.notes), this.normalizeOptionalText((_e = payload.revision) === null || _e === void 0 ? void 0 : _e.contentHtml), Number(payload.approvedByUserId) || null);
2567
- const contractId = (_f = created[0]) === null || _f === void 0 ? void 0 : _f.id;
2471
+ RETURNING id`, generatedCode, contractName, contractCategory, contractType, clientName, 'not_started', true, billingModel, null, null, null, 'crm_proposal', sourceEntityId, (_b = proposal.validFrom) !== null && _b !== void 0 ? _b : null, (_c = proposal.validUntil) !== null && _c !== void 0 ? _c : null, null, (_d = proposal.validFrom) !== null && _d !== void 0 ? _d : null, Number((_e = proposal.totalAmount) !== null && _e !== void 0 ? _e : 0) > 0 ? Number((_f = proposal.totalAmount) !== null && _f !== void 0 ? _f : 0) : null, null, 'draft', 'blank', 1, this.normalizeOptionalText(proposal.notes), this.normalizeOptionalText((_g = payload.revision) === null || _g === void 0 ? void 0 : _g.contentHtml), Number(payload.approvedByUserId) || null);
2472
+ const contractId = (_h = created[0]) === null || _h === void 0 ? void 0 : _h.id;
2568
2473
  if (!contractId) {
2569
2474
  throw new common_1.BadRequestException('Could not create contract draft from proposal.');
2570
2475
  }
@@ -2579,27 +2484,10 @@ let OperationsService = OperationsService_1 = class OperationsService {
2579
2484
  isPrimary: true,
2580
2485
  },
2581
2486
  ]);
2582
- await this.replaceContractFinancialTerms(tx, contractId, financialTerms.map((term) => ({
2583
- termType: term.termType,
2584
- label: term.label,
2585
- amount: term.amount,
2586
- recurrence: term.recurrence,
2587
- dueDay: term.dueDay,
2588
- notes: term.notes,
2589
- })));
2590
- await this.replaceContractRevisions(tx, contractId, [
2591
- {
2592
- revisionType: 'revision',
2593
- title: (_h = (_g = payload.revision) === null || _g === void 0 ? void 0 : _g.title) !== null && _h !== void 0 ? _h : contractName,
2594
- effectiveDate: (_j = proposal.validFrom) !== null && _j !== void 0 ? _j : null,
2595
- status: 'draft',
2596
- summary: (_l = this.normalizeOptionalText((_k = payload.revision) === null || _k === void 0 ? void 0 : _k.summary)) !== null && _l !== void 0 ? _l : this.normalizeOptionalText(proposal.notes),
2597
- },
2598
- ]);
2599
- await this.insertContractHistory(tx, contractId, Number(payload.approvedByUserId) || null, 'created', `Draft contract generated from CRM proposal ${(_m = proposal.code) !== null && _m !== void 0 ? _m : sourceEntityId}.`, JSON.stringify({
2487
+ await this.insertContractHistory(tx, contractId, Number(payload.approvedByUserId) || null, 'created', `Draft contract generated from CRM proposal ${(_j = proposal.code) !== null && _j !== void 0 ? _j : sourceEntityId}.`, JSON.stringify({
2600
2488
  correlationId: payload.correlationId || `proposal:${sourceEntityId}`,
2601
- proposalId: (_o = payload.proposalId) !== null && _o !== void 0 ? _o : (Number(sourceEntityId) > 0 ? Number(sourceEntityId) : null),
2602
- proposalRevisionId: (_p = payload.proposalRevisionId) !== null && _p !== void 0 ? _p : null,
2489
+ proposalId: (_k = payload.proposalId) !== null && _k !== void 0 ? _k : (Number(sourceEntityId) > 0 ? Number(sourceEntityId) : null),
2490
+ proposalRevisionId: (_l = payload.proposalRevisionId) !== null && _l !== void 0 ? _l : null,
2603
2491
  sourceModule: 'contact',
2604
2492
  sourceEntity: 'proposal',
2605
2493
  sourceId: sourceEntityId,
@@ -2620,10 +2508,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
2620
2508
  billingModel,
2621
2509
  originType: 'crm_proposal',
2622
2510
  originId: sourceEntityId,
2623
- startDate: (_q = proposal.validFrom) !== null && _q !== void 0 ? _q : null,
2624
- endDate: (_r = proposal.validUntil) !== null && _r !== void 0 ? _r : null,
2511
+ startDate: (_m = proposal.validFrom) !== null && _m !== void 0 ? _m : null,
2512
+ endDate: (_o = proposal.validUntil) !== null && _o !== void 0 ? _o : null,
2625
2513
  description: this.normalizeOptionalText(proposal.notes),
2626
- financialTerms,
2627
2514
  }, payload, String(payload.locale || '').trim() || 'en', Number(payload.approvedByUserId) || undefined),
2628
2515
  metadata: {
2629
2516
  producer: 'operations',
@@ -2644,7 +2531,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2644
2531
  });
2645
2532
  }
2646
2533
  buildContractCreatedEventPayload(contract, payload, locale = 'en', createdByUserId) {
2647
- 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;
2534
+ 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;
2648
2535
  return {
2649
2536
  contractId: contract.id,
2650
2537
  proposalId: (_a = payload.proposalId) !== null && _a !== void 0 ? _a : null,
@@ -2674,19 +2561,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
2674
2561
  startDate: (_r = contract.startDate) !== null && _r !== void 0 ? _r : null,
2675
2562
  endDate: (_s = contract.endDate) !== null && _s !== void 0 ? _s : null,
2676
2563
  description: (_t = contract.description) !== null && _t !== void 0 ? _t : null,
2677
- financialTerms: (_u = contract.financialTerms) !== null && _u !== void 0 ? _u : [],
2678
2564
  },
2679
- proposal: (_v = payload.proposal) !== null && _v !== void 0 ? _v : {
2680
- code: (_w = payload.code) !== null && _w !== void 0 ? _w : null,
2681
- title: (_x = payload.title) !== null && _x !== void 0 ? _x : null,
2682
- totalAmount: (_y = payload.total) !== null && _y !== void 0 ? _y : null,
2683
- notes: (_0 = (_z = payload.commercialTerms) === null || _z === void 0 ? void 0 : _z.notes) !== null && _0 !== void 0 ? _0 : null,
2565
+ proposal: (_u = payload.proposal) !== null && _u !== void 0 ? _u : {
2566
+ code: (_v = payload.code) !== null && _v !== void 0 ? _v : null,
2567
+ title: (_w = payload.title) !== null && _w !== void 0 ? _w : null,
2568
+ totalAmount: (_x = payload.total) !== null && _x !== void 0 ? _x : null,
2569
+ notes: (_z = (_y = payload.commercialTerms) === null || _y === void 0 ? void 0 : _y.notes) !== null && _z !== void 0 ? _z : null,
2684
2570
  },
2685
- person: (_1 = payload.person) !== null && _1 !== void 0 ? _1 : null,
2571
+ person: (_0 = payload.person) !== null && _0 !== void 0 ? _0 : null,
2686
2572
  };
2687
2573
  }
2688
2574
  async buildContractActivatedEventPayload(contract, locale = 'en', activatedByUserId) {
2689
- 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, _8, _9, _10, _11;
2575
+ 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, _8, _9, _10;
2690
2576
  let personId = null;
2691
2577
  let proposalId = null;
2692
2578
  let personRecord = null;
@@ -2747,18 +2633,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2747
2633
  }
2748
2634
  }
2749
2635
  }
2750
- const financialTerms = ((_g = contract.financialTerms) !== null && _g !== void 0 ? _g : []).map((term) => {
2751
- var _a, _b, _c, _d, _e;
2752
- return ({
2753
- label: term.label,
2754
- termType: (_a = term.termType) !== null && _a !== void 0 ? _a : 'value',
2755
- amount: Number((_b = term.amount) !== null && _b !== void 0 ? _b : 0),
2756
- recurrence: (_c = term.recurrence) !== null && _c !== void 0 ? _c : 'one_time',
2757
- dueDay: (_d = term.dueDay) !== null && _d !== void 0 ? _d : null,
2758
- notes: (_e = term.notes) !== null && _e !== void 0 ? _e : null,
2759
- });
2760
- });
2761
- const totalAmount = financialTerms.reduce((sum, term) => sum + (Number.isFinite(term.amount) ? term.amount : 0), 0) || Number((_h = contract.budgetAmount) !== null && _h !== void 0 ? _h : 0);
2636
+ const totalAmount = Number((_g = contract.budgetAmount) !== null && _g !== void 0 ? _g : 0);
2762
2637
  return {
2763
2638
  contractId: contract.id,
2764
2639
  proposalId,
@@ -2766,7 +2641,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2766
2641
  activatedByUserId: activatedByUserId !== null && activatedByUserId !== void 0 ? activatedByUserId : null,
2767
2642
  signedByUserId: activatedByUserId !== null && activatedByUserId !== void 0 ? activatedByUserId : null,
2768
2643
  activatedAt: new Date().toISOString(),
2769
- signedAt: (_l = (_k = (_j = contract.signedAt) !== null && _j !== void 0 ? _j : contract.effectiveDate) !== null && _k !== void 0 ? _k : contract.startDate) !== null && _l !== void 0 ? _l : null,
2644
+ signedAt: (_k = (_j = (_h = contract.signedAt) !== null && _h !== void 0 ? _h : contract.effectiveDate) !== null && _j !== void 0 ? _j : contract.startDate) !== null && _k !== void 0 ? _k : null,
2770
2645
  locale,
2771
2646
  correlationId: proposalId ? `proposal:${proposalId}` : `contract:${contract.id}`,
2772
2647
  sourceModule: 'operations',
@@ -2776,38 +2651,37 @@ let OperationsService = OperationsService_1 = class OperationsService {
2776
2651
  source_entity: 'contract',
2777
2652
  source_id: String(contract.id),
2778
2653
  contract: {
2779
- code: (_m = contract.code) !== null && _m !== void 0 ? _m : null,
2780
- name: (_o = contract.name) !== null && _o !== void 0 ? _o : null,
2781
- clientName: (_p = contract.clientName) !== null && _p !== void 0 ? _p : null,
2782
- contractCategory: (_q = contract.contractCategory) !== null && _q !== void 0 ? _q : 'client',
2783
- contractType: (_r = contract.contractType) !== null && _r !== void 0 ? _r : 'service_agreement',
2784
- billingModel: (_s = contract.billingModel) !== null && _s !== void 0 ? _s : 'fixed_price',
2785
- originType: (_t = contract.originType) !== null && _t !== void 0 ? _t : 'manual',
2786
- originId: (_u = contract.originId) !== null && _u !== void 0 ? _u : null,
2787
- startDate: (_v = contract.startDate) !== null && _v !== void 0 ? _v : null,
2788
- endDate: (_w = contract.endDate) !== null && _w !== void 0 ? _w : null,
2789
- signedAt: (_x = contract.signedAt) !== null && _x !== void 0 ? _x : null,
2790
- effectiveDate: (_y = contract.effectiveDate) !== null && _y !== void 0 ? _y : null,
2791
- budgetAmount: (_z = contract.budgetAmount) !== null && _z !== void 0 ? _z : null,
2792
- description: (_0 = contract.description) !== null && _0 !== void 0 ? _0 : null,
2793
- financialTerms,
2794
- parties: (_1 = contract.parties) !== null && _1 !== void 0 ? _1 : [],
2654
+ code: (_l = contract.code) !== null && _l !== void 0 ? _l : null,
2655
+ name: (_m = contract.name) !== null && _m !== void 0 ? _m : null,
2656
+ clientName: (_o = contract.clientName) !== null && _o !== void 0 ? _o : null,
2657
+ contractCategory: (_p = contract.contractCategory) !== null && _p !== void 0 ? _p : 'client',
2658
+ contractType: (_q = contract.contractType) !== null && _q !== void 0 ? _q : 'service_agreement',
2659
+ billingModel: (_r = contract.billingModel) !== null && _r !== void 0 ? _r : 'fixed_price',
2660
+ originType: (_s = contract.originType) !== null && _s !== void 0 ? _s : 'manual',
2661
+ originId: (_t = contract.originId) !== null && _t !== void 0 ? _t : null,
2662
+ startDate: (_u = contract.startDate) !== null && _u !== void 0 ? _u : null,
2663
+ endDate: (_v = contract.endDate) !== null && _v !== void 0 ? _v : null,
2664
+ signedAt: (_w = contract.signedAt) !== null && _w !== void 0 ? _w : null,
2665
+ effectiveDate: (_x = contract.effectiveDate) !== null && _x !== void 0 ? _x : null,
2666
+ budgetAmount: (_y = contract.budgetAmount) !== null && _y !== void 0 ? _y : null,
2667
+ description: (_z = contract.description) !== null && _z !== void 0 ? _z : null,
2668
+ parties: (_0 = contract.parties) !== null && _0 !== void 0 ? _0 : [],
2795
2669
  },
2796
2670
  person: personRecord
2797
2671
  ? {
2798
- id: (_2 = personRecord.id) !== null && _2 !== void 0 ? _2 : null,
2799
- name: (_3 = personRecord.name) !== null && _3 !== void 0 ? _3 : null,
2800
- tradeName: (_4 = personRecord.trade_name) !== null && _4 !== void 0 ? _4 : null,
2801
- email: (_5 = personRecord.email) !== null && _5 !== void 0 ? _5 : null,
2802
- phone: (_6 = personRecord.phone) !== null && _6 !== void 0 ? _6 : null,
2803
- document: (_7 = personRecord.document) !== null && _7 !== void 0 ? _7 : null,
2672
+ id: (_1 = personRecord.id) !== null && _1 !== void 0 ? _1 : null,
2673
+ name: (_2 = personRecord.name) !== null && _2 !== void 0 ? _2 : null,
2674
+ tradeName: (_3 = personRecord.trade_name) !== null && _3 !== void 0 ? _3 : null,
2675
+ email: (_4 = personRecord.email) !== null && _4 !== void 0 ? _4 : null,
2676
+ phone: (_5 = personRecord.phone) !== null && _5 !== void 0 ? _5 : null,
2677
+ document: (_6 = personRecord.document) !== null && _6 !== void 0 ? _6 : null,
2804
2678
  }
2805
2679
  : null,
2806
2680
  receivable: {
2807
2681
  personId,
2808
- documentNumber: (_8 = contract.code) !== null && _8 !== void 0 ? _8 : `CONTRACT-${contract.id}`,
2682
+ documentNumber: (_7 = contract.code) !== null && _7 !== void 0 ? _7 : `CONTRACT-${contract.id}`,
2809
2683
  totalAmount,
2810
- description: (_11 = (_10 = (_9 = contract.description) !== null && _9 !== void 0 ? _9 : contract.name) !== null && _10 !== void 0 ? _10 : contract.code) !== null && _11 !== void 0 ? _11 : null,
2684
+ description: (_10 = (_9 = (_8 = contract.description) !== null && _8 !== void 0 ? _8 : contract.name) !== null && _9 !== void 0 ? _9 : contract.code) !== null && _10 !== void 0 ? _10 : null,
2811
2685
  },
2812
2686
  };
2813
2687
  }
@@ -2832,7 +2706,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2832
2706
  this.pushUpdate(updates, params, 'billing_model', data.billingModel, 'operations_contract_billing_model_409dc7fea2_enum');
2833
2707
  this.pushUpdate(updates, params, 'account_manager_collaborator_id', data.accountManagerCollaboratorId);
2834
2708
  this.pushUpdate(updates, params, 'related_collaborator_id', data.relatedCollaboratorId);
2835
- this.pushUpdate(updates, params, 'contract_template_id', data.contractTemplateId);
2836
2709
  this.pushUpdate(updates, params, 'origin_type', data.originType, 'operations_contract_origin_type_07a7cc2b5d_enum');
2837
2710
  this.pushUpdate(updates, params, 'origin_id', data.originId);
2838
2711
  this.pushUpdate(updates, params, 'start_date', data.startDate, 'date');
@@ -2847,7 +2720,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2847
2720
  this.pushUpdate(updates, params, 'description', data.description);
2848
2721
  this.pushUpdate(updates, params, 'content_html', data.contentHtml);
2849
2722
  await this.prisma.$transaction(async (tx) => {
2850
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
2723
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
2851
2724
  if (updates.length) {
2852
2725
  params.push(contractId);
2853
2726
  await tx.$executeRawUnsafe(`UPDATE operations_contract
@@ -2858,15 +2731,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
2858
2731
  if (data.parties) {
2859
2732
  await this.replaceContractParties(tx, contractId, data.parties);
2860
2733
  }
2861
- if (data.signatures) {
2862
- await this.replaceContractSignatures(tx, contractId, data.signatures);
2863
- }
2864
- if (data.financialTerms) {
2865
- await this.replaceContractFinancialTerms(tx, contractId, data.financialTerms);
2866
- }
2867
- if (data.revisions) {
2868
- await this.replaceContractRevisions(tx, contractId, data.revisions);
2869
- }
2870
2734
  if (data.replaceUploadedPdfDocument) {
2871
2735
  await this.replaceContractDocument(tx, contractId, 'source_upload', data.replaceUploadedPdfDocument);
2872
2736
  }
@@ -2888,8 +2752,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2888
2752
  effectiveDate: (_m = data.effectiveDate) !== null && _m !== void 0 ? _m : current.effectiveDate,
2889
2753
  budgetAmount: (_o = data.budgetAmount) !== null && _o !== void 0 ? _o : current.budgetAmount,
2890
2754
  description: (_p = data.description) !== null && _p !== void 0 ? _p : current.description,
2891
- financialTerms: (_q = data.financialTerms) !== null && _q !== void 0 ? _q : current.financialTerms,
2892
- parties: (_r = data.parties) !== null && _r !== void 0 ? _r : current.parties,
2755
+ parties: (_q = data.parties) !== null && _q !== void 0 ? _q : current.parties,
2893
2756
  }, 'en', userId);
2894
2757
  if (shouldPublishSigned) {
2895
2758
  await this.integrationApi.publishEvent({
@@ -2897,15 +2760,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
2897
2760
  sourceModule: 'operations',
2898
2761
  aggregateType: 'contract',
2899
2762
  aggregateId: String(contractId),
2900
- payload: Object.assign(Object.assign({}, contractEventPayload), { signedByUserId: userId !== null && userId !== void 0 ? userId : null, signedAt: (_u = (_t = (_s = data.signedAt) !== null && _s !== void 0 ? _s : current.signedAt) !== null && _t !== void 0 ? _t : contractEventPayload.signedAt) !== null && _u !== void 0 ? _u : new Date().toISOString() }),
2763
+ payload: Object.assign(Object.assign({}, contractEventPayload), { signedByUserId: userId !== null && userId !== void 0 ? userId : null, signedAt: (_t = (_s = (_r = data.signedAt) !== null && _r !== void 0 ? _r : current.signedAt) !== null && _s !== void 0 ? _s : contractEventPayload.signedAt) !== null && _t !== void 0 ? _t : new Date().toISOString() }),
2901
2764
  metadata: {
2902
2765
  producer: 'operations',
2903
2766
  correlationId: contractEventPayload.correlationId || `contract:${contractId}`,
2904
2767
  sourceModule: 'operations',
2905
2768
  sourceEntity: 'contract',
2906
2769
  sourceId: String(contractId),
2907
- originType: (_v = data.originType) !== null && _v !== void 0 ? _v : current.originType,
2908
- originId: (_w = data.originId) !== null && _w !== void 0 ? _w : current.originId,
2770
+ originType: (_u = data.originType) !== null && _u !== void 0 ? _u : current.originType,
2771
+ originId: (_v = data.originId) !== null && _v !== void 0 ? _v : current.originId,
2909
2772
  lifecycle: 'signed',
2910
2773
  },
2911
2774
  }, {
@@ -2925,8 +2788,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
2925
2788
  sourceModule: 'operations',
2926
2789
  sourceEntity: 'contract',
2927
2790
  sourceId: String(contractId),
2928
- originType: (_x = data.originType) !== null && _x !== void 0 ? _x : current.originType,
2929
- originId: (_y = data.originId) !== null && _y !== void 0 ? _y : current.originId,
2791
+ originType: (_w = data.originType) !== null && _w !== void 0 ? _w : current.originType,
2792
+ originId: (_x = data.originId) !== null && _x !== void 0 ? _x : current.originId,
2930
2793
  lifecycle: 'activated',
2931
2794
  },
2932
2795
  }, {
@@ -2950,10 +2813,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
2950
2813
  AND deleted_at IS NULL`, contractId);
2951
2814
  for (const tableName of [
2952
2815
  'operations_contract_party',
2953
- 'operations_contract_signature',
2954
- 'operations_contract_financial_term',
2955
2816
  'operations_contract_document',
2956
- 'operations_contract_revision',
2957
2817
  ]) {
2958
2818
  await tx.$executeRawUnsafe(`UPDATE ${tableName}
2959
2819
  SET deleted_at = NOW(),
@@ -2972,9 +2832,43 @@ let OperationsService = OperationsService_1 = class OperationsService {
2972
2832
  });
2973
2833
  return { success: true };
2974
2834
  }
2975
- async listTimesheets(userId) {
2835
+ async listTimesheets(userId, filters = {}) {
2836
+ var _a, _b, _c, _d, _e, _f;
2976
2837
  const actor = await this.getActorContext(userId);
2977
2838
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 't.collaborator_id', actor.isDirector);
2839
+ const pagination = this.shouldPaginate(filters)
2840
+ ? this.normalizePaginationParams(filters, {
2841
+ defaultSortField: 'weekStartDate',
2842
+ defaultSortOrder: 'desc',
2843
+ allowedSortFields: ['weekStartDate', 'weekEndDate', 'collaboratorName', 'status'],
2844
+ })
2845
+ : null;
2846
+ const params = [...filter.params];
2847
+ const where = ['t.deleted_at IS NULL', filter.clause];
2848
+ if (filters.status && filters.status !== 'all') {
2849
+ where.push(`t.status::text = ${this.param(params, filters.status)}`);
2850
+ }
2851
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
2852
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
2853
+ where.push(`(
2854
+ COALESCE(c.display_name, '') ILIKE ${searchPlaceholder}
2855
+ OR COALESCE(a.display_name, '') ILIKE ${searchPlaceholder}
2856
+ OR COALESCE(t.notes, '') ILIKE ${searchPlaceholder}
2857
+ OR COALESCE(approval.decision_note, '') ILIKE ${searchPlaceholder}
2858
+ )`);
2859
+ }
2860
+ const whereClause = where.join(' AND ');
2861
+ const sortColumn = (_b = {
2862
+ weekStartDate: 't.week_start_date',
2863
+ weekEndDate: 't.week_end_date',
2864
+ collaboratorName: 'c.display_name',
2865
+ status: 't.status',
2866
+ }[(_a = pagination === null || pagination === void 0 ? void 0 : pagination.sortField) !== null && _a !== void 0 ? _a : 'weekStartDate']) !== null && _b !== void 0 ? _b : 't.week_start_date';
2867
+ const headerParams = [...params];
2868
+ const limitSql = pagination
2869
+ ? `LIMIT ${this.param(headerParams, pagination.pageSize)}
2870
+ OFFSET ${this.param(headerParams, pagination.offset)}`
2871
+ : '';
2978
2872
  const headers = await this.queryRows(`SELECT t.id,
2979
2873
  t.collaborator_id AS "collaboratorId",
2980
2874
  c.display_name AS "collaboratorName",
@@ -2995,11 +2889,26 @@ let OperationsService = OperationsService_1 = class OperationsService {
2995
2889
  ON approval.target_type = 'timesheet'
2996
2890
  AND approval.target_id = t.id
2997
2891
  AND approval.deleted_at IS NULL
2998
- WHERE t.deleted_at IS NULL AND ${filter.clause}
2999
- ORDER BY t.week_start_date DESC, t.id DESC`, filter.params);
2892
+ WHERE ${whereClause}
2893
+ ORDER BY ${sortColumn} ${(_d = (_c = pagination === null || pagination === void 0 ? void 0 : pagination.sortOrder) === null || _c === void 0 ? void 0 : _c.toUpperCase()) !== null && _d !== void 0 ? _d : 'DESC'}, t.id DESC
2894
+ ${limitSql}`, headerParams);
3000
2895
  if (!headers.length) {
2896
+ if (pagination) {
2897
+ return this.buildPaginationResult([], 0, pagination.page, pagination.pageSize);
2898
+ }
3001
2899
  return headers;
3002
2900
  }
2901
+ const total = pagination
2902
+ ? Number((_f = (_e = (await this.querySingle(`SELECT COUNT(*)::text AS total
2903
+ FROM operations_timesheet t
2904
+ JOIN operations_collaborator c ON c.id = t.collaborator_id
2905
+ LEFT JOIN operations_collaborator a ON a.id = t.approver_collaborator_id
2906
+ LEFT JOIN operations_approval approval
2907
+ ON approval.target_type = 'timesheet'
2908
+ AND approval.target_id = t.id
2909
+ AND approval.deleted_at IS NULL
2910
+ WHERE ${whereClause}`, params))) === null || _e === void 0 ? void 0 : _e.total) !== null && _f !== void 0 ? _f : 0)
2911
+ : 0;
3003
2912
  const entries = await this.queryRows(`SELECT e.id,
3004
2913
  e.timesheet_id AS "timesheetId",
3005
2914
  e.project_assignment_id AS "projectAssignmentId",
@@ -3026,10 +2935,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
3026
2935
  AND e.timesheet_id = ANY($1::int[])
3027
2936
  ORDER BY e.work_date ASC, e.id ASC`, [headers.map((item) => item.id)]);
3028
2937
  const grouped = this.groupBy(entries, 'timesheetId');
3029
- return headers.map((timesheet) => {
2938
+ const data = headers.map((timesheet) => {
3030
2939
  var _a;
3031
2940
  return (Object.assign(Object.assign({}, timesheet), { entries: (_a = grouped[timesheet.id]) !== null && _a !== void 0 ? _a : [] }));
3032
2941
  });
2942
+ if (!pagination) {
2943
+ return data;
2944
+ }
2945
+ return this.buildPaginationResult(data, total, pagination.page, pagination.pageSize);
3033
2946
  }
3034
2947
  async createTimesheet(userId, data) {
3035
2948
  const actor = await this.getActorContext(userId);
@@ -3142,17 +3055,40 @@ let OperationsService = OperationsService_1 = class OperationsService {
3142
3055
  });
3143
3056
  return this.listSingleTimesheet(actor, timesheetId);
3144
3057
  }
3145
- async listTimeOffRequests(userId) {
3058
+ async listTimeOffRequests(userId, filters = {}) {
3059
+ var _a, _b;
3146
3060
  const actor = await this.getActorContext(userId);
3147
3061
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'tor.collaborator_id', actor.isDirector);
3148
- return this.queryRows(`SELECT tor.id,
3062
+ const pagination = this.shouldPaginate(filters)
3063
+ ? this.normalizePaginationParams(filters, {
3064
+ defaultSortField: 'startDate',
3065
+ defaultSortOrder: 'desc',
3066
+ allowedSortFields: ['startDate', 'endDate', 'collaboratorName', 'status'],
3067
+ })
3068
+ : null;
3069
+ const params = [...filter.params];
3070
+ const where = ['tor.deleted_at IS NULL', filter.clause];
3071
+ if (filters.status && filters.status !== 'all') {
3072
+ where.push(`tor.status::text = ${this.param(params, filters.status)}`);
3073
+ }
3074
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
3075
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
3076
+ where.push(`(
3077
+ COALESCE(c.display_name, '') ILIKE ${searchPlaceholder}
3078
+ OR COALESCE(a.display_name, '') ILIKE ${searchPlaceholder}
3079
+ OR COALESCE(tor.reason, '') ILIKE ${searchPlaceholder}
3080
+ OR COALESCE(tor.request_type::text, '') ILIKE ${searchPlaceholder}
3081
+ )`);
3082
+ }
3083
+ const whereClause = where.join(' AND ');
3084
+ const baseQuery = `SELECT tor.id,
3149
3085
  tor.collaborator_id AS "collaboratorId",
3150
3086
  c.display_name AS "collaboratorName",
3151
3087
  tor.approver_collaborator_id AS "approverCollaboratorId",
3152
3088
  a.display_name AS "approverName",
3153
3089
  tor.request_type AS "requestType",
3154
- tor.start_date AS "startDate",
3155
- tor.end_date AS "endDate",
3090
+ tor.start_date::text AS "startDate",
3091
+ tor.end_date::text AS "endDate",
3156
3092
  tor.total_days AS "totalDays",
3157
3093
  tor.status,
3158
3094
  tor.reason,
@@ -3166,8 +3102,30 @@ let OperationsService = OperationsService_1 = class OperationsService {
3166
3102
  ON approval.target_type = 'time_off_request'
3167
3103
  AND approval.target_id = tor.id
3168
3104
  AND approval.deleted_at IS NULL
3169
- WHERE tor.deleted_at IS NULL AND ${filter.clause}
3170
- ORDER BY tor.start_date DESC, tor.id DESC`, filter.params);
3105
+ WHERE ${whereClause}`;
3106
+ if (!pagination) {
3107
+ return this.queryRows(`${baseQuery}
3108
+ ORDER BY tor.start_date DESC, tor.id DESC`, params);
3109
+ }
3110
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
3111
+ FROM operations_time_off_request tor
3112
+ JOIN operations_collaborator c ON c.id = tor.collaborator_id
3113
+ LEFT JOIN operations_collaborator a ON a.id = tor.approver_collaborator_id
3114
+ WHERE ${whereClause}`, params);
3115
+ const sortColumn = (_a = {
3116
+ startDate: 'tor.start_date',
3117
+ endDate: 'tor.end_date',
3118
+ collaboratorName: 'c.display_name',
3119
+ status: 'tor.status',
3120
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : 'tor.start_date';
3121
+ const queryParams = [...params];
3122
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
3123
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
3124
+ const rows = await this.queryRows(`${baseQuery}
3125
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, tor.id DESC
3126
+ LIMIT ${limitPlaceholder}
3127
+ OFFSET ${offsetPlaceholder}`, queryParams);
3128
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
3171
3129
  }
3172
3130
  async createTimeOffRequest(userId, data) {
3173
3131
  const actor = await this.getActorContext(userId);
@@ -3222,8 +3180,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
3222
3180
  collaborator_id AS "collaboratorId",
3223
3181
  approver_collaborator_id AS "approverCollaboratorId",
3224
3182
  request_type AS "requestType",
3225
- start_date AS "startDate",
3226
- end_date AS "endDate",
3183
+ start_date::text AS "startDate",
3184
+ end_date::text AS "endDate",
3227
3185
  total_days AS "totalDays",
3228
3186
  status,
3229
3187
  reason,
@@ -3232,17 +3190,56 @@ let OperationsService = OperationsService_1 = class OperationsService {
3232
3190
  FROM operations_time_off_request
3233
3191
  WHERE id = $1`, [created]);
3234
3192
  }
3235
- async listScheduleAdjustments(userId) {
3193
+ async listScheduleAdjustments(userId, filters = {}) {
3194
+ var _a, _b, _c, _d, _e, _f;
3236
3195
  const actor = await this.getActorContext(userId);
3237
3196
  const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'sar.collaborator_id', actor.isDirector);
3197
+ const pagination = this.shouldPaginate(filters)
3198
+ ? this.normalizePaginationParams(filters, {
3199
+ defaultSortField: 'effectiveStartDate',
3200
+ defaultSortOrder: 'desc',
3201
+ allowedSortFields: [
3202
+ 'effectiveStartDate',
3203
+ 'effectiveEndDate',
3204
+ 'collaboratorName',
3205
+ 'status',
3206
+ ],
3207
+ })
3208
+ : null;
3209
+ const params = [...filter.params];
3210
+ const where = ['sar.deleted_at IS NULL', filter.clause];
3211
+ if (filters.status && filters.status !== 'all') {
3212
+ where.push(`sar.status::text = ${this.param(params, filters.status)}`);
3213
+ }
3214
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
3215
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
3216
+ where.push(`(
3217
+ COALESCE(c.display_name, '') ILIKE ${searchPlaceholder}
3218
+ OR COALESCE(a.display_name, '') ILIKE ${searchPlaceholder}
3219
+ OR COALESCE(sar.reason, '') ILIKE ${searchPlaceholder}
3220
+ OR COALESCE(sar.request_scope::text, '') ILIKE ${searchPlaceholder}
3221
+ )`);
3222
+ }
3223
+ const whereClause = where.join(' AND ');
3224
+ const sortColumn = (_b = {
3225
+ effectiveStartDate: 'sar.effective_start_date',
3226
+ effectiveEndDate: 'sar.effective_end_date',
3227
+ collaboratorName: 'c.display_name',
3228
+ status: 'sar.status',
3229
+ }[(_a = pagination === null || pagination === void 0 ? void 0 : pagination.sortField) !== null && _a !== void 0 ? _a : 'effectiveStartDate']) !== null && _b !== void 0 ? _b : 'sar.effective_start_date';
3230
+ const queryParams = [...params];
3231
+ const limitSql = pagination
3232
+ ? `LIMIT ${this.param(queryParams, pagination.pageSize)}
3233
+ OFFSET ${this.param(queryParams, pagination.offset)}`
3234
+ : '';
3238
3235
  const requests = await this.queryRows(`SELECT sar.id,
3239
3236
  sar.collaborator_id AS "collaboratorId",
3240
3237
  c.display_name AS "collaboratorName",
3241
3238
  sar.approver_collaborator_id AS "approverCollaboratorId",
3242
3239
  a.display_name AS "approverName",
3243
3240
  sar.request_scope AS "requestScope",
3244
- sar.effective_start_date AS "effectiveStartDate",
3245
- sar.effective_end_date AS "effectiveEndDate",
3241
+ sar.effective_start_date::text AS "effectiveStartDate",
3242
+ sar.effective_end_date::text AS "effectiveEndDate",
3246
3243
  sar.status,
3247
3244
  sar.reason,
3248
3245
  sar.submitted_at AS "submittedAt",
@@ -3255,11 +3252,22 @@ let OperationsService = OperationsService_1 = class OperationsService {
3255
3252
  ON approval.target_type = 'schedule_adjustment_request'
3256
3253
  AND approval.target_id = sar.id
3257
3254
  AND approval.deleted_at IS NULL
3258
- WHERE sar.deleted_at IS NULL AND ${filter.clause}
3259
- ORDER BY sar.effective_start_date DESC, sar.id DESC`, filter.params);
3255
+ WHERE ${whereClause}
3256
+ ORDER BY ${sortColumn} ${(_d = (_c = pagination === null || pagination === void 0 ? void 0 : pagination.sortOrder) === null || _c === void 0 ? void 0 : _c.toUpperCase()) !== null && _d !== void 0 ? _d : 'DESC'}, sar.id DESC
3257
+ ${limitSql}`, queryParams);
3260
3258
  if (!requests.length) {
3259
+ if (pagination) {
3260
+ return this.buildPaginationResult([], 0, pagination.page, pagination.pageSize);
3261
+ }
3261
3262
  return requests;
3262
3263
  }
3264
+ const total = pagination
3265
+ ? Number((_f = (_e = (await this.querySingle(`SELECT COUNT(*)::text AS total
3266
+ FROM operations_schedule_adjustment_request sar
3267
+ JOIN operations_collaborator c ON c.id = sar.collaborator_id
3268
+ LEFT JOIN operations_collaborator a ON a.id = sar.approver_collaborator_id
3269
+ WHERE ${whereClause}`, params))) === null || _e === void 0 ? void 0 : _e.total) !== null && _f !== void 0 ? _f : 0)
3270
+ : 0;
3263
3271
  const days = await this.queryRows(`SELECT schedule_adjustment_request_id AS "requestId",
3264
3272
  weekday,
3265
3273
  is_working_day AS "isWorkingDay",
@@ -3281,10 +3289,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
3281
3289
  ORDER BY collaborator_id, weekday, id DESC`, [this.uniqueNumbers(requests.map((item) => item.collaboratorId))]);
3282
3290
  const grouped = this.groupBy(days, 'requestId');
3283
3291
  const currentScheduleByCollaborator = this.groupBy(currentSchedule, 'collaboratorId');
3284
- return requests.map((request) => {
3292
+ const data = requests.map((request) => {
3285
3293
  var _a, _b;
3286
3294
  return (Object.assign(Object.assign({}, request), { days: (_a = grouped[request.id]) !== null && _a !== void 0 ? _a : [], currentSchedule: (_b = currentScheduleByCollaborator[request.collaboratorId]) !== null && _b !== void 0 ? _b : [] }));
3287
3295
  });
3296
+ if (!pagination) {
3297
+ return data;
3298
+ }
3299
+ return this.buildPaginationResult(data, total, pagination.page, pagination.pageSize);
3288
3300
  }
3289
3301
  async createScheduleAdjustmentRequest(userId, data) {
3290
3302
  const actor = await this.getActorContext(userId);
@@ -3356,8 +3368,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
3356
3368
  collaborator_id AS "collaboratorId",
3357
3369
  approver_collaborator_id AS "approverCollaboratorId",
3358
3370
  request_scope AS "requestScope",
3359
- effective_start_date AS "effectiveStartDate",
3360
- effective_end_date AS "effectiveEndDate",
3371
+ effective_start_date::text AS "effectiveStartDate",
3372
+ effective_end_date::text AS "effectiveEndDate",
3361
3373
  status,
3362
3374
  reason,
3363
3375
  submitted_at AS "submittedAt",
@@ -3365,14 +3377,40 @@ let OperationsService = OperationsService_1 = class OperationsService {
3365
3377
  FROM operations_schedule_adjustment_request
3366
3378
  WHERE id = $1`, [created]);
3367
3379
  }
3368
- async listApprovals(userId) {
3380
+ async listApprovals(userId, filters = {}) {
3381
+ var _a, _b;
3369
3382
  const actor = await this.getActorContext(userId);
3370
3383
  this.ensureSupervisor(actor);
3371
3384
  const params = [];
3372
3385
  const clause = actor.isDirector
3373
3386
  ? 'a.deleted_at IS NULL'
3374
3387
  : `a.deleted_at IS NULL AND a.approver_collaborator_id = ${this.param(params, actor.collaboratorId)}`;
3375
- return this.queryRows(`SELECT a.id,
3388
+ const pagination = this.shouldPaginate(filters)
3389
+ ? this.normalizePaginationParams(filters, {
3390
+ defaultSortField: 'submittedAt',
3391
+ defaultSortOrder: 'desc',
3392
+ allowedSortFields: ['submittedAt', 'decidedAt', 'requesterName', 'status'],
3393
+ })
3394
+ : null;
3395
+ const where = [clause];
3396
+ if (filters.status && filters.status !== 'all') {
3397
+ where.push(`a.status::text = ${this.param(params, filters.status)}`);
3398
+ }
3399
+ if (filters.targetType && filters.targetType !== 'all') {
3400
+ where.push(`a.target_type = ${this.param(params, filters.targetType)}::text::operations_approval_target_type_32d3f04385_enum`);
3401
+ }
3402
+ if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
3403
+ const searchPlaceholder = this.param(params, `%${pagination.search}%`);
3404
+ where.push(`(
3405
+ COALESCE(requester.display_name, '') ILIKE ${searchPlaceholder}
3406
+ OR COALESCE(approver.display_name, '') ILIKE ${searchPlaceholder}
3407
+ OR COALESCE(a.decision_note, '') ILIKE ${searchPlaceholder}
3408
+ OR COALESCE(tor.reason, '') ILIKE ${searchPlaceholder}
3409
+ OR COALESCE(sar.reason, '') ILIKE ${searchPlaceholder}
3410
+ )`);
3411
+ }
3412
+ const whereClause = where.join(' AND ');
3413
+ const baseQuery = `SELECT a.id,
3376
3414
  a.target_type AS "targetType",
3377
3415
  a.target_id AS "targetId",
3378
3416
  a.requester_collaborator_id AS "requesterCollaboratorId",
@@ -3391,12 +3429,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
3391
3429
  ''
3392
3430
  ) AS "timesheetProjectNames",
3393
3431
  tor.request_type AS "timeOffType",
3394
- tor.start_date AS "timeOffStartDate",
3395
- tor.end_date AS "timeOffEndDate",
3432
+ tor.start_date::text AS "timeOffStartDate",
3433
+ tor.end_date::text AS "timeOffEndDate",
3396
3434
  tor.reason AS "timeOffReason",
3397
3435
  sar.request_scope AS "scheduleRequestScope",
3398
- sar.effective_start_date AS "scheduleStartDate",
3399
- sar.effective_end_date AS "scheduleEndDate",
3436
+ sar.effective_start_date::text AS "scheduleStartDate",
3437
+ sar.effective_end_date::text AS "scheduleEndDate",
3400
3438
  sar.reason AS "scheduleReason"
3401
3439
  FROM operations_approval a
3402
3440
  JOIN operations_collaborator requester
@@ -3419,9 +3457,42 @@ let OperationsService = OperationsService_1 = class OperationsService {
3419
3457
  LEFT JOIN operations_schedule_adjustment_request sar
3420
3458
  ON a.target_type = 'schedule_adjustment_request'
3421
3459
  AND sar.id = a.target_id
3422
- WHERE ${clause}
3423
- GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id
3424
- ORDER BY a.submitted_at DESC, a.id DESC`, params);
3460
+ WHERE ${whereClause}
3461
+ GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id`;
3462
+ if (!pagination) {
3463
+ return this.queryRows(`${baseQuery}
3464
+ ORDER BY a.submitted_at DESC, a.id DESC`, params);
3465
+ }
3466
+ const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
3467
+ FROM operations_approval a
3468
+ JOIN operations_collaborator requester
3469
+ ON requester.id = a.requester_collaborator_id
3470
+ LEFT JOIN operations_collaborator approver
3471
+ ON approver.id = a.approver_collaborator_id
3472
+ LEFT JOIN operations_timesheet t
3473
+ ON a.target_type = 'timesheet'
3474
+ AND t.id = a.target_id
3475
+ LEFT JOIN operations_time_off_request tor
3476
+ ON a.target_type = 'time_off_request'
3477
+ AND tor.id = a.target_id
3478
+ LEFT JOIN operations_schedule_adjustment_request sar
3479
+ ON a.target_type = 'schedule_adjustment_request'
3480
+ AND sar.id = a.target_id
3481
+ WHERE ${whereClause}`, params);
3482
+ const sortColumn = (_a = {
3483
+ submittedAt: 'a.submitted_at',
3484
+ decidedAt: 'a.decided_at',
3485
+ requesterName: 'requester.display_name',
3486
+ status: 'a.status',
3487
+ }[pagination.sortField]) !== null && _a !== void 0 ? _a : 'a.submitted_at';
3488
+ const queryParams = [...params];
3489
+ const limitPlaceholder = this.param(queryParams, pagination.pageSize);
3490
+ const offsetPlaceholder = this.param(queryParams, pagination.offset);
3491
+ const rows = await this.queryRows(`${baseQuery}
3492
+ ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, a.id DESC
3493
+ LIMIT ${limitPlaceholder}
3494
+ OFFSET ${offsetPlaceholder}`, queryParams);
3495
+ return this.buildPaginationResult(rows, Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
3425
3496
  }
3426
3497
  async approve(userId, approvalId, data) {
3427
3498
  return this.decideApproval(userId, approvalId, 'approve', data);
@@ -3429,6 +3500,120 @@ let OperationsService = OperationsService_1 = class OperationsService {
3429
3500
  async reject(userId, approvalId, data) {
3430
3501
  return this.decideApproval(userId, approvalId, 'reject', data);
3431
3502
  }
3503
+ async getApprovalDetail(userId, approvalId) {
3504
+ const actor = await this.getActorContext(userId);
3505
+ this.ensureSupervisor(actor);
3506
+ const params = [];
3507
+ const actorClause = actor.isDirector
3508
+ ? 'a.deleted_at IS NULL'
3509
+ : `a.deleted_at IS NULL AND a.approver_collaborator_id = ${this.param(params, actor.collaboratorId)}`;
3510
+ const idPlaceholder = this.param(params, approvalId);
3511
+ const row = await this.querySingle(`SELECT a.id,
3512
+ a.target_type AS "targetType",
3513
+ a.target_id AS "targetId",
3514
+ a.requester_collaborator_id AS "requesterCollaboratorId",
3515
+ requester.display_name AS "requesterName",
3516
+ a.approver_collaborator_id AS "approverCollaboratorId",
3517
+ approver.display_name AS "approverName",
3518
+ a.status,
3519
+ a.submitted_at AS "submittedAt",
3520
+ a.decided_at AS "decidedAt",
3521
+ a.decision_note AS "decisionNote",
3522
+ t.week_start_date AS "timesheetWeekStartDate",
3523
+ t.week_end_date AS "timesheetWeekEndDate",
3524
+ t.total_hours AS "timesheetTotalHours",
3525
+ COALESCE(
3526
+ STRING_AGG(DISTINCT p.name, ', ') FILTER (WHERE p.name IS NOT NULL),
3527
+ ''
3528
+ ) AS "timesheetProjectNames",
3529
+ tor.request_type AS "timeOffType",
3530
+ tor.start_date::text AS "timeOffStartDate",
3531
+ tor.end_date::text AS "timeOffEndDate",
3532
+ tor.reason AS "timeOffReason",
3533
+ sar.request_scope AS "scheduleRequestScope",
3534
+ sar.effective_start_date::text AS "scheduleStartDate",
3535
+ sar.effective_end_date::text AS "scheduleEndDate",
3536
+ sar.reason AS "scheduleReason"
3537
+ FROM operations_approval a
3538
+ JOIN operations_collaborator requester
3539
+ ON requester.id = a.requester_collaborator_id
3540
+ LEFT JOIN operations_collaborator approver
3541
+ ON approver.id = a.approver_collaborator_id
3542
+ LEFT JOIN operations_timesheet t
3543
+ ON a.target_type = 'timesheet'
3544
+ AND t.id = a.target_id
3545
+ LEFT JOIN operations_timesheet_entry te
3546
+ ON te.timesheet_id = t.id
3547
+ AND te.deleted_at IS NULL
3548
+ LEFT JOIN operations_project_assignment pa
3549
+ ON pa.id = te.project_assignment_id
3550
+ LEFT JOIN operations_project p
3551
+ ON p.id = pa.project_id
3552
+ LEFT JOIN operations_time_off_request tor
3553
+ ON a.target_type = 'time_off_request'
3554
+ AND tor.id = a.target_id
3555
+ LEFT JOIN operations_schedule_adjustment_request sar
3556
+ ON a.target_type = 'schedule_adjustment_request'
3557
+ AND sar.id = a.target_id
3558
+ WHERE ${actorClause}
3559
+ AND a.id = ${idPlaceholder}
3560
+ GROUP BY a.id, requester.id, approver.id, t.id, tor.id, sar.id`, params);
3561
+ if (!row) {
3562
+ throw new Error('Approval not found or access denied');
3563
+ }
3564
+ if (row.targetType === 'timesheet') {
3565
+ const entries = await this.queryRows(`SELECT e.id,
3566
+ e.timesheet_id AS "timesheetId",
3567
+ e.project_assignment_id AS "projectAssignmentId",
3568
+ pa.project_id AS "projectId",
3569
+ p.name AS "projectName",
3570
+ pa.role_label AS "roleLabel",
3571
+ e.task_id AS "taskId",
3572
+ task_record.name AS "taskName",
3573
+ COALESCE(task_record.name, e.activity_label) AS "activityLabel",
3574
+ e.work_date AS "workDate",
3575
+ COALESCE(NULLIF(e.duration_minutes, 0), ROUND(COALESCE(e.hours, 0)::numeric * 60))::int AS "durationMinutes",
3576
+ COALESCE(
3577
+ e.hours,
3578
+ ROUND((COALESCE(NULLIF(e.duration_minutes, 0), 0)::numeric / 60), 2)
3579
+ ) AS hours,
3580
+ e.description
3581
+ FROM operations_timesheet_entry e
3582
+ LEFT JOIN operations_project_assignment pa ON pa.id = e.project_assignment_id
3583
+ LEFT JOIN operations_project p ON p.id = pa.project_id
3584
+ LEFT JOIN operations_task task_record
3585
+ ON task_record.id = e.task_id
3586
+ AND task_record.deleted_at IS NULL
3587
+ WHERE e.deleted_at IS NULL
3588
+ AND e.timesheet_id = $1
3589
+ ORDER BY e.work_date ASC, e.id ASC`, [row.targetId]);
3590
+ return Object.assign(Object.assign({}, row), { entries });
3591
+ }
3592
+ if (row.targetType === 'schedule_adjustment_request') {
3593
+ const days = await this.queryRows(`SELECT schedule_adjustment_request_id AS "requestId",
3594
+ weekday,
3595
+ is_working_day AS "isWorkingDay",
3596
+ start_time AS "startTime",
3597
+ end_time AS "endTime",
3598
+ break_minutes AS "breakMinutes"
3599
+ FROM operations_schedule_adjustment_day
3600
+ WHERE schedule_adjustment_request_id = $1
3601
+ ORDER BY id ASC`, [row.targetId]);
3602
+ const collaboratorIdParam = [row.requesterCollaboratorId];
3603
+ const currentSchedule = await this.queryRows(`SELECT DISTINCT ON (collaborator_id, weekday)
3604
+ collaborator_id AS "collaboratorId",
3605
+ weekday,
3606
+ is_working_day AS "isWorkingDay",
3607
+ start_time AS "startTime",
3608
+ end_time AS "endTime",
3609
+ break_minutes AS "breakMinutes"
3610
+ FROM operations_collaborator_schedule_day
3611
+ WHERE collaborator_id = $1
3612
+ ORDER BY collaborator_id, weekday, id DESC`, collaboratorIdParam);
3613
+ return Object.assign(Object.assign({}, row), { days, currentSchedule });
3614
+ }
3615
+ return row;
3616
+ }
3432
3617
  async publishAccountsPayableReference(userId, data) {
3433
3618
  var _a, _b, _c, _d;
3434
3619
  const actor = await this.getActorContext(userId);
@@ -4021,73 +4206,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
4021
4206
  LIMIT 1`, normalizedLookup));
4022
4207
  return (_b = collaboratorTypes[0]) !== null && _b !== void 0 ? _b : null;
4023
4208
  }
4024
- async getContractTemplateRecord(client, templateId, includeInactive = false) {
4025
- const template = (await client.$queryRawUnsafe(`SELECT t.id,
4026
- t.slug,
4027
- t.code,
4028
- t.name,
4029
- t.description,
4030
- t.contract_category AS "contractCategory",
4031
- t.contract_type AS "contractType",
4032
- t.billing_model AS "billingModel",
4033
- t.signature_status AS "signatureStatus",
4034
- t.is_active AS "isActive",
4035
- t.status,
4036
- t.content_html AS "contentHtml",
4037
- COUNT(DISTINCT c.id)::int AS "usageCount",
4038
- t.created_at AS "createdAt",
4039
- t.updated_at AS "updatedAt"
4040
- FROM operations_contract_template t
4041
- LEFT JOIN operations_contract c
4042
- ON c.contract_template_id = t.id
4043
- AND c.deleted_at IS NULL
4044
- WHERE t.id = $1
4045
- AND ($2::boolean = true OR t.deleted_at IS NULL)
4046
- GROUP BY t.id
4047
- LIMIT 1`, templateId, includeInactive));
4048
- const record = template[0];
4049
- if (!record) {
4050
- throw new common_1.NotFoundException('Contract template not found.');
4051
- }
4052
- return record;
4053
- }
4054
- async assertContractTemplateNameAvailable(client, name, excludeTemplateId) {
4055
- const existing = (await client.$queryRawUnsafe(`SELECT id
4056
- FROM operations_contract_template
4057
- WHERE LOWER(name) = LOWER($1)
4058
- AND deleted_at IS NULL
4059
- AND ($2::int IS NULL OR id <> $2)
4060
- LIMIT 1`, name, excludeTemplateId !== null && excludeTemplateId !== void 0 ? excludeTemplateId : null));
4061
- if (existing[0]) {
4062
- throw new common_1.BadRequestException('A contract template with this name already exists.');
4063
- }
4064
- }
4065
- async assertContractTemplateCodeAvailable(client, code, excludeTemplateId) {
4066
- const existing = (await client.$queryRawUnsafe(`SELECT id
4067
- FROM operations_contract_template
4068
- WHERE UPPER(COALESCE(code, '')) = UPPER($1)
4069
- AND deleted_at IS NULL
4070
- AND ($2::int IS NULL OR id <> $2)
4071
- LIMIT 1`, code, excludeTemplateId !== null && excludeTemplateId !== void 0 ? excludeTemplateId : null));
4072
- if (existing[0]) {
4073
- throw new common_1.BadRequestException('A contract template with this code already exists.');
4074
- }
4075
- }
4076
- async generateUniqueContractTemplateSlug(client, label, excludeTemplateId) {
4077
- const baseSlug = this.slugifyValue(label) || `contract-template-${Date.now().toString(36)}`;
4078
- for (let attempt = 0; attempt < 25; attempt += 1) {
4079
- const candidate = attempt === 0 ? baseSlug : `${baseSlug}-${attempt + 1}`;
4080
- const existing = (await client.$queryRawUnsafe(`SELECT id
4081
- FROM operations_contract_template
4082
- WHERE slug = $1
4083
- AND ($2::int IS NULL OR id <> $2)
4084
- LIMIT 1`, candidate, excludeTemplateId !== null && excludeTemplateId !== void 0 ? excludeTemplateId : null));
4085
- if (!existing.length) {
4086
- return candidate;
4087
- }
4088
- }
4089
- return `${baseSlug}-${Date.now().toString(36)}`;
4090
- }
4091
4209
  slugifyValue(value) {
4092
4210
  return (value !== null && value !== void 0 ? value : '')
4093
4211
  .normalize('NFKD')
@@ -4213,7 +4331,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
4213
4331
  WHERE pa.project_id = $1
4214
4332
  AND pa.deleted_at IS NULL`, [projectId]),
4215
4333
  this.querySingle(`SELECT COUNT(*) FILTER (WHERE status IN ('planned', 'active'))::text AS "activeAssignments",
4216
- COUNT(*) FILTER (WHERE is_billable = true AND status IN ('planned', 'active'))::text AS "billableAssignments",
4334
+ COUNT(*) FILTER (WHERE status = 'completed')::text AS "completedAssignments",
4217
4335
  COALESCE(AVG(allocation_percent), 0)::text AS "averageAllocation",
4218
4336
  COALESCE(SUM(weekly_hours), 0)::text AS "totalWeeklyHours"
4219
4337
  FROM operations_project_assignment
@@ -4227,11 +4345,70 @@ let OperationsService = OperationsService_1 = class OperationsService {
4227
4345
  totalHours: Number((_c = timesheetSummary === null || timesheetSummary === void 0 ? void 0 : timesheetSummary.totalHours) !== null && _c !== void 0 ? _c : 0),
4228
4346
  }, operationalIndicators: {
4229
4347
  activeAssignments: Number((_d = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.activeAssignments) !== null && _d !== void 0 ? _d : 0),
4230
- billableAssignments: Number((_e = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.billableAssignments) !== null && _e !== void 0 ? _e : 0),
4348
+ completedAssignments: Number((_e = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.completedAssignments) !== null && _e !== void 0 ? _e : 0),
4231
4349
  averageAllocation: Number((_f = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.averageAllocation) !== null && _f !== void 0 ? _f : 0),
4232
4350
  totalWeeklyHours: Number((_g = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.totalWeeklyHours) !== null && _g !== void 0 ? _g : 0),
4233
4351
  } });
4234
4352
  }
4353
+ async getProjectStats(userId, projectId) {
4354
+ var _a, _b, _c;
4355
+ const [weeklyVelocityRows, allocationRows, quickRadarRow] = await Promise.all([
4356
+ this.prisma.$queryRawUnsafe(`SELECT TO_CHAR(DATE_TRUNC('week', e.work_date), 'DD/MM') AS "weekStart",
4357
+ COALESCE(SUM(e.hours), 0)::text AS "loggedHours"
4358
+ FROM operations_project_assignment pa
4359
+ JOIN operations_timesheet_entry e ON e.project_assignment_id = pa.id AND e.deleted_at IS NULL
4360
+ WHERE pa.project_id = $1
4361
+ AND pa.deleted_at IS NULL
4362
+ AND e.work_date >= NOW() - INTERVAL '7 weeks'
4363
+ GROUP BY DATE_TRUNC('week', e.work_date)
4364
+ ORDER BY DATE_TRUNC('week', e.work_date) ASC
4365
+ LIMIT 7`, projectId),
4366
+ this.prisma.$queryRawUnsafe(`SELECT c.display_name AS "name",
4367
+ COALESCE(pa.allocation_percent, 0)::text AS "allocation"
4368
+ FROM operations_project_assignment pa
4369
+ JOIN operations_collaborator c ON c.id = pa.collaborator_id AND c.deleted_at IS NULL
4370
+ WHERE pa.project_id = $1
4371
+ AND pa.deleted_at IS NULL
4372
+ AND pa.status IN ('planned', 'active')
4373
+ ORDER BY c.display_name ASC
4374
+ LIMIT 6`, projectId),
4375
+ this.prisma.$queryRawUnsafe(`SELECT
4376
+ (SELECT COUNT(*)::text
4377
+ FROM operations_task tk
4378
+ WHERE tk.project_id = $1
4379
+ AND tk.deleted_at IS NULL
4380
+ AND tk.assignee_collaborator_id IS NOT NULL
4381
+ AND tk.status IN ('todo', 'doing', 'review')
4382
+ ) AS "activeAssignments",
4383
+ COUNT(DISTINCT ts.id) FILTER (WHERE ts.status = 'submitted')::text AS "pendingTimesheets",
4384
+ COALESCE(SUM(pa.weekly_hours) FILTER (WHERE pa.status IN ('planned', 'active')), 0)::text AS "totalWeeklyHours"
4385
+ FROM operations_project_assignment pa
4386
+ LEFT JOIN operations_timesheet_entry e ON e.project_assignment_id = pa.id AND e.deleted_at IS NULL
4387
+ LEFT JOIN operations_timesheet ts ON ts.id = e.timesheet_id AND ts.deleted_at IS NULL
4388
+ WHERE pa.project_id = $1 AND pa.deleted_at IS NULL`, projectId).then(rows => Array.isArray(rows) ? rows[0] : rows),
4389
+ ]);
4390
+ const getInitials = (name) => name
4391
+ .split(' ')
4392
+ .map((part) => part[0])
4393
+ .slice(0, 2)
4394
+ .join('')
4395
+ .toUpperCase();
4396
+ return {
4397
+ weeklyVelocity: weeklyVelocityRows.map((row) => ({
4398
+ weekLabel: row.weekStart,
4399
+ loggedHours: Number(row.loggedHours),
4400
+ })),
4401
+ allocationByCollaborator: allocationRows.map((row) => ({
4402
+ name: getInitials(row.name),
4403
+ allocation: Math.round(Number(row.allocation)),
4404
+ })),
4405
+ quickRadar: {
4406
+ activeAssignments: Number((_a = quickRadarRow === null || quickRadarRow === void 0 ? void 0 : quickRadarRow.activeAssignments) !== null && _a !== void 0 ? _a : 0),
4407
+ pendingTimesheets: Number((_b = quickRadarRow === null || quickRadarRow === void 0 ? void 0 : quickRadarRow.pendingTimesheets) !== null && _b !== void 0 ? _b : 0),
4408
+ totalWeeklyHours: Number((_c = quickRadarRow === null || quickRadarRow === void 0 ? void 0 : quickRadarRow.totalWeeklyHours) !== null && _c !== void 0 ? _c : 0),
4409
+ },
4410
+ };
4411
+ }
4235
4412
  async getCollaboratorDetails(collaboratorId) {
4236
4413
  var _a, _b, _c, _d, _e, _f;
4237
4414
  const collaborator = await this.querySingle(`SELECT c.id,
@@ -4373,8 +4550,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
4373
4550
  AND deleted_at IS NULL`, [collaboratorId]),
4374
4551
  this.queryRows(`SELECT id,
4375
4552
  request_scope AS "requestScope",
4376
- effective_start_date AS "effectiveStartDate",
4377
- effective_end_date AS "effectiveEndDate",
4553
+ effective_start_date::text AS "effectiveStartDate",
4554
+ effective_end_date::text AS "effectiveEndDate",
4378
4555
  status,
4379
4556
  reason
4380
4557
  FROM operations_schedule_adjustment_request
@@ -4977,7 +5154,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
4977
5154
  }
4978
5155
  }
4979
5156
  async replaceProjectAssignments(client, projectId, teamAssignments) {
4980
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
5157
+ var _a, _b, _c, _d, _e, _f, _g, _h;
4981
5158
  await client.$executeRawUnsafe(`UPDATE operations_project_assignment
4982
5159
  SET deleted_at = NOW(),
4983
5160
  updated_at = NOW()
@@ -4996,17 +5173,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
4996
5173
  role_label,
4997
5174
  allocation_percent,
4998
5175
  weekly_hours,
4999
- is_billable,
5000
5176
  start_date,
5001
5177
  end_date,
5002
5178
  status,
5003
5179
  created_at,
5004
5180
  updated_at
5005
5181
  ) VALUES (
5006
- $1, $2, $3, $4, $5, $6, $7, $8::date, $9::date,
5007
- $10::operations_project_assignment_status_155b459bbf_enum,
5182
+ $1, $2, $3, $4, $5, $6, $7::date, $8::date,
5183
+ $9::operations_project_assignment_status_155b459bbf_enum,
5008
5184
  NOW(), NOW()
5009
- )`, projectId, assignment.collaboratorId, (_a = projectRole === null || projectRole === void 0 ? void 0 : projectRole.id) !== null && _a !== void 0 ? _a : null, (_c = (_b = this.normalizeOptionalText(assignment.roleLabel)) !== null && _b !== void 0 ? _b : projectRole === null || projectRole === void 0 ? void 0 : projectRole.name) !== null && _c !== void 0 ? _c : 'Team Member', (_d = assignment.allocationPercent) !== null && _d !== void 0 ? _d : null, (_e = assignment.weeklyHours) !== null && _e !== void 0 ? _e : null, (_f = assignment.isBillable) !== null && _f !== void 0 ? _f : true, (_g = assignment.startDate) !== null && _g !== void 0 ? _g : null, (_h = assignment.endDate) !== null && _h !== void 0 ? _h : null, (_j = assignment.status) !== null && _j !== void 0 ? _j : 'active');
5185
+ )`, projectId, assignment.collaboratorId, (_a = projectRole === null || projectRole === void 0 ? void 0 : projectRole.id) !== null && _a !== void 0 ? _a : null, (_c = (_b = this.normalizeOptionalText(assignment.roleLabel)) !== null && _b !== void 0 ? _b : projectRole === null || projectRole === void 0 ? void 0 : projectRole.name) !== null && _c !== void 0 ? _c : 'Team Member', (_d = assignment.allocationPercent) !== null && _d !== void 0 ? _d : null, (_e = assignment.weeklyHours) !== null && _e !== void 0 ? _e : null, (_f = assignment.startDate) !== null && _f !== void 0 ? _f : null, (_g = assignment.endDate) !== null && _g !== void 0 ? _g : null, (_h = assignment.status) !== null && _h !== void 0 ? _h : 'active');
5010
5186
  }
5011
5187
  }
5012
5188
  async replaceCollaboratorEquityParticipation(client, collaboratorId, equityParticipation) {
@@ -5258,106 +5434,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
5258
5434
  updated_at = NOW()
5259
5435
  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
5436
  }
5261
- async createProjectContractDraft(client, createdByUserId, input) {
5262
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
5263
- const templateRows = input.contractTemplateId
5264
- ? (await client.$queryRawUnsafe(`SELECT id,
5265
- code,
5266
- name,
5267
- description,
5268
- contract_category AS "contractCategory",
5269
- contract_type AS "contractType",
5270
- billing_model AS "billingModel",
5271
- signature_status AS "signatureStatus",
5272
- content_html AS "contentHtml"
5273
- FROM operations_contract_template
5274
- WHERE id = $1
5275
- AND deleted_at IS NULL
5276
- LIMIT 1`, input.contractTemplateId))
5277
- : [];
5278
- const selectedTemplate = (_a = templateRows[0]) !== null && _a !== void 0 ? _a : null;
5279
- const templateContext = {
5280
- project_code: input.projectCode,
5281
- project_name: input.projectName,
5282
- client_name: input.clientName,
5283
- start_date: (_b = input.startDate) !== null && _b !== void 0 ? _b : '',
5284
- end_date: (_c = input.endDate) !== null && _c !== void 0 ? _c : '',
5285
- budget_amount: input.budgetAmount !== null && input.budgetAmount !== undefined
5286
- ? String(input.budgetAmount)
5287
- : '',
5288
- monthly_hour_cap: input.monthlyHourCap !== null && input.monthlyHourCap !== undefined
5289
- ? String(input.monthlyHourCap)
5290
- : '',
5291
- };
5292
- const applyTemplateVariables = (value) => {
5293
- const source = value !== null && value !== void 0 ? value : '';
5294
- return Object.entries(templateContext).reduce((result, [key, replacement]) => result.split(`{{${key}}}`).join(replacement || ''), source);
5295
- };
5296
- const templateCodePrefix = this.normalizeOptionalText(selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.code);
5297
- const generatedContractCode = ((_e = (_d = this.normalizeOptionalText(input.contractCode)) !== null && _d !== void 0 ? _d : (templateCodePrefix
5298
- ? `${templateCodePrefix}-${input.projectCode}`
5299
- : null)) !== null && _e !== void 0 ? _e : `PRJ-${input.projectCode}`).slice(0, 40);
5300
- const generatedContractName = (_g = (_f = this.normalizeOptionalText(input.contractName)) !== null && _f !== void 0 ? _f : this.normalizeOptionalText(applyTemplateVariables(selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.name))) !== null && _g !== void 0 ? _g : `${input.projectName} Service Agreement`;
5301
- const generatedDescription = this.normalizeOptionalText(applyTemplateVariables((_h = input.description) !== null && _h !== void 0 ? _h : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.description));
5302
- const generatedContentHtml = this.normalizeOptionalText(applyTemplateVariables(selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contentHtml));
5303
- const created = await client.$queryRawUnsafe(`INSERT INTO operations_contract (
5304
- code,
5305
- name,
5306
- contract_category,
5307
- contract_type,
5308
- client_name,
5309
- signature_status,
5310
- is_active,
5311
- billing_model,
5312
- account_manager_collaborator_id,
5313
- related_collaborator_id,
5314
- contract_template_id,
5315
- origin_type,
5316
- origin_id,
5317
- start_date,
5318
- end_date,
5319
- signed_at,
5320
- effective_date,
5321
- budget_amount,
5322
- monthly_hour_cap,
5323
- status,
5324
- description,
5325
- content_html,
5326
- created_by_user_id,
5327
- updated_by_user_id,
5328
- created_at,
5329
- updated_at
5330
- ) VALUES (
5331
- $1,
5332
- $2,
5333
- $3::operations_contract_contract_category_70d553ea09_enum,
5334
- $4::operations_contract_contract_type_48331e2ebf_enum,
5335
- $5,
5336
- $6::operations_contract_signature_status_2cb7282a7b_enum,
5337
- true,
5338
- $7::operations_contract_billing_model_409dc7fea2_enum,
5339
- $8,
5340
- NULL,
5341
- $9,
5342
- 'client_project',
5343
- $10,
5344
- $11::date,
5345
- $12::date,
5346
- NULL,
5347
- $11::date,
5348
- $13,
5349
- $14,
5350
- 'draft',
5351
- $15,
5352
- $16,
5353
- $17,
5354
- $17,
5355
- NOW(),
5356
- NOW()
5357
- )
5358
- RETURNING id`, generatedContractCode, generatedContractName, (_j = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contractCategory) !== null && _j !== void 0 ? _j : 'client', (_k = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contractType) !== null && _k !== void 0 ? _k : 'service_agreement', input.clientName, (_l = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.signatureStatus) !== null && _l !== void 0 ? _l : 'not_started', (_m = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.billingModel) !== null && _m !== void 0 ? _m : input.billingModel, input.managerCollaboratorId, (_o = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.id) !== null && _o !== void 0 ? _o : null, input.projectId, (_p = input.startDate) !== null && _p !== void 0 ? _p : new Date().toISOString().slice(0, 10), (_q = input.endDate) !== null && _q !== void 0 ? _q : null, (_r = input.budgetAmount) !== null && _r !== void 0 ? _r : null, (_s = input.monthlyHourCap) !== null && _s !== void 0 ? _s : null, generatedDescription, generatedContentHtml, createdByUserId);
5359
- return (_t = created[0]) === null || _t === void 0 ? void 0 : _t.id;
5360
- }
5361
5437
  async replaceContractParties(client, contractId, parties) {
5362
5438
  var _a, _b, _c, _d, _e, _f;
5363
5439
  await client.$executeRawUnsafe(`UPDATE operations_contract_party
@@ -5385,88 +5461,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
5385
5461
  )`, 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);
5386
5462
  }
5387
5463
  }
5388
- async replaceContractSignatures(client, contractId, signatures) {
5389
- var _a, _b, _c, _d;
5390
- await client.$executeRawUnsafe(`UPDATE operations_contract_signature
5391
- SET deleted_at = NOW(),
5392
- updated_at = NOW()
5393
- WHERE contract_id = $1
5394
- AND deleted_at IS NULL`, contractId);
5395
- for (const signature of signatures !== null && signatures !== void 0 ? signatures : []) {
5396
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_signature (
5397
- contract_id,
5398
- signer_name,
5399
- signer_role,
5400
- signer_email,
5401
- signer_status,
5402
- signed_at,
5403
- created_at,
5404
- updated_at
5405
- ) VALUES (
5406
- $1, $2, $3, $4,
5407
- $5::operations_contract_signature_signer_status_1e6fbe2519_enum,
5408
- $6::timestamp, NOW(), NOW()
5409
- )`, 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);
5410
- }
5411
- }
5412
- async replaceContractFinancialTerms(client, contractId, financialTerms) {
5413
- var _a, _b, _c, _d;
5414
- await client.$executeRawUnsafe(`UPDATE operations_contract_financial_term
5415
- SET deleted_at = NOW(),
5416
- updated_at = NOW()
5417
- WHERE contract_id = $1
5418
- AND deleted_at IS NULL`, contractId);
5419
- for (const term of financialTerms !== null && financialTerms !== void 0 ? financialTerms : []) {
5420
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_financial_term (
5421
- contract_id,
5422
- term_type,
5423
- label,
5424
- amount,
5425
- recurrence,
5426
- due_day,
5427
- notes,
5428
- created_at,
5429
- updated_at
5430
- ) VALUES (
5431
- $1,
5432
- $2::operations_contract_financial_term_term_type_700635c06a_enum,
5433
- $3,
5434
- $4,
5435
- $5::operations_contract_financial_term_recurrence_ba90bbe3bf_enum,
5436
- $6,
5437
- $7,
5438
- NOW(), NOW()
5439
- )`, 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);
5440
- }
5441
- }
5442
- async replaceContractRevisions(client, contractId, revisions) {
5443
- var _a, _b, _c, _d;
5444
- await client.$executeRawUnsafe(`UPDATE operations_contract_revision
5445
- SET deleted_at = NOW(),
5446
- updated_at = NOW()
5447
- WHERE contract_id = $1
5448
- AND deleted_at IS NULL`, contractId);
5449
- for (const revision of revisions !== null && revisions !== void 0 ? revisions : []) {
5450
- await client.$executeRawUnsafe(`INSERT INTO operations_contract_revision (
5451
- contract_id,
5452
- revision_type,
5453
- title,
5454
- effective_date,
5455
- status,
5456
- summary,
5457
- created_at,
5458
- updated_at
5459
- ) VALUES (
5460
- $1,
5461
- $2::operations_contract_revision_revision_type_cf5ba1a538_enum,
5462
- $3,
5463
- $4::date,
5464
- $5::operations_contract_revision_status_f44f35bb66_enum,
5465
- $6,
5466
- NOW(), NOW()
5467
- )`, 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);
5468
- }
5469
- }
5470
5464
  async replaceContractDocument(client, contractId, documentType, document) {
5471
5465
  var _a, _b, _c, _d, _e;
5472
5466
  await client.$executeRawUnsafe(`UPDATE operations_contract_document
@@ -5474,7 +5468,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
5474
5468
  updated_at = NOW()
5475
5469
  WHERE contract_id = $1
5476
5470
  AND deleted_at IS NULL
5477
- AND document_type = $2`, contractId, documentType);
5471
+ AND document_type::text = $2`, contractId, documentType);
5478
5472
  await client.$executeRawUnsafe(`INSERT INTO operations_contract_document (
5479
5473
  contract_id,
5480
5474
  document_type,
@@ -5489,7 +5483,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
5489
5483
  created_at,
5490
5484
  updated_at
5491
5485
  ) VALUES (
5492
- $1, $2, $3, $4, $5, $6, true, $7, $8, $9, NOW(), NOW()
5486
+ $1, $2::operations_contract_document_document_type_15ebaff0c9_enum, $3, $4, $5, $6, true, $7::operations_contract_document_extraction_status_6d94c231f3_enum, $8, $9, NOW(), NOW()
5493
5487
  )`, contractId, documentType, (_a = document.fileId) !== null && _a !== void 0 ? _a : null, document.fileName, document.mimeType, (_b = document.fileContentBase64) !== null && _b !== void 0 ? _b : null, (_c = document.extractionStatus) !== null && _c !== void 0 ? _c : 'skipped', (_d = document.extractionSummary) !== null && _d !== void 0 ? _d : null, (_e = document.notes) !== null && _e !== void 0 ? _e : null);
5494
5488
  }
5495
5489
  async resolveContractExtractionFile(userId, data) {
@@ -5574,7 +5568,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
5574
5568
  }
5575
5569
  }
5576
5570
  async buildContractPdfHtml(contract) {
5577
- var _a, _b, _c, _d, _e;
5571
+ var _a, _b, _c, _d;
5578
5572
  const logoUrl = await this.resolveContractPdfLogoUrl();
5579
5573
  const contentHtml = (_c = (_a = this.normalizeOptionalText(contract.contentHtml)) !== null && _a !== void 0 ? _a : (_b = this.normalizeOptionalText(contract.description)) === null || _b === void 0 ? void 0 : _b.split(/\n{2,}/).map((paragraph) => `<p>${this.escapeHtml(paragraph)}</p>`).join('')) !== null && _c !== void 0 ? _c : '<p>No contract body was provided yet.</p>';
5580
5574
  const partiesHtml = ((_d = contract.parties) !== null && _d !== void 0 ? _d : []).length
@@ -5586,17 +5580,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
5586
5580
  </li>`)
5587
5581
  .join('')
5588
5582
  : '<li><strong>No parties registered yet.</strong><span>Complete this contract later if needed.</span></li>';
5589
- const financialTermsHtml = ((_e = contract.financialTerms) !== null && _e !== void 0 ? _e : []).length
5590
- ? contract.financialTerms
5591
- .map((term) => `
5592
- <li>
5593
- <strong>${this.escapeHtml(term.label || 'Term')}</strong>
5594
- <span>${this.escapeHtml([term.termType, term.amount, term.recurrence]
5595
- .filter((item) => item !== null && item !== undefined && String(item).trim())
5596
- .join(' • '))}</span>
5597
- </li>`)
5598
- .join('')
5599
- : '<li><strong>No financial terms registered yet.</strong><span>The draft is intentionally lightweight.</span></li>';
5600
5583
  return `<!DOCTYPE html>
5601
5584
  <html lang="en">
5602
5585
  <head>
@@ -5741,11 +5724,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
5741
5724
  <ul>${partiesHtml}</ul>
5742
5725
  </section>
5743
5726
 
5744
- <section>
5745
- <h2>Financial Terms</h2>
5746
- <ul>${financialTermsHtml}</ul>
5747
- </section>
5748
-
5749
5727
  <section>
5750
5728
  <h2>Contract Body</h2>
5751
5729
  <div class="content">${contentHtml}</div>
@@ -5986,7 +5964,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
5986
5964
  effectiveDate: contract.effectiveDate,
5987
5965
  signatureStatus: contract.signatureStatus,
5988
5966
  parties: contract.parties,
5989
- financialTerms: contract.financialTerms,
5990
5967
  }, null, 2)}`,
5991
5968
  `Contract HTML:\n${(_b = this.normalizeOptionalText(contract.contentHtml)) !== null && _b !== void 0 ? _b : ''}`,
5992
5969
  ].join('\n\n'),
@@ -6026,7 +6003,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
6026
6003
  }
6027
6004
  }
6028
6005
  buildHeuristicContractLegalReview(contract) {
6029
- var _a, _b, _c, _d, _e, _f, _g;
6006
+ var _a, _b, _c, _d, _e, _f;
6030
6007
  const contentText = ((_a = this.normalizeOptionalText(contract.contentHtml)) !== null && _a !== void 0 ? _a : '')
6031
6008
  .replace(/<[^>]+>/g, ' ')
6032
6009
  .replace(/\s+/g, ' ')
@@ -6064,12 +6041,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
6064
6041
  if (!this.normalizeOptionalText(primaryParty === null || primaryParty === void 0 ? void 0 : primaryParty.documentNumber)) {
6065
6042
  warnings.push('Consider confirming the primary party document number.');
6066
6043
  }
6067
- if (!((_g = contract.financialTerms) !== null && _g !== void 0 ? _g : []).length && contract.budgetAmount == null) {
6068
- warnings.push('Commercial conditions are still generic; define prices, recurrence, and penalties.');
6069
- checklist.push('Attention: validate billing, adjustments, and penalties.');
6044
+ if (contract.budgetAmount == null) {
6045
+ warnings.push('Commercial conditions are still generic; define a budget amount before signature.');
6046
+ checklist.push('Attention: validate billing and penalties.');
6070
6047
  }
6071
6048
  else {
6072
- checklist.push('OK: there is at least one financial reference to review.');
6049
+ checklist.push('OK: there is a budget reference to review.');
6073
6050
  }
6074
6051
  if (!contentText) {
6075
6052
  missingFields.push('Contract body content');
@@ -6235,34 +6212,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
6235
6212
  isPrimary: this.normalizeExtractionBoolean(party.isPrimary),
6236
6213
  }))
6237
6214
  .filter((party) => party.displayName),
6238
- signatures: this.normalizeObjectList(raw.signatures)
6239
- .map((signature) => ({
6240
- signerName: this.normalizeExtractionString(signature.signerName),
6241
- signerRole: this.normalizeExtractionString(signature.signerRole),
6242
- signerEmail: this.normalizeExtractionString(signature.signerEmail),
6243
- status: this.normalizeEnumValue(signature.status, SIGNATURE_ITEM_STATUS_VALUES, 'pending'),
6244
- signedAt: this.normalizeExtractionDate(signature.signedAt),
6245
- }))
6246
- .filter((signature) => signature.signerName),
6247
- financialTerms: this.normalizeObjectList(raw.financialTerms)
6248
- .map((term) => ({
6249
- label: this.normalizeExtractionString(term.label),
6250
- termType: this.normalizeEnumValue(term.termType, FINANCIAL_TERM_TYPE_VALUES, 'value'),
6251
- amount: this.normalizeExtractionNumber(term.amount),
6252
- recurrence: this.normalizeEnumValue(term.recurrence, RECURRENCE_VALUES, 'one_time'),
6253
- dueDay: this.normalizeExtractionNumber(term.dueDay),
6254
- notes: this.normalizeExtractionString(term.notes),
6255
- }))
6256
- .filter((term) => term.label),
6257
- revisions: this.normalizeObjectList(raw.revisions)
6258
- .map((revision) => ({
6259
- title: this.normalizeExtractionString(revision.title),
6260
- revisionType: this.normalizeEnumValue(revision.revisionType, REVISION_TYPE_VALUES, 'revision'),
6261
- effectiveDate: this.normalizeExtractionDate(revision.effectiveDate),
6262
- status: this.normalizeEnumValue(revision.status, REVISION_STATUS_VALUES, 'draft'),
6263
- summary: this.normalizeExtractionString(revision.summary),
6264
- }))
6265
- .filter((revision) => revision.title),
6266
6215
  };
6267
6216
  if (!draft.name)
6268
6217
  missingFields.push('Contract title');
@@ -6364,6 +6313,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
6364
6313
  offset: (page - 1) * pageSize,
6365
6314
  };
6366
6315
  }
6316
+ shouldPaginate(input) {
6317
+ return [
6318
+ input.page,
6319
+ input.pageSize,
6320
+ input.search,
6321
+ input.sortField,
6322
+ input.sortOrder,
6323
+ ].some((value) => value !== undefined && value !== null && value !== '');
6324
+ }
6367
6325
  buildPaginationResult(data, total, page, pageSize) {
6368
6326
  const lastPage = Math.max(1, Math.ceil(total / Math.max(pageSize, 1)));
6369
6327
  return {
@@ -6508,6 +6466,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
6508
6466
  async execute(sql, params = []) {
6509
6467
  return this.prisma.$executeRawUnsafe(sql, ...params);
6510
6468
  }
6469
+ async getNextCollaboratorTypeSortOrder(client = this.prisma) {
6470
+ var _a, _b;
6471
+ const row = (await client.$queryRawUnsafe(`SELECT COALESCE(MAX(sort_order), 0) + 1 AS "nextSortOrder"
6472
+ FROM operations_collaborator_type`));
6473
+ return Number((_b = (_a = row === null || row === void 0 ? void 0 : row[0]) === null || _a === void 0 ? void 0 : _a.nextSortOrder) !== null && _b !== void 0 ? _b : 1);
6474
+ }
6511
6475
  };
6512
6476
  exports.OperationsService = OperationsService;
6513
6477
  exports.OperationsService = OperationsService = OperationsService_1 = __decorate([