@hed-hog/operations 0.0.305 → 0.0.309
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/operations-approvals.controller.d.ts +114 -1
- package/dist/controllers/operations-approvals.controller.d.ts.map +1 -1
- package/dist/controllers/operations-approvals.controller.js +16 -3
- package/dist/controllers/operations-approvals.controller.js.map +1 -1
- package/dist/controllers/operations-collaborators.controller.d.ts +16 -1
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +16 -3
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +14 -453
- package/dist/controllers/operations-contracts.controller.d.ts.map +1 -1
- package/dist/controllers/operations-contracts.controller.js +11 -112
- package/dist/controllers/operations-contracts.controller.js.map +1 -1
- package/dist/controllers/operations-org-structure.controller.d.ts +65 -2
- package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -1
- package/dist/controllers/operations-org-structure.controller.js +18 -5
- package/dist/controllers/operations-org-structure.controller.js.map +1 -1
- package/dist/controllers/operations-projects.controller.d.ts +28 -4
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.js +17 -5
- package/dist/controllers/operations-projects.controller.js.map +1 -1
- package/dist/controllers/operations-timesheets.controller.d.ts +52 -4
- package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
- package/dist/controllers/operations-timesheets.controller.js +28 -11
- package/dist/controllers/operations-timesheets.controller.js.map +1 -1
- package/dist/dto/list-approvals.dto.d.ts +6 -0
- package/dist/dto/list-approvals.dto.d.ts.map +1 -0
- package/dist/dto/list-approvals.dto.js +28 -0
- package/dist/dto/list-approvals.dto.js.map +1 -0
- package/dist/dto/list-collaborator-types.dto.d.ts +3 -1
- package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -1
- package/dist/dto/list-collaborator-types.dto.js +7 -1
- package/dist/dto/list-collaborator-types.dto.js.map +1 -1
- package/dist/dto/list-collaborators.dto.d.ts +1 -0
- package/dist/dto/list-collaborators.dto.d.ts.map +1 -1
- package/dist/dto/list-collaborators.dto.js +5 -0
- package/dist/dto/list-collaborators.dto.js.map +1 -1
- package/dist/dto/list-contracts.dto.d.ts +8 -0
- package/dist/dto/list-contracts.dto.d.ts.map +1 -0
- package/dist/dto/list-contracts.dto.js +38 -0
- package/dist/dto/list-contracts.dto.js.map +1 -0
- package/dist/dto/list-departments.dto.d.ts +5 -0
- package/dist/dto/list-departments.dto.d.ts.map +1 -0
- package/dist/dto/list-departments.dto.js +23 -0
- package/dist/dto/list-departments.dto.js.map +1 -0
- package/dist/dto/list-projects.dto.d.ts +5 -0
- package/dist/dto/list-projects.dto.d.ts.map +1 -0
- package/dist/dto/list-projects.dto.js +23 -0
- package/dist/dto/list-projects.dto.js.map +1 -0
- package/dist/dto/list-schedule-adjustments.dto.d.ts +5 -0
- package/dist/dto/list-schedule-adjustments.dto.d.ts.map +1 -0
- package/dist/dto/list-schedule-adjustments.dto.js +23 -0
- package/dist/dto/list-schedule-adjustments.dto.js.map +1 -0
- package/dist/dto/list-time-off-requests.dto.d.ts +5 -0
- package/dist/dto/list-time-off-requests.dto.d.ts.map +1 -0
- package/dist/dto/list-time-off-requests.dto.js +23 -0
- package/dist/dto/list-time-off-requests.dto.js.map +1 -0
- package/dist/dto/list-timesheets.dto.d.ts +5 -0
- package/dist/dto/list-timesheets.dto.d.ts.map +1 -0
- package/dist/dto/list-timesheets.dto.js +23 -0
- package/dist/dto/list-timesheets.dto.js.map +1 -0
- package/dist/dto/reorder-collaborator-types.dto.d.ts +4 -0
- package/dist/dto/reorder-collaborator-types.dto.d.ts.map +1 -0
- package/dist/dto/reorder-collaborator-types.dto.js +25 -0
- package/dist/dto/reorder-collaborator-types.dto.js.map +1 -0
- package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
- package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
- package/dist/dto/update-collaborator-type.dto.js +2 -1
- package/dist/dto/update-collaborator-type.dto.js.map +1 -1
- package/dist/operations.service.d.ts +362 -271
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1195 -1098
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +73 -22
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/menu.yaml +19 -55
- package/hedhog/data/operations_collaborator_type.yaml +76 -76
- package/hedhog/data/route.yaml +52 -70
- package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +8 -1
- package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +15 -10
- package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +108 -213
- package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +251 -2039
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +167 -60
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +70 -301
- package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +102 -51
- package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +19 -24
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +14 -9
- package/hedhog/frontend/app/approvals/page.tsx.ejs +843 -151
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +457 -154
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +118 -49
- package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/contracts/page.tsx.ejs +215 -617
- package/hedhog/frontend/app/departments/page.tsx.ejs +257 -113
- package/hedhog/frontend/app/projects/page.tsx.ejs +90 -51
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +546 -118
- package/hedhog/frontend/app/time-off/page.tsx.ejs +400 -123
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +647 -342
- package/hedhog/frontend/messages/en.json +148 -14
- package/hedhog/frontend/messages/pt.json +199 -56
- package/hedhog/table/operations_collaborator.yaml +18 -18
- package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
- package/hedhog/table/operations_collaborator_type.yaml +33 -33
- package/hedhog/table/operations_contract.yaml +0 -9
- package/hedhog/table/operations_contract_document.yaml +33 -33
- package/package.json +4 -4
- package/src/controllers/operations-approvals.controller.ts +9 -3
- package/src/controllers/operations-collaborators.controller.ts +15 -2
- package/src/controllers/operations-contracts.controller.ts +8 -92
- package/src/controllers/operations-org-structure.controller.ts +17 -4
- package/src/controllers/operations-projects.controller.ts +10 -4
- package/src/controllers/operations-timesheets.controller.ts +30 -8
- package/src/dto/create-collaborator-type.dto.ts +43 -43
- package/src/dto/create-collaborator.dto.ts +223 -223
- package/src/dto/list-approvals.dto.ts +12 -0
- package/src/dto/list-collaborator-types.dto.ts +20 -15
- package/src/dto/list-collaborators.dto.ts +34 -30
- package/src/dto/list-contracts.dto.ts +20 -0
- package/src/dto/list-departments.dto.ts +8 -0
- package/src/dto/list-projects.dto.ts +8 -0
- package/src/dto/list-schedule-adjustments.dto.ts +8 -0
- package/src/dto/list-time-off-requests.dto.ts +8 -0
- package/src/dto/list-timesheets.dto.ts +8 -0
- package/src/dto/reorder-collaborator-types.dto.ts +10 -0
- package/src/dto/update-collaborator-type.dto.ts +4 -3
- package/src/dto/update-collaborator.dto.ts +3 -3
- package/src/operations.service.spec.ts +96 -30
- package/src/operations.service.ts +1738 -1777
- package/hedhog/frontend/app/_components/contract-creation-wizard.tsx.ejs +0 -631
- package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +0 -526
- package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +0 -247
- package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +0 -3520
- package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +0 -380
- package/hedhog/frontend/app/team/page.tsx.ejs +0 -352
- package/hedhog/table/operations_contract_financial_term.yaml +0 -40
- package/hedhog/table/operations_contract_revision.yaml +0 -38
- package/hedhog/table/operations_contract_signature.yaml +0 -38
- 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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
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,28 +1353,49 @@ 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
|
|
1053
|
-
GROUP BY p.id, c.id, m.id
|
|
1054
|
-
|
|
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;
|
|
1058
1385
|
const actor = await this.getActorContext(userId);
|
|
1059
1386
|
this.ensureCollaborator(actor);
|
|
1060
|
-
if (!actor.collaboratorId) {
|
|
1061
|
-
throw new common_1.BadRequestException('Collaborator context is required.');
|
|
1062
|
-
}
|
|
1063
1387
|
const pagination = this.normalizePaginationParams(paginationParams, {
|
|
1064
1388
|
defaultSortField: 'name',
|
|
1065
1389
|
defaultSortOrder: 'asc',
|
|
1066
1390
|
allowedSortFields: ['name', 'code', 'clientName', 'startDate', 'endDate'],
|
|
1067
1391
|
});
|
|
1068
|
-
const
|
|
1392
|
+
const filter = this.buildIdFilter(actor.visibleProjectIds, 'p.id', actor.isDirector);
|
|
1393
|
+
const params = [...filter.params];
|
|
1069
1394
|
const filters = [
|
|
1070
1395
|
'p.deleted_at IS NULL',
|
|
1071
1396
|
'pa.deleted_at IS NULL',
|
|
1072
|
-
`pa.collaborator_id = $1`,
|
|
1073
1397
|
`pa.status IN ('planned', 'active')`,
|
|
1398
|
+
filter.clause,
|
|
1074
1399
|
];
|
|
1075
1400
|
if (pagination.search) {
|
|
1076
1401
|
const searchPlaceholder = this.param(params, `%${pagination.search}%`);
|
|
@@ -1120,21 +1445,25 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1120
1445
|
var _a, _b;
|
|
1121
1446
|
const actor = await this.getActorContext(userId);
|
|
1122
1447
|
this.ensureCollaborator(actor);
|
|
1123
|
-
if (!actor.collaboratorId) {
|
|
1124
|
-
throw new common_1.BadRequestException('Collaborator context is required.');
|
|
1125
|
-
}
|
|
1126
1448
|
const pagination = this.normalizePaginationParams(paginationParams, {
|
|
1127
1449
|
defaultSortField: 'name',
|
|
1128
1450
|
defaultSortOrder: 'asc',
|
|
1129
1451
|
allowedSortFields: ['name', 'projectName', 'status', 'createdAt'],
|
|
1130
1452
|
});
|
|
1131
|
-
const
|
|
1453
|
+
const projectFilter = this.buildIdFilter(actor.visibleProjectIds, 'COALESCE(t.project_id, pa.project_id)', actor.isDirector);
|
|
1454
|
+
const params = [...projectFilter.params];
|
|
1132
1455
|
const filters = [
|
|
1133
1456
|
't.deleted_at IS NULL',
|
|
1134
|
-
'pa.deleted_at IS NULL',
|
|
1135
1457
|
'p.deleted_at IS NULL',
|
|
1136
|
-
|
|
1137
|
-
`
|
|
1458
|
+
projectFilter.clause,
|
|
1459
|
+
`(
|
|
1460
|
+
t.project_id IS NOT NULL
|
|
1461
|
+
OR (
|
|
1462
|
+
pa.id IS NOT NULL
|
|
1463
|
+
AND pa.deleted_at IS NULL
|
|
1464
|
+
AND pa.status IN ('planned', 'active')
|
|
1465
|
+
)
|
|
1466
|
+
)`,
|
|
1138
1467
|
];
|
|
1139
1468
|
if (pagination.search) {
|
|
1140
1469
|
const searchPlaceholder = this.param(params, `%${pagination.search}%`);
|
|
@@ -1149,18 +1478,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1149
1478
|
filters.push(`pa.id = ${this.param(params, paginationParams.projectAssignmentId)}`);
|
|
1150
1479
|
}
|
|
1151
1480
|
if (paginationParams.projectId) {
|
|
1152
|
-
filters.push(`pa.project_id = ${this.param(params, paginationParams.projectId)}`);
|
|
1481
|
+
filters.push(`COALESCE(t.project_id, pa.project_id) = ${this.param(params, paginationParams.projectId)}`);
|
|
1153
1482
|
}
|
|
1154
1483
|
if (paginationParams.status) {
|
|
1155
|
-
filters.push(`t.status = ${this.param(params, paginationParams.status)}`);
|
|
1484
|
+
filters.push(`t.status::text = ${this.param(params, paginationParams.status)}`);
|
|
1156
1485
|
}
|
|
1157
1486
|
const whereClause = filters.join(' AND ');
|
|
1158
1487
|
const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
|
|
1159
1488
|
FROM operations_task t
|
|
1160
|
-
JOIN operations_project_assignment pa
|
|
1489
|
+
LEFT JOIN operations_project_assignment pa
|
|
1161
1490
|
ON pa.id = t.project_assignment_id
|
|
1162
1491
|
JOIN operations_project p
|
|
1163
|
-
ON p.id = pa.project_id
|
|
1492
|
+
ON p.id = COALESCE(t.project_id, pa.project_id)
|
|
1164
1493
|
WHERE ${whereClause}`, params);
|
|
1165
1494
|
const sortColumn = (_a = {
|
|
1166
1495
|
name: 't.name',
|
|
@@ -1175,16 +1504,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1175
1504
|
t.name,
|
|
1176
1505
|
t.description,
|
|
1177
1506
|
t.status,
|
|
1178
|
-
pa.project_id AS "projectId",
|
|
1507
|
+
COALESCE(t.project_id, pa.project_id) AS "projectId",
|
|
1179
1508
|
pa.id AS "projectAssignmentId",
|
|
1180
1509
|
p.name AS "projectName",
|
|
1181
1510
|
p.code AS "projectCode",
|
|
1182
1511
|
t.created_at AS "createdAt"
|
|
1183
1512
|
FROM operations_task t
|
|
1184
|
-
JOIN operations_project_assignment pa
|
|
1513
|
+
LEFT JOIN operations_project_assignment pa
|
|
1185
1514
|
ON pa.id = t.project_assignment_id
|
|
1186
1515
|
JOIN operations_project p
|
|
1187
|
-
ON p.id = pa.project_id
|
|
1516
|
+
ON p.id = COALESCE(t.project_id, pa.project_id)
|
|
1188
1517
|
WHERE ${whereClause}
|
|
1189
1518
|
ORDER BY ${sortColumn} ${pagination.sortOrder.toUpperCase()}, t.id ASC
|
|
1190
1519
|
LIMIT ${limitPlaceholder}
|
|
@@ -1463,8 +1792,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1463
1792
|
const resolvedTask = data.taskId
|
|
1464
1793
|
? await this.getOwnedTaskRecord(tx, actor.collaboratorId, data.taskId)
|
|
1465
1794
|
: null;
|
|
1466
|
-
if (resolvedTask && resolvedTask.
|
|
1467
|
-
throw new common_1.BadRequestException('The selected task does not belong to the chosen project
|
|
1795
|
+
if (resolvedTask && resolvedTask.projectId !== assignment.projectId) {
|
|
1796
|
+
throw new common_1.BadRequestException('The selected task does not belong to the chosen project.');
|
|
1468
1797
|
}
|
|
1469
1798
|
const activityLabel = (_e = (_d = (_c = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _c !== void 0 ? _c : taskLabel) !== null && _d !== void 0 ? _d : assignment.roleLabel) !== null && _e !== void 0 ? _e : assignment.projectName;
|
|
1470
1799
|
if (!activityLabel) {
|
|
@@ -1500,6 +1829,68 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1500
1829
|
});
|
|
1501
1830
|
return this.getTimesheetEntryByIdForActor(actor, createdEntryId);
|
|
1502
1831
|
}
|
|
1832
|
+
async updateTimesheetEntry(userId, entryId, data) {
|
|
1833
|
+
var _a;
|
|
1834
|
+
const actor = await this.getActorContext(userId);
|
|
1835
|
+
this.ensureCollaborator(actor);
|
|
1836
|
+
this.requireFields(data, ['workDate', 'duration']);
|
|
1837
|
+
if (!actor.collaboratorId && !actor.isDirector) {
|
|
1838
|
+
throw new common_1.BadRequestException('Collaborator context is required.');
|
|
1839
|
+
}
|
|
1840
|
+
const entry = await this.getTimesheetEntryByIdForActor(actor, entryId);
|
|
1841
|
+
if (!actor.isDirector && entry.collaboratorId !== actor.collaboratorId) {
|
|
1842
|
+
throw new common_1.ForbiddenException('Only the entry owner can update this timesheet entry.');
|
|
1843
|
+
}
|
|
1844
|
+
if (!['draft', 'rejected'].includes(entry.status)) {
|
|
1845
|
+
throw new common_1.BadRequestException('Only draft or rejected timesheet entries can be edited.');
|
|
1846
|
+
}
|
|
1847
|
+
const collaboratorId = actor.isDirector
|
|
1848
|
+
? entry.collaboratorId
|
|
1849
|
+
: actor.collaboratorId;
|
|
1850
|
+
const durationMinutes = this.normalizeDurationMinutes(data.duration, data.unit);
|
|
1851
|
+
const taskLabel = (_a = this.normalizeOptionalText(data.taskName)) !== null && _a !== void 0 ? _a : this.normalizeOptionalText(data.activityLabel);
|
|
1852
|
+
const targetWeek = this.getWorkWeekRange(data.workDate);
|
|
1853
|
+
const isSameWeek = entry.weekStartDate === targetWeek.weekStartDate &&
|
|
1854
|
+
entry.weekEndDate === targetWeek.weekEndDate;
|
|
1855
|
+
await this.prisma.$transaction(async (tx) => {
|
|
1856
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1857
|
+
const assignment = await this.resolveOwnedProjectAssignment(tx, collaboratorId, {
|
|
1858
|
+
projectId: (_a = data.projectId) !== null && _a !== void 0 ? _a : null,
|
|
1859
|
+
projectAssignmentId: (_b = data.projectAssignmentId) !== null && _b !== void 0 ? _b : null,
|
|
1860
|
+
});
|
|
1861
|
+
const resolvedTask = data.taskId
|
|
1862
|
+
? await this.getOwnedTaskRecord(tx, collaboratorId, data.taskId)
|
|
1863
|
+
: null;
|
|
1864
|
+
if (resolvedTask && resolvedTask.projectId !== assignment.projectId) {
|
|
1865
|
+
throw new common_1.BadRequestException('The selected task does not belong to the chosen project.');
|
|
1866
|
+
}
|
|
1867
|
+
const activityLabel = (_e = (_d = (_c = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _c !== void 0 ? _c : taskLabel) !== null && _d !== void 0 ? _d : assignment.roleLabel) !== null && _e !== void 0 ? _e : assignment.projectName;
|
|
1868
|
+
if (!activityLabel) {
|
|
1869
|
+
throw new common_1.BadRequestException('A task is required for the timesheet entry.');
|
|
1870
|
+
}
|
|
1871
|
+
const targetTimesheetId = isSameWeek
|
|
1872
|
+
? entry.timesheetId
|
|
1873
|
+
: await this.getOrCreateTimesheetForWorkDate(tx, collaboratorId, data.workDate);
|
|
1874
|
+
await tx.$executeRawUnsafe(`UPDATE operations_timesheet_entry
|
|
1875
|
+
SET timesheet_id = $1,
|
|
1876
|
+
project_assignment_id = $2,
|
|
1877
|
+
task_id = $3,
|
|
1878
|
+
activity_label = $4,
|
|
1879
|
+
work_date = $5::date,
|
|
1880
|
+
duration_minutes = $6,
|
|
1881
|
+
hours = $7,
|
|
1882
|
+
description = $8,
|
|
1883
|
+
updated_at = NOW()
|
|
1884
|
+
WHERE id = $9
|
|
1885
|
+
AND deleted_at IS NULL`, targetTimesheetId, assignment.id, (_g = (_f = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.id) !== null && _f !== void 0 ? _f : data.taskId) !== null && _g !== void 0 ? _g : null, activityLabel, data.workDate, durationMinutes, Number((durationMinutes / 60).toFixed(2)), this.normalizeOptionalText(data.description), entryId);
|
|
1886
|
+
await this.refreshTimesheetTotal(tx, entry.timesheetId);
|
|
1887
|
+
if (targetTimesheetId !== entry.timesheetId) {
|
|
1888
|
+
await this.refreshTimesheetTotal(tx, targetTimesheetId);
|
|
1889
|
+
}
|
|
1890
|
+
await this.cleanupEmptyEditableTimesheet(tx, entry.timesheetId);
|
|
1891
|
+
});
|
|
1892
|
+
return this.getTimesheetEntryByIdForActor(actor, entryId);
|
|
1893
|
+
}
|
|
1503
1894
|
async removeTimesheetEntry(userId, entryId) {
|
|
1504
1895
|
const actor = await this.getActorContext(userId);
|
|
1505
1896
|
this.ensureCollaborator(actor);
|
|
@@ -1520,6 +1911,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1520
1911
|
WHERE id = $1
|
|
1521
1912
|
AND deleted_at IS NULL`, entryId);
|
|
1522
1913
|
await this.refreshTimesheetTotal(tx, entry.timesheetId);
|
|
1914
|
+
await this.cleanupEmptyEditableTimesheet(tx, entry.timesheetId);
|
|
1523
1915
|
});
|
|
1524
1916
|
return { success: true };
|
|
1525
1917
|
}
|
|
@@ -1533,7 +1925,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1533
1925
|
this.ensureDirector(actor);
|
|
1534
1926
|
this.requireFields(data, ['code', 'name']);
|
|
1535
1927
|
const createdProjectId = await this.prisma.$transaction(async (tx) => {
|
|
1536
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o
|
|
1928
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
1537
1929
|
const created = await tx.$queryRawUnsafe(`INSERT INTO operations_project (
|
|
1538
1930
|
contract_id,
|
|
1539
1931
|
manager_collaborator_id,
|
|
@@ -1562,28 +1954,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1562
1954
|
if ((_o = data.teamAssignments) === null || _o === void 0 ? void 0 : _o.length) {
|
|
1563
1955
|
await this.replaceProjectAssignments(tx, projectId, data.teamAssignments);
|
|
1564
1956
|
}
|
|
1565
|
-
if (!data.contractId && data.autoGenerateContractDraft !== false) {
|
|
1566
|
-
const contractId = await this.createProjectContractDraft(tx, actor.userId, {
|
|
1567
|
-
projectId,
|
|
1568
|
-
contractTemplateId: (_p = data.contractTemplateId) !== null && _p !== void 0 ? _p : null,
|
|
1569
|
-
projectCode: data.code,
|
|
1570
|
-
projectName: data.name,
|
|
1571
|
-
clientName: (_q = data.clientName) !== null && _q !== void 0 ? _q : data.name,
|
|
1572
|
-
managerCollaboratorId: (_r = data.managerCollaboratorId) !== null && _r !== void 0 ? _r : null,
|
|
1573
|
-
startDate: (_s = data.startDate) !== null && _s !== void 0 ? _s : null,
|
|
1574
|
-
endDate: (_t = data.endDate) !== null && _t !== void 0 ? _t : null,
|
|
1575
|
-
budgetAmount: (_u = data.budgetAmount) !== null && _u !== void 0 ? _u : null,
|
|
1576
|
-
monthlyHourCap: (_v = data.monthlyHourCap) !== null && _v !== void 0 ? _v : null,
|
|
1577
|
-
billingModel: (_w = data.billingModel) !== null && _w !== void 0 ? _w : 'time_and_material',
|
|
1578
|
-
contractCode: (_x = data.contractCode) !== null && _x !== void 0 ? _x : null,
|
|
1579
|
-
contractName: (_y = data.contractName) !== null && _y !== void 0 ? _y : null,
|
|
1580
|
-
description: (_0 = (_z = data.contractDescription) !== null && _z !== void 0 ? _z : data.summary) !== null && _0 !== void 0 ? _0 : null,
|
|
1581
|
-
});
|
|
1582
|
-
await tx.$executeRawUnsafe(`UPDATE operations_project
|
|
1583
|
-
SET contract_id = $1,
|
|
1584
|
-
updated_at = NOW()
|
|
1585
|
-
WHERE id = $2`, contractId, projectId);
|
|
1586
|
-
}
|
|
1587
1957
|
return projectId;
|
|
1588
1958
|
});
|
|
1589
1959
|
return this.getProjectById(userId, createdProjectId);
|
|
@@ -1608,7 +1978,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1608
1978
|
this.pushUpdate(updates, params, 'start_date', data.startDate, 'date');
|
|
1609
1979
|
this.pushUpdate(updates, params, 'end_date', data.endDate, 'date');
|
|
1610
1980
|
await this.prisma.$transaction(async (tx) => {
|
|
1611
|
-
var _a, _b
|
|
1981
|
+
var _a, _b;
|
|
1612
1982
|
if (updates.length) {
|
|
1613
1983
|
params.push(projectId);
|
|
1614
1984
|
await tx.$executeRawUnsafe(`UPDATE operations_project
|
|
@@ -1622,30 +1992,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1622
1992
|
const nextContractId = data.contractId !== undefined
|
|
1623
1993
|
? data.contractId
|
|
1624
1994
|
: ((_b = (_a = currentProject.relatedContract) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null);
|
|
1625
|
-
|
|
1626
|
-
if (shouldGenerateDraft) {
|
|
1627
|
-
const contractId = await this.createProjectContractDraft(tx, actor.userId, {
|
|
1628
|
-
projectId,
|
|
1629
|
-
contractTemplateId: (_c = data.contractTemplateId) !== null && _c !== void 0 ? _c : null,
|
|
1630
|
-
projectCode: (_d = data.code) !== null && _d !== void 0 ? _d : currentProject.code,
|
|
1631
|
-
projectName: (_e = data.name) !== null && _e !== void 0 ? _e : currentProject.name,
|
|
1632
|
-
clientName: (_g = (_f = data.clientName) !== null && _f !== void 0 ? _f : currentProject.clientName) !== null && _g !== void 0 ? _g : currentProject.name,
|
|
1633
|
-
managerCollaboratorId: (_j = (_h = data.managerCollaboratorId) !== null && _h !== void 0 ? _h : currentProject.managerCollaboratorId) !== null && _j !== void 0 ? _j : null,
|
|
1634
|
-
startDate: (_l = (_k = data.startDate) !== null && _k !== void 0 ? _k : currentProject.startDate) !== null && _l !== void 0 ? _l : null,
|
|
1635
|
-
endDate: (_o = (_m = data.endDate) !== null && _m !== void 0 ? _m : currentProject.endDate) !== null && _o !== void 0 ? _o : null,
|
|
1636
|
-
budgetAmount: (_q = (_p = data.budgetAmount) !== null && _p !== void 0 ? _p : currentProject.budgetAmount) !== null && _q !== void 0 ? _q : null,
|
|
1637
|
-
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,
|
|
1638
|
-
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',
|
|
1639
|
-
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,
|
|
1640
|
-
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,
|
|
1641
|
-
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,
|
|
1642
|
-
});
|
|
1643
|
-
await tx.$executeRawUnsafe(`UPDATE operations_project
|
|
1644
|
-
SET contract_id = $1,
|
|
1645
|
-
updated_at = NOW()
|
|
1646
|
-
WHERE id = $2`, contractId, projectId);
|
|
1647
|
-
}
|
|
1648
|
-
else if (nextContractId &&
|
|
1995
|
+
if (nextContractId &&
|
|
1649
1996
|
(data.monthlyHourCap !== undefined || data.billingModel !== undefined)) {
|
|
1650
1997
|
const contractUpdates = [];
|
|
1651
1998
|
const contractParams = [];
|
|
@@ -1664,149 +2011,54 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1664
2011
|
});
|
|
1665
2012
|
return this.getProjectById(userId, projectId);
|
|
1666
2013
|
}
|
|
1667
|
-
async
|
|
1668
|
-
|
|
1669
|
-
this.ensureDirector(actor);
|
|
1670
|
-
return this.queryRows(`SELECT t.id,
|
|
1671
|
-
t.slug,
|
|
1672
|
-
t.code,
|
|
1673
|
-
t.name,
|
|
1674
|
-
t.description,
|
|
1675
|
-
t.contract_category AS "contractCategory",
|
|
1676
|
-
t.contract_type AS "contractType",
|
|
1677
|
-
t.billing_model AS "billingModel",
|
|
1678
|
-
t.signature_status AS "signatureStatus",
|
|
1679
|
-
t.is_active AS "isActive",
|
|
1680
|
-
t.status,
|
|
1681
|
-
t.content_html AS "contentHtml",
|
|
1682
|
-
t.created_at AS "createdAt",
|
|
1683
|
-
t.updated_at AS "updatedAt",
|
|
1684
|
-
COUNT(DISTINCT c.id)::int AS "usageCount"
|
|
1685
|
-
FROM operations_contract_template t
|
|
1686
|
-
LEFT JOIN operations_contract c
|
|
1687
|
-
ON c.contract_template_id = t.id
|
|
1688
|
-
AND c.deleted_at IS NULL
|
|
1689
|
-
WHERE t.deleted_at IS NULL
|
|
1690
|
-
GROUP BY t.id
|
|
1691
|
-
ORDER BY CASE
|
|
1692
|
-
WHEN t.status = 'active' THEN 0
|
|
1693
|
-
WHEN t.status = 'draft' THEN 1
|
|
1694
|
-
WHEN t.status = 'inactive' THEN 2
|
|
1695
|
-
ELSE 3
|
|
1696
|
-
END,
|
|
1697
|
-
t.name ASC`);
|
|
1698
|
-
}
|
|
1699
|
-
async getContractTemplateById(userId, templateId) {
|
|
1700
|
-
const actor = await this.getActorContext(userId);
|
|
1701
|
-
this.ensureDirector(actor);
|
|
1702
|
-
return this.getContractTemplateRecord(this.prisma, templateId);
|
|
1703
|
-
}
|
|
1704
|
-
async createContractTemplate(userId, data) {
|
|
2014
|
+
async listContracts(userId, filters = {}) {
|
|
2015
|
+
var _a, _b;
|
|
1705
2016
|
const actor = await this.getActorContext(userId);
|
|
1706
|
-
|
|
1707
|
-
const
|
|
1708
|
-
|
|
1709
|
-
|
|
2017
|
+
const params = [];
|
|
2018
|
+
const accessClause = actor.isDirector
|
|
2019
|
+
? 'c.deleted_at IS NULL'
|
|
2020
|
+
: `c.deleted_at IS NULL AND (
|
|
2021
|
+
c.related_collaborator_id = ANY(${this.param(params, actor.visibleCollaboratorIds)}::int[])
|
|
2022
|
+
OR EXISTS (
|
|
2023
|
+
SELECT 1
|
|
2024
|
+
FROM operations_project p_access
|
|
2025
|
+
WHERE p_access.contract_id = c.id
|
|
2026
|
+
AND p_access.deleted_at IS NULL
|
|
2027
|
+
AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
|
|
2028
|
+
)
|
|
2029
|
+
)`;
|
|
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)}`);
|
|
1710
2040
|
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
created_at,
|
|
1733
|
-
updated_at
|
|
1734
|
-
) VALUES (
|
|
1735
|
-
$1, $2, $3, $4,
|
|
1736
|
-
$5, $6, $7, $8, $9, $10, $11,
|
|
1737
|
-
NOW(), NOW()
|
|
1738
|
-
)
|
|
1739
|
-
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)));
|
|
1740
|
-
const templateId = (_j = created[0]) === null || _j === void 0 ? void 0 : _j.id;
|
|
1741
|
-
if (!templateId) {
|
|
1742
|
-
throw new common_1.BadRequestException('Unable to create the contract template.');
|
|
1743
|
-
}
|
|
1744
|
-
return this.getContractTemplateRecord(tx, templateId, true);
|
|
1745
|
-
});
|
|
1746
|
-
}
|
|
1747
|
-
async updateContractTemplate(userId, templateId, data) {
|
|
1748
|
-
const actor = await this.getActorContext(userId);
|
|
1749
|
-
this.ensureDirector(actor);
|
|
1750
|
-
return this.prisma.$transaction(async (tx) => {
|
|
1751
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
1752
|
-
const current = await this.getContractTemplateRecord(tx, templateId, true);
|
|
1753
|
-
const nextName = data.name !== undefined
|
|
1754
|
-
? this.normalizeOptionalText(data.name)
|
|
1755
|
-
: current.name;
|
|
1756
|
-
if (!nextName) {
|
|
1757
|
-
throw new common_1.BadRequestException('Contract template name is required.');
|
|
1758
|
-
}
|
|
1759
|
-
if (String(nextName).toLowerCase() !== String(current.name).toLowerCase()) {
|
|
1760
|
-
await this.assertContractTemplateNameAvailable(tx, nextName, templateId);
|
|
1761
|
-
}
|
|
1762
|
-
const nextCode = data.code !== undefined
|
|
1763
|
-
? (_b = (_a = this.normalizeOptionalText(data.code)) === null || _a === void 0 ? void 0 : _a.toUpperCase()) !== null && _b !== void 0 ? _b : null
|
|
1764
|
-
: ((_c = current.code) !== null && _c !== void 0 ? _c : null);
|
|
1765
|
-
if (nextCode) {
|
|
1766
|
-
await this.assertContractTemplateCodeAvailable(tx, nextCode, templateId);
|
|
1767
|
-
}
|
|
1768
|
-
const nextStatus = (_e = (_d = data.status) !== null && _d !== void 0 ? _d : current.status) !== null && _e !== void 0 ? _e : 'active';
|
|
1769
|
-
const nextIsActive = (_f = data.isActive) !== null && _f !== void 0 ? _f : !['inactive', 'archived'].includes(nextStatus);
|
|
1770
|
-
const nextSlug = String(nextName).toLowerCase() !== String(current.name).toLowerCase()
|
|
1771
|
-
? await this.generateUniqueContractTemplateSlug(tx, nextName, templateId)
|
|
1772
|
-
: current.slug;
|
|
1773
|
-
await tx.$executeRawUnsafe(`UPDATE operations_contract_template
|
|
1774
|
-
SET slug = $1,
|
|
1775
|
-
code = $2,
|
|
1776
|
-
name = $3,
|
|
1777
|
-
description = $4,
|
|
1778
|
-
contract_category = $5,
|
|
1779
|
-
contract_type = $6,
|
|
1780
|
-
billing_model = $7,
|
|
1781
|
-
signature_status = $8,
|
|
1782
|
-
is_active = $9,
|
|
1783
|
-
status = $10,
|
|
1784
|
-
content_html = $11,
|
|
1785
|
-
updated_at = NOW()
|
|
1786
|
-
WHERE id = $12`, nextSlug, nextCode, nextName, data.description !== undefined
|
|
1787
|
-
? this.normalizeOptionalText(data.description)
|
|
1788
|
-
: ((_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
|
|
1789
|
-
? this.normalizeOptionalText(data.contentHtml)
|
|
1790
|
-
: ((_r = current.contentHtml) !== null && _r !== void 0 ? _r : null), templateId);
|
|
1791
|
-
return this.getContractTemplateRecord(tx, templateId, true);
|
|
1792
|
-
});
|
|
1793
|
-
}
|
|
1794
|
-
async listContracts(userId) {
|
|
1795
|
-
const actor = await this.getActorContext(userId);
|
|
1796
|
-
const params = [];
|
|
1797
|
-
const accessClause = actor.isDirector
|
|
1798
|
-
? 'c.deleted_at IS NULL'
|
|
1799
|
-
: `c.deleted_at IS NULL AND (
|
|
1800
|
-
c.related_collaborator_id = ANY(${this.param(params, actor.visibleCollaboratorIds)}::int[])
|
|
1801
|
-
OR EXISTS (
|
|
1802
|
-
SELECT 1
|
|
1803
|
-
FROM operations_project p_access
|
|
1804
|
-
WHERE p_access.contract_id = c.id
|
|
1805
|
-
AND p_access.deleted_at IS NULL
|
|
1806
|
-
AND p_access.id = ANY(${this.param(params, actor.visibleProjectIds)}::int[])
|
|
1807
|
-
)
|
|
1808
|
-
)`;
|
|
1809
|
-
return this.queryRows(`SELECT c.id,
|
|
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,
|
|
1810
2062
|
c.code,
|
|
1811
2063
|
c.name,
|
|
1812
2064
|
c.contract_category AS "contractCategory",
|
|
@@ -1817,10 +2069,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1817
2069
|
c.billing_model AS "billingModel",
|
|
1818
2070
|
c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
|
|
1819
2071
|
c.related_collaborator_id AS "relatedCollaboratorId",
|
|
1820
|
-
c.contract_template_id AS "contractTemplateId",
|
|
1821
|
-
template.name AS "contractTemplateName",
|
|
1822
|
-
template.slug AS "contractTemplateSlug",
|
|
1823
|
-
template.code AS "contractTemplateCode",
|
|
1824
2072
|
c.origin_type AS "originType",
|
|
1825
2073
|
c.origin_id AS "originId",
|
|
1826
2074
|
c.start_date AS "startDate",
|
|
@@ -1836,17 +2084,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1836
2084
|
m.display_name AS "accountManagerName",
|
|
1837
2085
|
linked.display_name AS "relatedCollaboratorName",
|
|
1838
2086
|
MAX(COALESCE(primary_party.display_name, linked.display_name, c.client_name)) AS "mainRelatedPartyName",
|
|
1839
|
-
MAX(COALESCE(financials.value_amount, 0)) AS "valueAmount",
|
|
1840
|
-
MAX(COALESCE(financials.payment_amount, 0)) AS "paymentAmount",
|
|
1841
|
-
MAX(COALESCE(financials.revenue_amount, 0)) AS "revenueAmount",
|
|
1842
|
-
MAX(COALESCE(financials.fine_amount, 0)) AS "fineAmount",
|
|
1843
2087
|
MAX(COALESCE(pdf_document.file_name, '')) AS "currentPdfFileName",
|
|
1844
2088
|
COUNT(DISTINCT p.id)::int AS "projectCount"
|
|
1845
2089
|
FROM operations_contract c
|
|
1846
2090
|
LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
|
|
1847
2091
|
LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
|
|
1848
|
-
LEFT JOIN operations_contract_template template
|
|
1849
|
-
ON template.id = c.contract_template_id
|
|
1850
2092
|
LEFT JOIN LATERAL (
|
|
1851
2093
|
SELECT cp.display_name
|
|
1852
2094
|
FROM operations_contract_party cp
|
|
@@ -1855,16 +2097,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1855
2097
|
ORDER BY cp.is_primary DESC, cp.id ASC
|
|
1856
2098
|
LIMIT 1
|
|
1857
2099
|
) primary_party ON TRUE
|
|
1858
|
-
LEFT JOIN LATERAL (
|
|
1859
|
-
SELECT
|
|
1860
|
-
SUM(CASE WHEN term_type = 'value' THEN amount ELSE 0 END) AS value_amount,
|
|
1861
|
-
SUM(CASE WHEN term_type = 'payment' THEN amount ELSE 0 END) AS payment_amount,
|
|
1862
|
-
SUM(CASE WHEN term_type = 'revenue' THEN amount ELSE 0 END) AS revenue_amount,
|
|
1863
|
-
SUM(CASE WHEN term_type = 'fine' THEN amount ELSE 0 END) AS fine_amount
|
|
1864
|
-
FROM operations_contract_financial_term ft
|
|
1865
|
-
WHERE ft.contract_id = c.id
|
|
1866
|
-
AND ft.deleted_at IS NULL
|
|
1867
|
-
) financials ON TRUE
|
|
1868
2100
|
LEFT JOIN LATERAL (
|
|
1869
2101
|
SELECT cd.file_name
|
|
1870
2102
|
FROM operations_contract_document cd
|
|
@@ -1878,9 +2110,71 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1878
2110
|
LEFT JOIN operations_project p
|
|
1879
2111
|
ON p.contract_id = c.id
|
|
1880
2112
|
AND p.deleted_at IS NULL
|
|
1881
|
-
WHERE ${
|
|
1882
|
-
GROUP BY c.id, m.id, linked.id
|
|
1883
|
-
|
|
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
|
+
};
|
|
1884
2178
|
}
|
|
1885
2179
|
async getContractById(userId, contractId) {
|
|
1886
2180
|
var _a, _b, _c;
|
|
@@ -1896,10 +2190,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1896
2190
|
c.billing_model AS "billingModel",
|
|
1897
2191
|
c.account_manager_collaborator_id AS "accountManagerCollaboratorId",
|
|
1898
2192
|
c.related_collaborator_id AS "relatedCollaboratorId",
|
|
1899
|
-
c.contract_template_id AS "contractTemplateId",
|
|
1900
|
-
template.name AS "contractTemplateName",
|
|
1901
|
-
template.slug AS "contractTemplateSlug",
|
|
1902
|
-
template.code AS "contractTemplateCode",
|
|
1903
2193
|
c.origin_type AS "originType",
|
|
1904
2194
|
c.origin_id AS "originId",
|
|
1905
2195
|
c.start_date AS "startDate",
|
|
@@ -1918,8 +2208,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1918
2208
|
FROM operations_contract c
|
|
1919
2209
|
LEFT JOIN operations_collaborator m ON m.id = c.account_manager_collaborator_id
|
|
1920
2210
|
LEFT JOIN operations_collaborator linked ON linked.id = c.related_collaborator_id
|
|
1921
|
-
LEFT JOIN operations_contract_template template
|
|
1922
|
-
ON template.id = c.contract_template_id
|
|
1923
2211
|
WHERE c.id = $1
|
|
1924
2212
|
AND c.deleted_at IS NULL`, [contractId]);
|
|
1925
2213
|
if (!contract) {
|
|
@@ -1946,7 +2234,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1946
2234
|
throw new common_1.ForbiddenException('You do not have access to this contract.');
|
|
1947
2235
|
}
|
|
1948
2236
|
}
|
|
1949
|
-
const [projects, scheduleSummary, parties,
|
|
2237
|
+
const [projects, scheduleSummary, parties, documents, history] = await Promise.all([
|
|
1950
2238
|
this.queryRows(`SELECT id, code, name, status
|
|
1951
2239
|
FROM operations_project
|
|
1952
2240
|
WHERE contract_id = $1
|
|
@@ -1975,27 +2263,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1975
2263
|
WHERE contract_id = $1
|
|
1976
2264
|
AND deleted_at IS NULL
|
|
1977
2265
|
ORDER BY is_primary DESC, id ASC`, [contractId]),
|
|
1978
|
-
this.queryRows(`SELECT id,
|
|
1979
|
-
signer_name AS "signerName",
|
|
1980
|
-
signer_role AS "signerRole",
|
|
1981
|
-
signer_email AS "signerEmail",
|
|
1982
|
-
signer_status AS status,
|
|
1983
|
-
signed_at AS "signedAt"
|
|
1984
|
-
FROM operations_contract_signature
|
|
1985
|
-
WHERE contract_id = $1
|
|
1986
|
-
AND deleted_at IS NULL
|
|
1987
|
-
ORDER BY id ASC`, [contractId]),
|
|
1988
|
-
this.queryRows(`SELECT id,
|
|
1989
|
-
term_type AS "termType",
|
|
1990
|
-
label,
|
|
1991
|
-
amount,
|
|
1992
|
-
recurrence,
|
|
1993
|
-
due_day AS "dueDay",
|
|
1994
|
-
notes
|
|
1995
|
-
FROM operations_contract_financial_term
|
|
1996
|
-
WHERE contract_id = $1
|
|
1997
|
-
AND deleted_at IS NULL
|
|
1998
|
-
ORDER BY id ASC`, [contractId]),
|
|
1999
2266
|
this.queryRows(`SELECT id,
|
|
2000
2267
|
document_type AS "documentType",
|
|
2001
2268
|
file_id AS "fileId",
|
|
@@ -2011,16 +2278,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2011
2278
|
WHERE contract_id = $1
|
|
2012
2279
|
AND deleted_at IS NULL
|
|
2013
2280
|
ORDER BY is_current DESC, id DESC`, [contractId]),
|
|
2014
|
-
this.queryRows(`SELECT id,
|
|
2015
|
-
revision_type AS "revisionType",
|
|
2016
|
-
title,
|
|
2017
|
-
effective_date AS "effectiveDate",
|
|
2018
|
-
status,
|
|
2019
|
-
summary
|
|
2020
|
-
FROM operations_contract_revision
|
|
2021
|
-
WHERE contract_id = $1
|
|
2022
|
-
AND deleted_at IS NULL
|
|
2023
|
-
ORDER BY effective_date DESC NULLS LAST, id DESC`, [contractId]),
|
|
2024
2281
|
this.queryRows(`SELECT id,
|
|
2025
2282
|
actor_user_id AS "actorUserId",
|
|
2026
2283
|
action,
|
|
@@ -2034,17 +2291,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2034
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,
|
|
2035
2292
|
scheduleSummary,
|
|
2036
2293
|
parties,
|
|
2037
|
-
signatures,
|
|
2038
|
-
financialTerms,
|
|
2039
2294
|
documents,
|
|
2040
|
-
revisions,
|
|
2041
2295
|
history });
|
|
2042
2296
|
}
|
|
2043
2297
|
async createContract(userId, data) {
|
|
2044
2298
|
const actor = await this.getActorContext(userId);
|
|
2045
2299
|
this.ensureDirector(actor);
|
|
2046
2300
|
const createdId = await this.prisma.$transaction(async (tx) => {
|
|
2047
|
-
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
|
|
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;
|
|
2048
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));
|
|
2049
2303
|
const created = await tx.$queryRawUnsafe(`INSERT INTO operations_contract (
|
|
2050
2304
|
code,
|
|
@@ -2057,7 +2311,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2057
2311
|
billing_model,
|
|
2058
2312
|
account_manager_collaborator_id,
|
|
2059
2313
|
related_collaborator_id,
|
|
2060
|
-
contract_template_id,
|
|
2061
2314
|
origin_type,
|
|
2062
2315
|
origin_id,
|
|
2063
2316
|
start_date,
|
|
@@ -2083,24 +2336,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2083
2336
|
$8::operations_contract_billing_model_409dc7fea2_enum,
|
|
2084
2337
|
$9,
|
|
2085
2338
|
$10,
|
|
2086
|
-
$11,
|
|
2087
|
-
$12
|
|
2088
|
-
$13,
|
|
2089
|
-
$14::date,
|
|
2090
|
-
$
|
|
2091
|
-
$20::
|
|
2092
|
-
$21
|
|
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,
|
|
2093
2346
|
$22,
|
|
2094
2347
|
$23,
|
|
2095
|
-
$24,
|
|
2096
2348
|
NOW(), NOW()
|
|
2097
2349
|
)
|
|
2098
|
-
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.
|
|
2099
|
-
const contractId = (
|
|
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;
|
|
2100
2352
|
await this.replaceContractParties(tx, contractId, data.parties);
|
|
2101
|
-
await this.replaceContractSignatures(tx, contractId, data.signatures);
|
|
2102
|
-
await this.replaceContractFinancialTerms(tx, contractId, data.financialTerms);
|
|
2103
|
-
await this.replaceContractRevisions(tx, contractId, data.revisions);
|
|
2104
2353
|
if (data.replaceUploadedPdfDocument) {
|
|
2105
2354
|
await this.replaceContractDocument(tx, contractId, 'source_upload', data.replaceUploadedPdfDocument);
|
|
2106
2355
|
}
|
|
@@ -2111,258 +2360,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2111
2360
|
});
|
|
2112
2361
|
return this.getContractById(userId, createdId);
|
|
2113
2362
|
}
|
|
2114
|
-
async createContractDraft(userId, data) {
|
|
2115
|
-
const actor = await this.getActorContext(userId);
|
|
2116
|
-
this.ensureDirector(actor);
|
|
2117
|
-
const creationMode = this.normalizeEnumValue(data.creationMode, CONTRACT_CREATION_MODE_VALUES, 'blank');
|
|
2118
|
-
const selectedTemplate = data.templateId && data.templateId > 0
|
|
2119
|
-
? await this.getContractTemplateById(userId, data.templateId)
|
|
2120
|
-
: null;
|
|
2121
|
-
const duplicateSource = data.duplicateFromId && data.duplicateFromId > 0
|
|
2122
|
-
? await this.getContractById(userId, data.duplicateFromId)
|
|
2123
|
-
: null;
|
|
2124
|
-
const storedSourceFile = data.sourceFileId && data.sourceFileId > 0
|
|
2125
|
-
? await this.prisma.file.findUnique({
|
|
2126
|
-
where: { id: data.sourceFileId },
|
|
2127
|
-
include: { file_mimetype: true },
|
|
2128
|
-
})
|
|
2129
|
-
: null;
|
|
2130
|
-
if (data.sourceFileId && !storedSourceFile) {
|
|
2131
|
-
throw new common_1.NotFoundException('Source contract file not found.');
|
|
2132
|
-
}
|
|
2133
|
-
const createdId = await this.prisma.$transaction(async (tx) => {
|
|
2134
|
-
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;
|
|
2135
|
-
const generatedCode = await this.generateContractCode(tx);
|
|
2136
|
-
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';
|
|
2137
|
-
const created = await tx.$queryRawUnsafe(`INSERT INTO operations_contract (
|
|
2138
|
-
code,
|
|
2139
|
-
name,
|
|
2140
|
-
contract_category,
|
|
2141
|
-
contract_type,
|
|
2142
|
-
client_name,
|
|
2143
|
-
signature_status,
|
|
2144
|
-
is_active,
|
|
2145
|
-
billing_model,
|
|
2146
|
-
account_manager_collaborator_id,
|
|
2147
|
-
related_collaborator_id,
|
|
2148
|
-
contract_template_id,
|
|
2149
|
-
origin_type,
|
|
2150
|
-
origin_id,
|
|
2151
|
-
start_date,
|
|
2152
|
-
end_date,
|
|
2153
|
-
signed_at,
|
|
2154
|
-
effective_date,
|
|
2155
|
-
budget_amount,
|
|
2156
|
-
monthly_hour_cap,
|
|
2157
|
-
status,
|
|
2158
|
-
creation_mode,
|
|
2159
|
-
wizard_step,
|
|
2160
|
-
description,
|
|
2161
|
-
content_html,
|
|
2162
|
-
created_by_user_id,
|
|
2163
|
-
updated_by_user_id,
|
|
2164
|
-
created_at,
|
|
2165
|
-
updated_at
|
|
2166
|
-
) VALUES (
|
|
2167
|
-
$1,
|
|
2168
|
-
$2,
|
|
2169
|
-
$3::operations_contract_contract_category_70d553ea09_enum,
|
|
2170
|
-
$4::operations_contract_contract_type_48331e2ebf_enum,
|
|
2171
|
-
$5,
|
|
2172
|
-
$6::operations_contract_signature_status_2cb7282a7b_enum,
|
|
2173
|
-
$7,
|
|
2174
|
-
$8::operations_contract_billing_model_409dc7fea2_enum,
|
|
2175
|
-
$9,
|
|
2176
|
-
$10,
|
|
2177
|
-
$11,
|
|
2178
|
-
$12::operations_contract_origin_type_07a7cc2b5d_enum,
|
|
2179
|
-
$13,
|
|
2180
|
-
$14::date,
|
|
2181
|
-
$15::date,
|
|
2182
|
-
$16::date,
|
|
2183
|
-
$17::date,
|
|
2184
|
-
$18,
|
|
2185
|
-
$19,
|
|
2186
|
-
$20::operations_contract_status_a0395962df_enum,
|
|
2187
|
-
$21::operations_contract_creation_mode_98ba669209_enum,
|
|
2188
|
-
$22,
|
|
2189
|
-
$23,
|
|
2190
|
-
$24,
|
|
2191
|
-
$25,
|
|
2192
|
-
$25,
|
|
2193
|
-
NOW(),
|
|
2194
|
-
NOW()
|
|
2195
|
-
)
|
|
2196
|
-
RETURNING id`, generatedCode, creationMode === 'duplicate'
|
|
2197
|
-
? `${duplicateNameBase} Copy`
|
|
2198
|
-
: 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);
|
|
2199
|
-
const contractId = (_3 = created[0]) === null || _3 === void 0 ? void 0 : _3.id;
|
|
2200
|
-
if (duplicateSource) {
|
|
2201
|
-
await this.replaceContractParties(tx, contractId, duplicateSource.parties);
|
|
2202
|
-
await this.replaceContractSignatures(tx, contractId, duplicateSource.signatures);
|
|
2203
|
-
await this.replaceContractFinancialTerms(tx, contractId, duplicateSource.financialTerms);
|
|
2204
|
-
await this.replaceContractRevisions(tx, contractId, duplicateSource.revisions);
|
|
2205
|
-
const currentSourceDocument = (_4 = duplicateSource.documents.find((document) => document.isCurrent && document.documentType === 'source_upload')) !== null && _4 !== void 0 ? _4 : null;
|
|
2206
|
-
if (currentSourceDocument) {
|
|
2207
|
-
await this.replaceContractDocument(tx, contractId, 'source_upload', {
|
|
2208
|
-
fileId: (_5 = currentSourceDocument.fileId) !== null && _5 !== void 0 ? _5 : null,
|
|
2209
|
-
fileName: currentSourceDocument.fileName,
|
|
2210
|
-
mimeType: currentSourceDocument.mimeType,
|
|
2211
|
-
fileContentBase64: (_6 = currentSourceDocument.fileContentBase64) !== null && _6 !== void 0 ? _6 : null,
|
|
2212
|
-
notes: (_7 = currentSourceDocument.notes) !== null && _7 !== void 0 ? _7 : null,
|
|
2213
|
-
extractionStatus: (_8 = currentSourceDocument.extractionStatus) !== null && _8 !== void 0 ? _8 : 'skipped',
|
|
2214
|
-
extractionSummary: (_9 = currentSourceDocument.extractionSummary) !== null && _9 !== void 0 ? _9 : null,
|
|
2215
|
-
});
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
if (storedSourceFile) {
|
|
2219
|
-
await this.replaceContractDocument(tx, contractId, 'source_upload', {
|
|
2220
|
-
fileId: storedSourceFile.id,
|
|
2221
|
-
fileName: (_10 = this.normalizeOptionalText(data.sourceFileName)) !== null && _10 !== void 0 ? _10 : storedSourceFile.filename,
|
|
2222
|
-
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',
|
|
2223
|
-
notes: 'Source contract document uploaded during draft creation.',
|
|
2224
|
-
extractionStatus: 'pending',
|
|
2225
|
-
extractionSummary: null,
|
|
2226
|
-
});
|
|
2227
|
-
}
|
|
2228
|
-
await this.insertContractHistory(tx, contractId, userId, 'draft_created', `Contract draft created with mode ${creationMode}.`, JSON.stringify({
|
|
2229
|
-
creationMode,
|
|
2230
|
-
templateId: (_14 = selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.id) !== null && _14 !== void 0 ? _14 : null,
|
|
2231
|
-
duplicateFromId: (_15 = duplicateSource === null || duplicateSource === void 0 ? void 0 : duplicateSource.id) !== null && _15 !== void 0 ? _15 : null,
|
|
2232
|
-
sourceFileId: (_16 = storedSourceFile === null || storedSourceFile === void 0 ? void 0 : storedSourceFile.id) !== null && _16 !== void 0 ? _16 : null,
|
|
2233
|
-
}));
|
|
2234
|
-
return contractId;
|
|
2235
|
-
});
|
|
2236
|
-
return this.getContractById(userId, createdId);
|
|
2237
|
-
}
|
|
2238
|
-
async extractContractSource(userId, contractId, data) {
|
|
2239
|
-
var _a, _b;
|
|
2240
|
-
const actor = await this.getActorContext(userId);
|
|
2241
|
-
this.ensureDirector(actor);
|
|
2242
|
-
const contract = await this.getContractById(userId, contractId);
|
|
2243
|
-
const sourceDocument = contract.documents.find((document) => document.isCurrent && document.documentType === 'source_upload');
|
|
2244
|
-
if (!sourceDocument) {
|
|
2245
|
-
throw new common_1.BadRequestException('No source document is attached to this contract draft.');
|
|
2246
|
-
}
|
|
2247
|
-
await this.prisma.$executeRawUnsafe(`UPDATE operations_contract_document
|
|
2248
|
-
SET extraction_status = 'processing',
|
|
2249
|
-
updated_at = NOW()
|
|
2250
|
-
WHERE id = $1`, sourceDocument.id);
|
|
2251
|
-
try {
|
|
2252
|
-
const extracted = await this.extractContractDraft(userId, {
|
|
2253
|
-
contractId,
|
|
2254
|
-
provider: (_a = data.provider) !== null && _a !== void 0 ? _a : null,
|
|
2255
|
-
promptMessage: (_b = data.promptMessage) !== null && _b !== void 0 ? _b : null,
|
|
2256
|
-
});
|
|
2257
|
-
await this.prisma.$executeRawUnsafe(`UPDATE operations_contract_document
|
|
2258
|
-
SET extraction_status = 'completed',
|
|
2259
|
-
extraction_summary = $2,
|
|
2260
|
-
updated_at = NOW()
|
|
2261
|
-
WHERE id = $1`, sourceDocument.id, extracted.summary || extracted.description || extracted.name || null);
|
|
2262
|
-
await this.insertContractHistory(this.prisma, contractId, userId, 'source_extracted', 'Source contract document extracted with AI.');
|
|
2263
|
-
return extracted;
|
|
2264
|
-
}
|
|
2265
|
-
catch (error) {
|
|
2266
|
-
await this.prisma.$executeRawUnsafe(`UPDATE operations_contract_document
|
|
2267
|
-
SET extraction_status = 'failed',
|
|
2268
|
-
updated_at = NOW()
|
|
2269
|
-
WHERE id = $1`, sourceDocument.id);
|
|
2270
|
-
throw error;
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
async generateContractContent(userId, contractId, data = {}) {
|
|
2274
|
-
const actor = await this.getActorContext(userId);
|
|
2275
|
-
this.ensureDirector(actor);
|
|
2276
|
-
const contract = await this.getContractById(userId, contractId);
|
|
2277
|
-
const contentHtml = await this.generateContractContentHtml(contract, data);
|
|
2278
|
-
await this.prisma.$transaction(async (tx) => {
|
|
2279
|
-
var _a;
|
|
2280
|
-
await tx.$executeRawUnsafe(`UPDATE operations_contract
|
|
2281
|
-
SET content_html = $2,
|
|
2282
|
-
wizard_step = CASE
|
|
2283
|
-
WHEN COALESCE(wizard_step, 0) < 4 THEN 4
|
|
2284
|
-
ELSE wizard_step
|
|
2285
|
-
END,
|
|
2286
|
-
updated_by_user_id = $3,
|
|
2287
|
-
updated_at = NOW()
|
|
2288
|
-
WHERE id = $1`, contractId, contentHtml, userId);
|
|
2289
|
-
await this.insertContractHistory(tx, contractId, userId, 'content_generated', 'Contract content generated automatically for editing.', JSON.stringify({
|
|
2290
|
-
provider: (_a = data.provider) !== null && _a !== void 0 ? _a : null,
|
|
2291
|
-
overwrite: Boolean(data.overwrite),
|
|
2292
|
-
hasPrompt: Boolean(this.normalizeOptionalText(data.promptMessage)),
|
|
2293
|
-
}));
|
|
2294
|
-
});
|
|
2295
|
-
return this.getContractById(userId, contractId);
|
|
2296
|
-
}
|
|
2297
|
-
async reviewContractLegally(userId, contractId, data = {}) {
|
|
2298
|
-
const actor = await this.getActorContext(userId);
|
|
2299
|
-
this.ensureDirector(actor);
|
|
2300
|
-
const contract = await this.getContractById(userId, contractId);
|
|
2301
|
-
const review = await this.buildContractLegalReview(contract, data);
|
|
2302
|
-
await this.insertContractHistory(this.prisma, contractId, userId, 'legal_reviewed', 'Advisory legal checklist updated for this contract.', JSON.stringify(review));
|
|
2303
|
-
return review;
|
|
2304
|
-
}
|
|
2305
|
-
async generateContractPdf(userId, contractId) {
|
|
2306
|
-
const actor = await this.getActorContext(userId);
|
|
2307
|
-
this.ensureDirector(actor);
|
|
2308
|
-
const contract = await this.getContractById(userId, contractId);
|
|
2309
|
-
const pdfBuffer = await this.renderContractPdfBuffer(contract);
|
|
2310
|
-
const fileName = this.buildContractPdfFileName(contract);
|
|
2311
|
-
const uploadedFile = await this.fileService.upload('operations/contracts/generated', {
|
|
2312
|
-
fieldname: 'file',
|
|
2313
|
-
originalname: fileName,
|
|
2314
|
-
encoding: '7bit',
|
|
2315
|
-
mimetype: 'application/pdf',
|
|
2316
|
-
size: pdfBuffer.length,
|
|
2317
|
-
destination: '',
|
|
2318
|
-
filename: fileName,
|
|
2319
|
-
path: '',
|
|
2320
|
-
buffer: pdfBuffer,
|
|
2321
|
-
});
|
|
2322
|
-
await this.prisma.$transaction(async (tx) => {
|
|
2323
|
-
await this.replaceContractDocument(tx, contractId, 'generated_pdf', {
|
|
2324
|
-
fileId: uploadedFile.id,
|
|
2325
|
-
fileName,
|
|
2326
|
-
mimeType: 'application/pdf',
|
|
2327
|
-
notes: 'PDF generated from contract rich text content.',
|
|
2328
|
-
extractionStatus: 'skipped',
|
|
2329
|
-
extractionSummary: null,
|
|
2330
|
-
});
|
|
2331
|
-
await this.insertContractHistory(tx, contractId, userId, 'pdf_generated', 'Generated a branded PDF version of the contract.');
|
|
2332
|
-
});
|
|
2333
|
-
return {
|
|
2334
|
-
contractId,
|
|
2335
|
-
fileId: uploadedFile.id,
|
|
2336
|
-
fileName,
|
|
2337
|
-
mimeType: 'application/pdf',
|
|
2338
|
-
documentType: 'generated_pdf',
|
|
2339
|
-
downloadUrl: `/file/open/${uploadedFile.id}`,
|
|
2340
|
-
};
|
|
2341
|
-
}
|
|
2342
|
-
async extractContractDraft(userId, data) {
|
|
2343
|
-
var _a;
|
|
2344
|
-
const actor = await this.getActorContext(userId);
|
|
2345
|
-
this.ensureDirector(actor);
|
|
2346
|
-
const uploadFile = await this.resolveContractExtractionFile(userId, data);
|
|
2347
|
-
const aiResult = await this.aiService.chat({
|
|
2348
|
-
provider: data.provider === 'gemini' ? 'gemini' : 'openai',
|
|
2349
|
-
model: data.provider === 'gemini' ? 'gemini-1.5-flash' : 'gpt-4o-mini',
|
|
2350
|
-
message: this.normalizeExtractionString(data.promptMessage) ||
|
|
2351
|
-
'Analyze the attached contract and extract a structured draft for the contract form.',
|
|
2352
|
-
systemPrompt: this.buildContractExtractionSystemPrompt(),
|
|
2353
|
-
}, uploadFile);
|
|
2354
|
-
const parsed = this.parseAiJsonPayload(String((_a = aiResult === null || aiResult === void 0 ? void 0 : aiResult.content) !== null && _a !== void 0 ? _a : ''));
|
|
2355
|
-
const draft = this.normalizeContractExtractDraft(parsed);
|
|
2356
|
-
const warnings = [...draft.warnings];
|
|
2357
|
-
if (uploadFile.mimetype === 'application/msword' ||
|
|
2358
|
-
uploadFile.mimetype ===
|
|
2359
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
|
2360
|
-
warnings.push('Word extraction is best-effort. Review names, dates, and values before saving.');
|
|
2361
|
-
}
|
|
2362
|
-
return Object.assign(Object.assign({}, draft), { warnings: Array.from(new Set(warnings.filter(Boolean))) });
|
|
2363
|
-
}
|
|
2364
2363
|
async createContractFromProposalIntegration(payload) {
|
|
2365
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y
|
|
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;
|
|
2366
2365
|
const sourceEntityId = String(payload.proposalId || '').trim();
|
|
2367
2366
|
if (!sourceEntityId) {
|
|
2368
2367
|
throw new common_1.BadRequestException('proposalId is required for CRM proposal integration.');
|
|
@@ -2386,30 +2385,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2386
2385
|
const billingModel = (_u = proposal.billingModel) !== null && _u !== void 0 ? _u : 'fixed_price';
|
|
2387
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}`;
|
|
2388
2387
|
const contractName = (_y = this.normalizeOptionalText(proposal.title)) !== null && _y !== void 0 ? _y : `Proposal ${sourceEntityId}`;
|
|
2389
|
-
const financialTerms = items
|
|
2390
|
-
.map((item) => {
|
|
2391
|
-
var _a, _b, _c;
|
|
2392
|
-
return ({
|
|
2393
|
-
termType: this.normalizeEnumValue(item.termType, FINANCIAL_TERM_TYPE_VALUES, 'value'),
|
|
2394
|
-
label: (_a = this.normalizeOptionalText(item.name)) !== null && _a !== void 0 ? _a : 'Commercial term',
|
|
2395
|
-
amount: Number((_b = item.amount) !== null && _b !== void 0 ? _b : Number(item.totalAmountCents || 0) / 100),
|
|
2396
|
-
recurrence: this.normalizeEnumValue(item.recurrence, RECURRENCE_VALUES, 'one_time'),
|
|
2397
|
-
dueDay: (_c = item.dueDay) !== null && _c !== void 0 ? _c : null,
|
|
2398
|
-
notes: this.normalizeOptionalText(item.description),
|
|
2399
|
-
});
|
|
2400
|
-
})
|
|
2401
|
-
.filter((term) => Number.isFinite(term.amount) && term.amount > 0);
|
|
2402
|
-
const fallbackAmount = Number((_z = proposal.totalAmount) !== null && _z !== void 0 ? _z : 0);
|
|
2403
|
-
if (financialTerms.length === 0 && Number.isFinite(fallbackAmount) && fallbackAmount > 0) {
|
|
2404
|
-
financialTerms.push({
|
|
2405
|
-
termType: 'revenue',
|
|
2406
|
-
label: contractName,
|
|
2407
|
-
amount: fallbackAmount,
|
|
2408
|
-
recurrence: 'one_time',
|
|
2409
|
-
dueDay: null,
|
|
2410
|
-
notes: this.normalizeOptionalText(proposal.notes),
|
|
2411
|
-
});
|
|
2412
|
-
}
|
|
2413
2388
|
const primaryPartyRole = ['employee', 'contractor'].includes(contractCategory)
|
|
2414
2389
|
? 'employee'
|
|
2415
2390
|
: ['supplier', 'vendor'].includes(contractCategory)
|
|
@@ -2418,7 +2393,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2418
2393
|
? 'partner'
|
|
2419
2394
|
: 'client';
|
|
2420
2395
|
return this.prisma.$transaction(async (tx) => {
|
|
2421
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o
|
|
2396
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
2422
2397
|
await tx.$queryRawUnsafe(`SELECT pg_advisory_xact_lock(hashtext($1))`, `operations:crm_proposal:${sourceEntityId}`);
|
|
2423
2398
|
const existingContracts = await tx.$queryRawUnsafe(`SELECT id, code
|
|
2424
2399
|
FROM operations_contract
|
|
@@ -2446,7 +2421,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2446
2421
|
billing_model,
|
|
2447
2422
|
account_manager_collaborator_id,
|
|
2448
2423
|
related_collaborator_id,
|
|
2449
|
-
contract_template_id,
|
|
2450
2424
|
origin_type,
|
|
2451
2425
|
origin_id,
|
|
2452
2426
|
start_date,
|
|
@@ -2494,8 +2468,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2494
2468
|
NOW(),
|
|
2495
2469
|
NOW()
|
|
2496
2470
|
)
|
|
2497
|
-
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,
|
|
2498
|
-
const contractId = (
|
|
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;
|
|
2499
2473
|
if (!contractId) {
|
|
2500
2474
|
throw new common_1.BadRequestException('Could not create contract draft from proposal.');
|
|
2501
2475
|
}
|
|
@@ -2510,27 +2484,10 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2510
2484
|
isPrimary: true,
|
|
2511
2485
|
},
|
|
2512
2486
|
]);
|
|
2513
|
-
await this.
|
|
2514
|
-
termType: term.termType,
|
|
2515
|
-
label: term.label,
|
|
2516
|
-
amount: term.amount,
|
|
2517
|
-
recurrence: term.recurrence,
|
|
2518
|
-
dueDay: term.dueDay,
|
|
2519
|
-
notes: term.notes,
|
|
2520
|
-
})));
|
|
2521
|
-
await this.replaceContractRevisions(tx, contractId, [
|
|
2522
|
-
{
|
|
2523
|
-
revisionType: 'revision',
|
|
2524
|
-
title: (_h = (_g = payload.revision) === null || _g === void 0 ? void 0 : _g.title) !== null && _h !== void 0 ? _h : contractName,
|
|
2525
|
-
effectiveDate: (_j = proposal.validFrom) !== null && _j !== void 0 ? _j : null,
|
|
2526
|
-
status: 'draft',
|
|
2527
|
-
summary: (_l = this.normalizeOptionalText((_k = payload.revision) === null || _k === void 0 ? void 0 : _k.summary)) !== null && _l !== void 0 ? _l : this.normalizeOptionalText(proposal.notes),
|
|
2528
|
-
},
|
|
2529
|
-
]);
|
|
2530
|
-
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({
|
|
2531
2488
|
correlationId: payload.correlationId || `proposal:${sourceEntityId}`,
|
|
2532
|
-
proposalId: (
|
|
2533
|
-
proposalRevisionId: (
|
|
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,
|
|
2534
2491
|
sourceModule: 'contact',
|
|
2535
2492
|
sourceEntity: 'proposal',
|
|
2536
2493
|
sourceId: sourceEntityId,
|
|
@@ -2551,10 +2508,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2551
2508
|
billingModel,
|
|
2552
2509
|
originType: 'crm_proposal',
|
|
2553
2510
|
originId: sourceEntityId,
|
|
2554
|
-
startDate: (
|
|
2555
|
-
endDate: (
|
|
2511
|
+
startDate: (_m = proposal.validFrom) !== null && _m !== void 0 ? _m : null,
|
|
2512
|
+
endDate: (_o = proposal.validUntil) !== null && _o !== void 0 ? _o : null,
|
|
2556
2513
|
description: this.normalizeOptionalText(proposal.notes),
|
|
2557
|
-
financialTerms,
|
|
2558
2514
|
}, payload, String(payload.locale || '').trim() || 'en', Number(payload.approvedByUserId) || undefined),
|
|
2559
2515
|
metadata: {
|
|
2560
2516
|
producer: 'operations',
|
|
@@ -2575,7 +2531,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2575
2531
|
});
|
|
2576
2532
|
}
|
|
2577
2533
|
buildContractCreatedEventPayload(contract, payload, locale = 'en', createdByUserId) {
|
|
2578
|
-
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
|
|
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;
|
|
2579
2535
|
return {
|
|
2580
2536
|
contractId: contract.id,
|
|
2581
2537
|
proposalId: (_a = payload.proposalId) !== null && _a !== void 0 ? _a : null,
|
|
@@ -2605,19 +2561,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2605
2561
|
startDate: (_r = contract.startDate) !== null && _r !== void 0 ? _r : null,
|
|
2606
2562
|
endDate: (_s = contract.endDate) !== null && _s !== void 0 ? _s : null,
|
|
2607
2563
|
description: (_t = contract.description) !== null && _t !== void 0 ? _t : null,
|
|
2608
|
-
financialTerms: (_u = contract.financialTerms) !== null && _u !== void 0 ? _u : [],
|
|
2609
2564
|
},
|
|
2610
|
-
proposal: (
|
|
2611
|
-
code: (
|
|
2612
|
-
title: (
|
|
2613
|
-
totalAmount: (
|
|
2614
|
-
notes: (
|
|
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,
|
|
2615
2570
|
},
|
|
2616
|
-
person: (
|
|
2571
|
+
person: (_0 = payload.person) !== null && _0 !== void 0 ? _0 : null,
|
|
2617
2572
|
};
|
|
2618
2573
|
}
|
|
2619
2574
|
async buildContractActivatedEventPayload(contract, locale = 'en', activatedByUserId) {
|
|
2620
|
-
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
|
|
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;
|
|
2621
2576
|
let personId = null;
|
|
2622
2577
|
let proposalId = null;
|
|
2623
2578
|
let personRecord = null;
|
|
@@ -2678,18 +2633,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2678
2633
|
}
|
|
2679
2634
|
}
|
|
2680
2635
|
}
|
|
2681
|
-
const
|
|
2682
|
-
var _a, _b, _c, _d, _e;
|
|
2683
|
-
return ({
|
|
2684
|
-
label: term.label,
|
|
2685
|
-
termType: (_a = term.termType) !== null && _a !== void 0 ? _a : 'value',
|
|
2686
|
-
amount: Number((_b = term.amount) !== null && _b !== void 0 ? _b : 0),
|
|
2687
|
-
recurrence: (_c = term.recurrence) !== null && _c !== void 0 ? _c : 'one_time',
|
|
2688
|
-
dueDay: (_d = term.dueDay) !== null && _d !== void 0 ? _d : null,
|
|
2689
|
-
notes: (_e = term.notes) !== null && _e !== void 0 ? _e : null,
|
|
2690
|
-
});
|
|
2691
|
-
});
|
|
2692
|
-
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);
|
|
2693
2637
|
return {
|
|
2694
2638
|
contractId: contract.id,
|
|
2695
2639
|
proposalId,
|
|
@@ -2697,7 +2641,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2697
2641
|
activatedByUserId: activatedByUserId !== null && activatedByUserId !== void 0 ? activatedByUserId : null,
|
|
2698
2642
|
signedByUserId: activatedByUserId !== null && activatedByUserId !== void 0 ? activatedByUserId : null,
|
|
2699
2643
|
activatedAt: new Date().toISOString(),
|
|
2700
|
-
signedAt: (
|
|
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,
|
|
2701
2645
|
locale,
|
|
2702
2646
|
correlationId: proposalId ? `proposal:${proposalId}` : `contract:${contract.id}`,
|
|
2703
2647
|
sourceModule: 'operations',
|
|
@@ -2707,38 +2651,37 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2707
2651
|
source_entity: 'contract',
|
|
2708
2652
|
source_id: String(contract.id),
|
|
2709
2653
|
contract: {
|
|
2710
|
-
code: (
|
|
2711
|
-
name: (
|
|
2712
|
-
clientName: (
|
|
2713
|
-
contractCategory: (
|
|
2714
|
-
contractType: (
|
|
2715
|
-
billingModel: (
|
|
2716
|
-
originType: (
|
|
2717
|
-
originId: (
|
|
2718
|
-
startDate: (
|
|
2719
|
-
endDate: (
|
|
2720
|
-
signedAt: (
|
|
2721
|
-
effectiveDate: (
|
|
2722
|
-
budgetAmount: (
|
|
2723
|
-
description: (
|
|
2724
|
-
|
|
2725
|
-
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 : [],
|
|
2726
2669
|
},
|
|
2727
2670
|
person: personRecord
|
|
2728
2671
|
? {
|
|
2729
|
-
id: (
|
|
2730
|
-
name: (
|
|
2731
|
-
tradeName: (
|
|
2732
|
-
email: (
|
|
2733
|
-
phone: (
|
|
2734
|
-
document: (
|
|
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,
|
|
2735
2678
|
}
|
|
2736
2679
|
: null,
|
|
2737
2680
|
receivable: {
|
|
2738
2681
|
personId,
|
|
2739
|
-
documentNumber: (
|
|
2682
|
+
documentNumber: (_7 = contract.code) !== null && _7 !== void 0 ? _7 : `CONTRACT-${contract.id}`,
|
|
2740
2683
|
totalAmount,
|
|
2741
|
-
description: (
|
|
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,
|
|
2742
2685
|
},
|
|
2743
2686
|
};
|
|
2744
2687
|
}
|
|
@@ -2763,7 +2706,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2763
2706
|
this.pushUpdate(updates, params, 'billing_model', data.billingModel, 'operations_contract_billing_model_409dc7fea2_enum');
|
|
2764
2707
|
this.pushUpdate(updates, params, 'account_manager_collaborator_id', data.accountManagerCollaboratorId);
|
|
2765
2708
|
this.pushUpdate(updates, params, 'related_collaborator_id', data.relatedCollaboratorId);
|
|
2766
|
-
this.pushUpdate(updates, params, 'contract_template_id', data.contractTemplateId);
|
|
2767
2709
|
this.pushUpdate(updates, params, 'origin_type', data.originType, 'operations_contract_origin_type_07a7cc2b5d_enum');
|
|
2768
2710
|
this.pushUpdate(updates, params, 'origin_id', data.originId);
|
|
2769
2711
|
this.pushUpdate(updates, params, 'start_date', data.startDate, 'date');
|
|
@@ -2778,7 +2720,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2778
2720
|
this.pushUpdate(updates, params, 'description', data.description);
|
|
2779
2721
|
this.pushUpdate(updates, params, 'content_html', data.contentHtml);
|
|
2780
2722
|
await this.prisma.$transaction(async (tx) => {
|
|
2781
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x
|
|
2723
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
2782
2724
|
if (updates.length) {
|
|
2783
2725
|
params.push(contractId);
|
|
2784
2726
|
await tx.$executeRawUnsafe(`UPDATE operations_contract
|
|
@@ -2789,15 +2731,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2789
2731
|
if (data.parties) {
|
|
2790
2732
|
await this.replaceContractParties(tx, contractId, data.parties);
|
|
2791
2733
|
}
|
|
2792
|
-
if (data.signatures) {
|
|
2793
|
-
await this.replaceContractSignatures(tx, contractId, data.signatures);
|
|
2794
|
-
}
|
|
2795
|
-
if (data.financialTerms) {
|
|
2796
|
-
await this.replaceContractFinancialTerms(tx, contractId, data.financialTerms);
|
|
2797
|
-
}
|
|
2798
|
-
if (data.revisions) {
|
|
2799
|
-
await this.replaceContractRevisions(tx, contractId, data.revisions);
|
|
2800
|
-
}
|
|
2801
2734
|
if (data.replaceUploadedPdfDocument) {
|
|
2802
2735
|
await this.replaceContractDocument(tx, contractId, 'source_upload', data.replaceUploadedPdfDocument);
|
|
2803
2736
|
}
|
|
@@ -2819,8 +2752,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2819
2752
|
effectiveDate: (_m = data.effectiveDate) !== null && _m !== void 0 ? _m : current.effectiveDate,
|
|
2820
2753
|
budgetAmount: (_o = data.budgetAmount) !== null && _o !== void 0 ? _o : current.budgetAmount,
|
|
2821
2754
|
description: (_p = data.description) !== null && _p !== void 0 ? _p : current.description,
|
|
2822
|
-
|
|
2823
|
-
parties: (_r = data.parties) !== null && _r !== void 0 ? _r : current.parties,
|
|
2755
|
+
parties: (_q = data.parties) !== null && _q !== void 0 ? _q : current.parties,
|
|
2824
2756
|
}, 'en', userId);
|
|
2825
2757
|
if (shouldPublishSigned) {
|
|
2826
2758
|
await this.integrationApi.publishEvent({
|
|
@@ -2828,15 +2760,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2828
2760
|
sourceModule: 'operations',
|
|
2829
2761
|
aggregateType: 'contract',
|
|
2830
2762
|
aggregateId: String(contractId),
|
|
2831
|
-
payload: Object.assign(Object.assign({}, contractEventPayload), { signedByUserId: userId !== null && userId !== void 0 ? userId : null, signedAt: (
|
|
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() }),
|
|
2832
2764
|
metadata: {
|
|
2833
2765
|
producer: 'operations',
|
|
2834
2766
|
correlationId: contractEventPayload.correlationId || `contract:${contractId}`,
|
|
2835
2767
|
sourceModule: 'operations',
|
|
2836
2768
|
sourceEntity: 'contract',
|
|
2837
2769
|
sourceId: String(contractId),
|
|
2838
|
-
originType: (
|
|
2839
|
-
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,
|
|
2840
2772
|
lifecycle: 'signed',
|
|
2841
2773
|
},
|
|
2842
2774
|
}, {
|
|
@@ -2856,8 +2788,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2856
2788
|
sourceModule: 'operations',
|
|
2857
2789
|
sourceEntity: 'contract',
|
|
2858
2790
|
sourceId: String(contractId),
|
|
2859
|
-
originType: (
|
|
2860
|
-
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,
|
|
2861
2793
|
lifecycle: 'activated',
|
|
2862
2794
|
},
|
|
2863
2795
|
}, {
|
|
@@ -2881,10 +2813,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2881
2813
|
AND deleted_at IS NULL`, contractId);
|
|
2882
2814
|
for (const tableName of [
|
|
2883
2815
|
'operations_contract_party',
|
|
2884
|
-
'operations_contract_signature',
|
|
2885
|
-
'operations_contract_financial_term',
|
|
2886
2816
|
'operations_contract_document',
|
|
2887
|
-
'operations_contract_revision',
|
|
2888
2817
|
]) {
|
|
2889
2818
|
await tx.$executeRawUnsafe(`UPDATE ${tableName}
|
|
2890
2819
|
SET deleted_at = NOW(),
|
|
@@ -2903,9 +2832,43 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2903
2832
|
});
|
|
2904
2833
|
return { success: true };
|
|
2905
2834
|
}
|
|
2906
|
-
async listTimesheets(userId) {
|
|
2835
|
+
async listTimesheets(userId, filters = {}) {
|
|
2836
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2907
2837
|
const actor = await this.getActorContext(userId);
|
|
2908
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
|
+
: '';
|
|
2909
2872
|
const headers = await this.queryRows(`SELECT t.id,
|
|
2910
2873
|
t.collaborator_id AS "collaboratorId",
|
|
2911
2874
|
c.display_name AS "collaboratorName",
|
|
@@ -2926,11 +2889,26 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2926
2889
|
ON approval.target_type = 'timesheet'
|
|
2927
2890
|
AND approval.target_id = t.id
|
|
2928
2891
|
AND approval.deleted_at IS NULL
|
|
2929
|
-
WHERE
|
|
2930
|
-
ORDER BY
|
|
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);
|
|
2931
2895
|
if (!headers.length) {
|
|
2896
|
+
if (pagination) {
|
|
2897
|
+
return this.buildPaginationResult([], 0, pagination.page, pagination.pageSize);
|
|
2898
|
+
}
|
|
2932
2899
|
return headers;
|
|
2933
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;
|
|
2934
2912
|
const entries = await this.queryRows(`SELECT e.id,
|
|
2935
2913
|
e.timesheet_id AS "timesheetId",
|
|
2936
2914
|
e.project_assignment_id AS "projectAssignmentId",
|
|
@@ -2957,10 +2935,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2957
2935
|
AND e.timesheet_id = ANY($1::int[])
|
|
2958
2936
|
ORDER BY e.work_date ASC, e.id ASC`, [headers.map((item) => item.id)]);
|
|
2959
2937
|
const grouped = this.groupBy(entries, 'timesheetId');
|
|
2960
|
-
|
|
2938
|
+
const data = headers.map((timesheet) => {
|
|
2961
2939
|
var _a;
|
|
2962
2940
|
return (Object.assign(Object.assign({}, timesheet), { entries: (_a = grouped[timesheet.id]) !== null && _a !== void 0 ? _a : [] }));
|
|
2963
2941
|
});
|
|
2942
|
+
if (!pagination) {
|
|
2943
|
+
return data;
|
|
2944
|
+
}
|
|
2945
|
+
return this.buildPaginationResult(data, total, pagination.page, pagination.pageSize);
|
|
2964
2946
|
}
|
|
2965
2947
|
async createTimesheet(userId, data) {
|
|
2966
2948
|
const actor = await this.getActorContext(userId);
|
|
@@ -3026,7 +3008,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3026
3008
|
return this.listSingleTimesheet(actor, timesheetId);
|
|
3027
3009
|
}
|
|
3028
3010
|
async submitTimesheet(userId, timesheetId) {
|
|
3029
|
-
var _a, _b;
|
|
3011
|
+
var _a, _b, _c;
|
|
3030
3012
|
const actor = await this.getActorContext(userId);
|
|
3031
3013
|
const current = await this.getTimesheetById(timesheetId);
|
|
3032
3014
|
if (!actor.isDirector && current.collaboratorId !== actor.collaboratorId) {
|
|
@@ -3036,7 +3018,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3036
3018
|
throw new common_1.BadRequestException('Only draft or rejected timesheets can be submitted.');
|
|
3037
3019
|
}
|
|
3038
3020
|
const collaborator = await this.getCollaboratorById(current.collaboratorId);
|
|
3039
|
-
const
|
|
3021
|
+
const projectManagerRow = await this.querySingle(`SELECT p.manager_collaborator_id AS "managerCollaboratorId"
|
|
3022
|
+
FROM operations_timesheet_entry e
|
|
3023
|
+
LEFT JOIN operations_project_assignment pa ON pa.id = e.project_assignment_id
|
|
3024
|
+
LEFT JOIN operations_project p ON p.id = pa.project_id
|
|
3025
|
+
WHERE e.timesheet_id = $1
|
|
3026
|
+
AND e.deleted_at IS NULL
|
|
3027
|
+
AND p.manager_collaborator_id IS NOT NULL
|
|
3028
|
+
LIMIT 1`, [timesheetId]);
|
|
3029
|
+
const approverId = (_c = (_b = (_a = projectManagerRow === null || projectManagerRow === void 0 ? void 0 : projectManagerRow.managerCollaboratorId) !== null && _a !== void 0 ? _a : current.approverCollaboratorId) !== null && _b !== void 0 ? _b : collaborator.supervisorId) !== null && _c !== void 0 ? _c : null;
|
|
3040
3030
|
if (!approverId) {
|
|
3041
3031
|
throw new common_1.BadRequestException('An approver is required before submitting a timesheet.');
|
|
3042
3032
|
}
|
|
@@ -3065,17 +3055,40 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3065
3055
|
});
|
|
3066
3056
|
return this.listSingleTimesheet(actor, timesheetId);
|
|
3067
3057
|
}
|
|
3068
|
-
async listTimeOffRequests(userId) {
|
|
3058
|
+
async listTimeOffRequests(userId, filters = {}) {
|
|
3059
|
+
var _a, _b;
|
|
3069
3060
|
const actor = await this.getActorContext(userId);
|
|
3070
3061
|
const filter = this.buildIdFilter(actor.visibleCollaboratorIds, 'tor.collaborator_id', actor.isDirector);
|
|
3071
|
-
|
|
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,
|
|
3072
3085
|
tor.collaborator_id AS "collaboratorId",
|
|
3073
3086
|
c.display_name AS "collaboratorName",
|
|
3074
3087
|
tor.approver_collaborator_id AS "approverCollaboratorId",
|
|
3075
3088
|
a.display_name AS "approverName",
|
|
3076
3089
|
tor.request_type AS "requestType",
|
|
3077
|
-
tor.start_date AS "startDate",
|
|
3078
|
-
tor.end_date AS "endDate",
|
|
3090
|
+
tor.start_date::text AS "startDate",
|
|
3091
|
+
tor.end_date::text AS "endDate",
|
|
3079
3092
|
tor.total_days AS "totalDays",
|
|
3080
3093
|
tor.status,
|
|
3081
3094
|
tor.reason,
|
|
@@ -3089,8 +3102,30 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3089
3102
|
ON approval.target_type = 'time_off_request'
|
|
3090
3103
|
AND approval.target_id = tor.id
|
|
3091
3104
|
AND approval.deleted_at IS NULL
|
|
3092
|
-
WHERE
|
|
3093
|
-
|
|
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);
|
|
3094
3129
|
}
|
|
3095
3130
|
async createTimeOffRequest(userId, data) {
|
|
3096
3131
|
const actor = await this.getActorContext(userId);
|
|
@@ -3145,8 +3180,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3145
3180
|
collaborator_id AS "collaboratorId",
|
|
3146
3181
|
approver_collaborator_id AS "approverCollaboratorId",
|
|
3147
3182
|
request_type AS "requestType",
|
|
3148
|
-
start_date AS "startDate",
|
|
3149
|
-
end_date AS "endDate",
|
|
3183
|
+
start_date::text AS "startDate",
|
|
3184
|
+
end_date::text AS "endDate",
|
|
3150
3185
|
total_days AS "totalDays",
|
|
3151
3186
|
status,
|
|
3152
3187
|
reason,
|
|
@@ -3155,17 +3190,56 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3155
3190
|
FROM operations_time_off_request
|
|
3156
3191
|
WHERE id = $1`, [created]);
|
|
3157
3192
|
}
|
|
3158
|
-
async listScheduleAdjustments(userId) {
|
|
3193
|
+
async listScheduleAdjustments(userId, filters = {}) {
|
|
3194
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3159
3195
|
const actor = await this.getActorContext(userId);
|
|
3160
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
|
+
: '';
|
|
3161
3235
|
const requests = await this.queryRows(`SELECT sar.id,
|
|
3162
3236
|
sar.collaborator_id AS "collaboratorId",
|
|
3163
3237
|
c.display_name AS "collaboratorName",
|
|
3164
3238
|
sar.approver_collaborator_id AS "approverCollaboratorId",
|
|
3165
3239
|
a.display_name AS "approverName",
|
|
3166
3240
|
sar.request_scope AS "requestScope",
|
|
3167
|
-
sar.effective_start_date AS "effectiveStartDate",
|
|
3168
|
-
sar.effective_end_date AS "effectiveEndDate",
|
|
3241
|
+
sar.effective_start_date::text AS "effectiveStartDate",
|
|
3242
|
+
sar.effective_end_date::text AS "effectiveEndDate",
|
|
3169
3243
|
sar.status,
|
|
3170
3244
|
sar.reason,
|
|
3171
3245
|
sar.submitted_at AS "submittedAt",
|
|
@@ -3178,11 +3252,22 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3178
3252
|
ON approval.target_type = 'schedule_adjustment_request'
|
|
3179
3253
|
AND approval.target_id = sar.id
|
|
3180
3254
|
AND approval.deleted_at IS NULL
|
|
3181
|
-
WHERE
|
|
3182
|
-
ORDER BY
|
|
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);
|
|
3183
3258
|
if (!requests.length) {
|
|
3259
|
+
if (pagination) {
|
|
3260
|
+
return this.buildPaginationResult([], 0, pagination.page, pagination.pageSize);
|
|
3261
|
+
}
|
|
3184
3262
|
return requests;
|
|
3185
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;
|
|
3186
3271
|
const days = await this.queryRows(`SELECT schedule_adjustment_request_id AS "requestId",
|
|
3187
3272
|
weekday,
|
|
3188
3273
|
is_working_day AS "isWorkingDay",
|
|
@@ -3192,7 +3277,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3192
3277
|
FROM operations_schedule_adjustment_day
|
|
3193
3278
|
WHERE schedule_adjustment_request_id = ANY($1::int[])
|
|
3194
3279
|
ORDER BY id ASC`, [requests.map((item) => item.id)]);
|
|
3195
|
-
const currentSchedule = await this.queryRows(`SELECT
|
|
3280
|
+
const currentSchedule = await this.queryRows(`SELECT DISTINCT ON (collaborator_id, weekday)
|
|
3281
|
+
collaborator_id AS "collaboratorId",
|
|
3196
3282
|
weekday,
|
|
3197
3283
|
is_working_day AS "isWorkingDay",
|
|
3198
3284
|
start_time AS "startTime",
|
|
@@ -3200,13 +3286,17 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3200
3286
|
break_minutes AS "breakMinutes"
|
|
3201
3287
|
FROM operations_collaborator_schedule_day
|
|
3202
3288
|
WHERE collaborator_id = ANY($1::int[])
|
|
3203
|
-
ORDER BY id
|
|
3289
|
+
ORDER BY collaborator_id, weekday, id DESC`, [this.uniqueNumbers(requests.map((item) => item.collaboratorId))]);
|
|
3204
3290
|
const grouped = this.groupBy(days, 'requestId');
|
|
3205
3291
|
const currentScheduleByCollaborator = this.groupBy(currentSchedule, 'collaboratorId');
|
|
3206
|
-
|
|
3292
|
+
const data = requests.map((request) => {
|
|
3207
3293
|
var _a, _b;
|
|
3208
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 : [] }));
|
|
3209
3295
|
});
|
|
3296
|
+
if (!pagination) {
|
|
3297
|
+
return data;
|
|
3298
|
+
}
|
|
3299
|
+
return this.buildPaginationResult(data, total, pagination.page, pagination.pageSize);
|
|
3210
3300
|
}
|
|
3211
3301
|
async createScheduleAdjustmentRequest(userId, data) {
|
|
3212
3302
|
const actor = await this.getActorContext(userId);
|
|
@@ -3278,8 +3368,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3278
3368
|
collaborator_id AS "collaboratorId",
|
|
3279
3369
|
approver_collaborator_id AS "approverCollaboratorId",
|
|
3280
3370
|
request_scope AS "requestScope",
|
|
3281
|
-
effective_start_date AS "effectiveStartDate",
|
|
3282
|
-
effective_end_date AS "effectiveEndDate",
|
|
3371
|
+
effective_start_date::text AS "effectiveStartDate",
|
|
3372
|
+
effective_end_date::text AS "effectiveEndDate",
|
|
3283
3373
|
status,
|
|
3284
3374
|
reason,
|
|
3285
3375
|
submitted_at AS "submittedAt",
|
|
@@ -3287,14 +3377,138 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3287
3377
|
FROM operations_schedule_adjustment_request
|
|
3288
3378
|
WHERE id = $1`, [created]);
|
|
3289
3379
|
}
|
|
3290
|
-
async listApprovals(userId) {
|
|
3380
|
+
async listApprovals(userId, filters = {}) {
|
|
3381
|
+
var _a, _b;
|
|
3382
|
+
const actor = await this.getActorContext(userId);
|
|
3383
|
+
this.ensureSupervisor(actor);
|
|
3384
|
+
const params = [];
|
|
3385
|
+
const clause = actor.isDirector
|
|
3386
|
+
? 'a.deleted_at IS NULL'
|
|
3387
|
+
: `a.deleted_at IS NULL AND a.approver_collaborator_id = ${this.param(params, actor.collaboratorId)}`;
|
|
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,
|
|
3414
|
+
a.target_type AS "targetType",
|
|
3415
|
+
a.target_id AS "targetId",
|
|
3416
|
+
a.requester_collaborator_id AS "requesterCollaboratorId",
|
|
3417
|
+
requester.display_name AS "requesterName",
|
|
3418
|
+
a.approver_collaborator_id AS "approverCollaboratorId",
|
|
3419
|
+
approver.display_name AS "approverName",
|
|
3420
|
+
a.status,
|
|
3421
|
+
a.submitted_at AS "submittedAt",
|
|
3422
|
+
a.decided_at AS "decidedAt",
|
|
3423
|
+
a.decision_note AS "decisionNote",
|
|
3424
|
+
t.week_start_date AS "timesheetWeekStartDate",
|
|
3425
|
+
t.week_end_date AS "timesheetWeekEndDate",
|
|
3426
|
+
t.total_hours AS "timesheetTotalHours",
|
|
3427
|
+
COALESCE(
|
|
3428
|
+
STRING_AGG(DISTINCT p.name, ', ') FILTER (WHERE p.name IS NOT NULL),
|
|
3429
|
+
''
|
|
3430
|
+
) AS "timesheetProjectNames",
|
|
3431
|
+
tor.request_type AS "timeOffType",
|
|
3432
|
+
tor.start_date::text AS "timeOffStartDate",
|
|
3433
|
+
tor.end_date::text AS "timeOffEndDate",
|
|
3434
|
+
tor.reason AS "timeOffReason",
|
|
3435
|
+
sar.request_scope AS "scheduleRequestScope",
|
|
3436
|
+
sar.effective_start_date::text AS "scheduleStartDate",
|
|
3437
|
+
sar.effective_end_date::text AS "scheduleEndDate",
|
|
3438
|
+
sar.reason AS "scheduleReason"
|
|
3439
|
+
FROM operations_approval a
|
|
3440
|
+
JOIN operations_collaborator requester
|
|
3441
|
+
ON requester.id = a.requester_collaborator_id
|
|
3442
|
+
LEFT JOIN operations_collaborator approver
|
|
3443
|
+
ON approver.id = a.approver_collaborator_id
|
|
3444
|
+
LEFT JOIN operations_timesheet t
|
|
3445
|
+
ON a.target_type = 'timesheet'
|
|
3446
|
+
AND t.id = a.target_id
|
|
3447
|
+
LEFT JOIN operations_timesheet_entry te
|
|
3448
|
+
ON te.timesheet_id = t.id
|
|
3449
|
+
AND te.deleted_at IS NULL
|
|
3450
|
+
LEFT JOIN operations_project_assignment pa
|
|
3451
|
+
ON pa.id = te.project_assignment_id
|
|
3452
|
+
LEFT JOIN operations_project p
|
|
3453
|
+
ON p.id = pa.project_id
|
|
3454
|
+
LEFT JOIN operations_time_off_request tor
|
|
3455
|
+
ON a.target_type = 'time_off_request'
|
|
3456
|
+
AND tor.id = a.target_id
|
|
3457
|
+
LEFT JOIN operations_schedule_adjustment_request sar
|
|
3458
|
+
ON a.target_type = 'schedule_adjustment_request'
|
|
3459
|
+
AND sar.id = a.target_id
|
|
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);
|
|
3496
|
+
}
|
|
3497
|
+
async approve(userId, approvalId, data) {
|
|
3498
|
+
return this.decideApproval(userId, approvalId, 'approve', data);
|
|
3499
|
+
}
|
|
3500
|
+
async reject(userId, approvalId, data) {
|
|
3501
|
+
return this.decideApproval(userId, approvalId, 'reject', data);
|
|
3502
|
+
}
|
|
3503
|
+
async getApprovalDetail(userId, approvalId) {
|
|
3291
3504
|
const actor = await this.getActorContext(userId);
|
|
3292
3505
|
this.ensureSupervisor(actor);
|
|
3293
3506
|
const params = [];
|
|
3294
|
-
const
|
|
3507
|
+
const actorClause = actor.isDirector
|
|
3295
3508
|
? 'a.deleted_at IS NULL'
|
|
3296
3509
|
: `a.deleted_at IS NULL AND a.approver_collaborator_id = ${this.param(params, actor.collaboratorId)}`;
|
|
3297
|
-
|
|
3510
|
+
const idPlaceholder = this.param(params, approvalId);
|
|
3511
|
+
const row = await this.querySingle(`SELECT a.id,
|
|
3298
3512
|
a.target_type AS "targetType",
|
|
3299
3513
|
a.target_id AS "targetId",
|
|
3300
3514
|
a.requester_collaborator_id AS "requesterCollaboratorId",
|
|
@@ -3313,12 +3527,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3313
3527
|
''
|
|
3314
3528
|
) AS "timesheetProjectNames",
|
|
3315
3529
|
tor.request_type AS "timeOffType",
|
|
3316
|
-
tor.start_date AS "timeOffStartDate",
|
|
3317
|
-
tor.end_date AS "timeOffEndDate",
|
|
3530
|
+
tor.start_date::text AS "timeOffStartDate",
|
|
3531
|
+
tor.end_date::text AS "timeOffEndDate",
|
|
3318
3532
|
tor.reason AS "timeOffReason",
|
|
3319
3533
|
sar.request_scope AS "scheduleRequestScope",
|
|
3320
|
-
sar.effective_start_date AS "scheduleStartDate",
|
|
3321
|
-
sar.effective_end_date AS "scheduleEndDate",
|
|
3534
|
+
sar.effective_start_date::text AS "scheduleStartDate",
|
|
3535
|
+
sar.effective_end_date::text AS "scheduleEndDate",
|
|
3322
3536
|
sar.reason AS "scheduleReason"
|
|
3323
3537
|
FROM operations_approval a
|
|
3324
3538
|
JOIN operations_collaborator requester
|
|
@@ -3341,15 +3555,64 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3341
3555
|
LEFT JOIN operations_schedule_adjustment_request sar
|
|
3342
3556
|
ON a.target_type = 'schedule_adjustment_request'
|
|
3343
3557
|
AND sar.id = a.target_id
|
|
3344
|
-
WHERE ${
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
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;
|
|
3353
3616
|
}
|
|
3354
3617
|
async publishAccountsPayableReference(userId, data) {
|
|
3355
3618
|
var _a, _b, _c, _d;
|
|
@@ -3524,17 +3787,44 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3524
3787
|
};
|
|
3525
3788
|
}
|
|
3526
3789
|
async getCollaboratorByUserId(userId) {
|
|
3527
|
-
return this.querySingle(`
|
|
3790
|
+
return this.querySingle(`WITH linked_collaborator AS (
|
|
3791
|
+
SELECT person_id
|
|
3792
|
+
FROM operations_collaborator
|
|
3793
|
+
WHERE user_id = $1
|
|
3794
|
+
AND deleted_at IS NULL
|
|
3795
|
+
ORDER BY id DESC
|
|
3796
|
+
LIMIT 1
|
|
3797
|
+
)
|
|
3798
|
+
SELECT c.id,
|
|
3799
|
+
c.user_id AS "userId",
|
|
3800
|
+
c.person_id AS "personId",
|
|
3528
3801
|
COALESCE(NULLIF(c.display_name, ''), person_record.name) AS "displayName",
|
|
3529
3802
|
s.id AS "supervisorId",
|
|
3530
|
-
s.display_name AS "supervisorName"
|
|
3803
|
+
s.display_name AS "supervisorName",
|
|
3804
|
+
COUNT(pa.id) FILTER (
|
|
3805
|
+
WHERE pa.deleted_at IS NULL
|
|
3806
|
+
AND pa.status IN ('planned', 'active')
|
|
3807
|
+
)::int AS "activeAssignments"
|
|
3531
3808
|
FROM operations_collaborator c
|
|
3532
3809
|
LEFT JOIN person person_record
|
|
3533
3810
|
ON person_record.id = c.person_id
|
|
3534
3811
|
LEFT JOIN operations_collaborator s
|
|
3535
3812
|
ON s.id = c.supervisor_collaborator_id
|
|
3536
|
-
|
|
3537
|
-
|
|
3813
|
+
LEFT JOIN operations_project_assignment pa
|
|
3814
|
+
ON pa.collaborator_id = c.id
|
|
3815
|
+
WHERE c.deleted_at IS NULL
|
|
3816
|
+
AND (
|
|
3817
|
+
c.user_id = $1
|
|
3818
|
+
OR (
|
|
3819
|
+
c.person_id IS NOT NULL
|
|
3820
|
+
AND c.person_id = (SELECT person_id FROM linked_collaborator)
|
|
3821
|
+
)
|
|
3822
|
+
)
|
|
3823
|
+
GROUP BY c.id, person_record.id, s.id
|
|
3824
|
+
ORDER BY "activeAssignments" DESC,
|
|
3825
|
+
CASE WHEN c.user_id = $1 THEN 0 ELSE 1 END,
|
|
3826
|
+
c.id ASC
|
|
3827
|
+
LIMIT 1`, [userId]);
|
|
3538
3828
|
}
|
|
3539
3829
|
async getCollaboratorById(collaboratorId) {
|
|
3540
3830
|
const collaborator = await this.querySingle(`SELECT c.id,
|
|
@@ -3916,73 +4206,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3916
4206
|
LIMIT 1`, normalizedLookup));
|
|
3917
4207
|
return (_b = collaboratorTypes[0]) !== null && _b !== void 0 ? _b : null;
|
|
3918
4208
|
}
|
|
3919
|
-
async getContractTemplateRecord(client, templateId, includeInactive = false) {
|
|
3920
|
-
const template = (await client.$queryRawUnsafe(`SELECT t.id,
|
|
3921
|
-
t.slug,
|
|
3922
|
-
t.code,
|
|
3923
|
-
t.name,
|
|
3924
|
-
t.description,
|
|
3925
|
-
t.contract_category AS "contractCategory",
|
|
3926
|
-
t.contract_type AS "contractType",
|
|
3927
|
-
t.billing_model AS "billingModel",
|
|
3928
|
-
t.signature_status AS "signatureStatus",
|
|
3929
|
-
t.is_active AS "isActive",
|
|
3930
|
-
t.status,
|
|
3931
|
-
t.content_html AS "contentHtml",
|
|
3932
|
-
COUNT(DISTINCT c.id)::int AS "usageCount",
|
|
3933
|
-
t.created_at AS "createdAt",
|
|
3934
|
-
t.updated_at AS "updatedAt"
|
|
3935
|
-
FROM operations_contract_template t
|
|
3936
|
-
LEFT JOIN operations_contract c
|
|
3937
|
-
ON c.contract_template_id = t.id
|
|
3938
|
-
AND c.deleted_at IS NULL
|
|
3939
|
-
WHERE t.id = $1
|
|
3940
|
-
AND ($2::boolean = true OR t.deleted_at IS NULL)
|
|
3941
|
-
GROUP BY t.id
|
|
3942
|
-
LIMIT 1`, templateId, includeInactive));
|
|
3943
|
-
const record = template[0];
|
|
3944
|
-
if (!record) {
|
|
3945
|
-
throw new common_1.NotFoundException('Contract template not found.');
|
|
3946
|
-
}
|
|
3947
|
-
return record;
|
|
3948
|
-
}
|
|
3949
|
-
async assertContractTemplateNameAvailable(client, name, excludeTemplateId) {
|
|
3950
|
-
const existing = (await client.$queryRawUnsafe(`SELECT id
|
|
3951
|
-
FROM operations_contract_template
|
|
3952
|
-
WHERE LOWER(name) = LOWER($1)
|
|
3953
|
-
AND deleted_at IS NULL
|
|
3954
|
-
AND ($2::int IS NULL OR id <> $2)
|
|
3955
|
-
LIMIT 1`, name, excludeTemplateId !== null && excludeTemplateId !== void 0 ? excludeTemplateId : null));
|
|
3956
|
-
if (existing[0]) {
|
|
3957
|
-
throw new common_1.BadRequestException('A contract template with this name already exists.');
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
async assertContractTemplateCodeAvailable(client, code, excludeTemplateId) {
|
|
3961
|
-
const existing = (await client.$queryRawUnsafe(`SELECT id
|
|
3962
|
-
FROM operations_contract_template
|
|
3963
|
-
WHERE UPPER(COALESCE(code, '')) = UPPER($1)
|
|
3964
|
-
AND deleted_at IS NULL
|
|
3965
|
-
AND ($2::int IS NULL OR id <> $2)
|
|
3966
|
-
LIMIT 1`, code, excludeTemplateId !== null && excludeTemplateId !== void 0 ? excludeTemplateId : null));
|
|
3967
|
-
if (existing[0]) {
|
|
3968
|
-
throw new common_1.BadRequestException('A contract template with this code already exists.');
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
async generateUniqueContractTemplateSlug(client, label, excludeTemplateId) {
|
|
3972
|
-
const baseSlug = this.slugifyValue(label) || `contract-template-${Date.now().toString(36)}`;
|
|
3973
|
-
for (let attempt = 0; attempt < 25; attempt += 1) {
|
|
3974
|
-
const candidate = attempt === 0 ? baseSlug : `${baseSlug}-${attempt + 1}`;
|
|
3975
|
-
const existing = (await client.$queryRawUnsafe(`SELECT id
|
|
3976
|
-
FROM operations_contract_template
|
|
3977
|
-
WHERE slug = $1
|
|
3978
|
-
AND ($2::int IS NULL OR id <> $2)
|
|
3979
|
-
LIMIT 1`, candidate, excludeTemplateId !== null && excludeTemplateId !== void 0 ? excludeTemplateId : null));
|
|
3980
|
-
if (!existing.length) {
|
|
3981
|
-
return candidate;
|
|
3982
|
-
}
|
|
3983
|
-
}
|
|
3984
|
-
return `${baseSlug}-${Date.now().toString(36)}`;
|
|
3985
|
-
}
|
|
3986
4209
|
slugifyValue(value) {
|
|
3987
4210
|
return (value !== null && value !== void 0 ? value : '')
|
|
3988
4211
|
.normalize('NFKD')
|
|
@@ -4108,7 +4331,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4108
4331
|
WHERE pa.project_id = $1
|
|
4109
4332
|
AND pa.deleted_at IS NULL`, [projectId]),
|
|
4110
4333
|
this.querySingle(`SELECT COUNT(*) FILTER (WHERE status IN ('planned', 'active'))::text AS "activeAssignments",
|
|
4111
|
-
COUNT(*) FILTER (WHERE
|
|
4334
|
+
COUNT(*) FILTER (WHERE status = 'completed')::text AS "completedAssignments",
|
|
4112
4335
|
COALESCE(AVG(allocation_percent), 0)::text AS "averageAllocation",
|
|
4113
4336
|
COALESCE(SUM(weekly_hours), 0)::text AS "totalWeeklyHours"
|
|
4114
4337
|
FROM operations_project_assignment
|
|
@@ -4122,11 +4345,70 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4122
4345
|
totalHours: Number((_c = timesheetSummary === null || timesheetSummary === void 0 ? void 0 : timesheetSummary.totalHours) !== null && _c !== void 0 ? _c : 0),
|
|
4123
4346
|
}, operationalIndicators: {
|
|
4124
4347
|
activeAssignments: Number((_d = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.activeAssignments) !== null && _d !== void 0 ? _d : 0),
|
|
4125
|
-
|
|
4348
|
+
completedAssignments: Number((_e = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.completedAssignments) !== null && _e !== void 0 ? _e : 0),
|
|
4126
4349
|
averageAllocation: Number((_f = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.averageAllocation) !== null && _f !== void 0 ? _f : 0),
|
|
4127
4350
|
totalWeeklyHours: Number((_g = operationalIndicators === null || operationalIndicators === void 0 ? void 0 : operationalIndicators.totalWeeklyHours) !== null && _g !== void 0 ? _g : 0),
|
|
4128
4351
|
} });
|
|
4129
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
|
+
}
|
|
4130
4412
|
async getCollaboratorDetails(collaboratorId) {
|
|
4131
4413
|
var _a, _b, _c, _d, _e, _f;
|
|
4132
4414
|
const collaborator = await this.querySingle(`SELECT c.id,
|
|
@@ -4268,8 +4550,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4268
4550
|
AND deleted_at IS NULL`, [collaboratorId]),
|
|
4269
4551
|
this.queryRows(`SELECT id,
|
|
4270
4552
|
request_scope AS "requestScope",
|
|
4271
|
-
effective_start_date AS "effectiveStartDate",
|
|
4272
|
-
effective_end_date AS "effectiveEndDate",
|
|
4553
|
+
effective_start_date::text AS "effectiveStartDate",
|
|
4554
|
+
effective_end_date::text AS "effectiveEndDate",
|
|
4273
4555
|
status,
|
|
4274
4556
|
reason
|
|
4275
4557
|
FROM operations_schedule_adjustment_request
|
|
@@ -4584,7 +4866,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4584
4866
|
return timesheet;
|
|
4585
4867
|
}
|
|
4586
4868
|
async replaceTimesheetEntries(client, timesheetId, entries, collaboratorId) {
|
|
4587
|
-
var _a, _b, _c, _d, _e, _f;
|
|
4869
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4588
4870
|
await client.$executeRawUnsafe(`UPDATE operations_timesheet_entry
|
|
4589
4871
|
SET deleted_at = NOW()
|
|
4590
4872
|
WHERE timesheet_id = $1
|
|
@@ -4594,17 +4876,19 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4594
4876
|
const assignmentIds = entries
|
|
4595
4877
|
.map((entry) => entry.projectAssignmentId)
|
|
4596
4878
|
.filter((value) => typeof value === 'number');
|
|
4879
|
+
const assignmentMap = new Map();
|
|
4597
4880
|
if (assignmentIds.length) {
|
|
4598
|
-
const assignments = (await client.$queryRawUnsafe(`SELECT id,
|
|
4881
|
+
const assignments = (await client.$queryRawUnsafe(`SELECT id,
|
|
4882
|
+
collaborator_id AS "collaboratorId",
|
|
4883
|
+
project_id AS "projectId"
|
|
4599
4884
|
FROM operations_project_assignment
|
|
4600
4885
|
WHERE id = ANY($1::int[])
|
|
4601
4886
|
AND deleted_at IS NULL`, assignmentIds));
|
|
4602
|
-
|
|
4603
|
-
assignment.id,
|
|
4604
|
-
|
|
4605
|
-
]));
|
|
4887
|
+
assignments.forEach((assignment) => {
|
|
4888
|
+
assignmentMap.set(assignment.id, assignment);
|
|
4889
|
+
});
|
|
4606
4890
|
for (const assignmentId of assignmentIds) {
|
|
4607
|
-
if (assignmentMap.get(assignmentId) !== collaboratorId) {
|
|
4891
|
+
if (((_a = assignmentMap.get(assignmentId)) === null || _a === void 0 ? void 0 : _a.collaboratorId) !== collaboratorId) {
|
|
4608
4892
|
throw new common_1.ForbiddenException('Timesheet entries must use assignments owned by the target collaborator.');
|
|
4609
4893
|
}
|
|
4610
4894
|
}
|
|
@@ -4615,16 +4899,19 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4615
4899
|
const resolvedTask = entry.taskId
|
|
4616
4900
|
? await this.getOwnedTaskRecord(client, collaboratorId, entry.taskId)
|
|
4617
4901
|
: null;
|
|
4902
|
+
const selectedAssignment = entry.projectAssignmentId
|
|
4903
|
+
? (_b = assignmentMap.get(entry.projectAssignmentId)) !== null && _b !== void 0 ? _b : null
|
|
4904
|
+
: null;
|
|
4618
4905
|
if (resolvedTask &&
|
|
4619
|
-
|
|
4620
|
-
resolvedTask.
|
|
4621
|
-
throw new common_1.BadRequestException('The selected task does not belong to the chosen project
|
|
4906
|
+
selectedAssignment &&
|
|
4907
|
+
resolvedTask.projectId !== selectedAssignment.projectId) {
|
|
4908
|
+
throw new common_1.BadRequestException('The selected task does not belong to the chosen project.');
|
|
4622
4909
|
}
|
|
4623
|
-
const resolvedAssignmentId = (
|
|
4910
|
+
const resolvedAssignmentId = (_d = (_c = entry.projectAssignmentId) !== null && _c !== void 0 ? _c : resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.projectAssignmentId) !== null && _d !== void 0 ? _d : null;
|
|
4624
4911
|
if (entry.taskId && !resolvedAssignmentId) {
|
|
4625
4912
|
throw new common_1.BadRequestException('The selected task must belong to a project assignment.');
|
|
4626
4913
|
}
|
|
4627
|
-
const activityLabel = (
|
|
4914
|
+
const activityLabel = (_f = (_e = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.name) !== null && _e !== void 0 ? _e : this.normalizeOptionalText(entry.taskName)) !== null && _f !== void 0 ? _f : this.normalizeOptionalText(entry.activityLabel);
|
|
4628
4915
|
if (!entry.workDate) {
|
|
4629
4916
|
throw new common_1.BadRequestException('Timesheet entry workDate is required.');
|
|
4630
4917
|
}
|
|
@@ -4639,7 +4926,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4639
4926
|
description,
|
|
4640
4927
|
created_at,
|
|
4641
4928
|
updated_at
|
|
4642
|
-
) VALUES ($1, $2, $3, $4, $5::date, $6, $7, $8, NOW(), NOW())`, timesheetId, resolvedAssignmentId, (
|
|
4929
|
+
) VALUES ($1, $2, $3, $4, $5::date, $6, $7, $8, NOW(), NOW())`, timesheetId, resolvedAssignmentId, (_h = (_g = resolvedTask === null || resolvedTask === void 0 ? void 0 : resolvedTask.id) !== null && _g !== void 0 ? _g : entry.taskId) !== null && _h !== void 0 ? _h : null, activityLabel !== null && activityLabel !== void 0 ? activityLabel : null, entry.workDate, durationMinutes, hours, this.normalizeOptionalText(entry.description));
|
|
4643
4930
|
}
|
|
4644
4931
|
}
|
|
4645
4932
|
async refreshTimesheetTotal(client, timesheetId) {
|
|
@@ -4665,6 +4952,29 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4665
4952
|
updated_at = NOW()
|
|
4666
4953
|
WHERE id = $1`, timesheetId);
|
|
4667
4954
|
}
|
|
4955
|
+
async cleanupEmptyEditableTimesheet(client, timesheetId) {
|
|
4956
|
+
var _a;
|
|
4957
|
+
const candidate = (await client.$queryRawUnsafe(`SELECT t.id
|
|
4958
|
+
FROM operations_timesheet t
|
|
4959
|
+
WHERE t.id = $1
|
|
4960
|
+
AND t.deleted_at IS NULL
|
|
4961
|
+
AND t.status IN ('draft', 'rejected')
|
|
4962
|
+
AND NOT EXISTS (
|
|
4963
|
+
SELECT 1
|
|
4964
|
+
FROM operations_timesheet_entry e
|
|
4965
|
+
WHERE e.timesheet_id = t.id
|
|
4966
|
+
AND e.deleted_at IS NULL
|
|
4967
|
+
)
|
|
4968
|
+
LIMIT 1`, timesheetId));
|
|
4969
|
+
if (!((_a = candidate[0]) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4972
|
+
await client.$executeRawUnsafe(`UPDATE operations_timesheet
|
|
4973
|
+
SET deleted_at = NOW(),
|
|
4974
|
+
updated_at = NOW()
|
|
4975
|
+
WHERE id = $1
|
|
4976
|
+
AND deleted_at IS NULL`, timesheetId);
|
|
4977
|
+
}
|
|
4668
4978
|
async upsertApproval(client, input) {
|
|
4669
4979
|
var _a, _b;
|
|
4670
4980
|
const existing = (await client.$queryRawUnsafe(`SELECT id
|
|
@@ -4844,7 +5154,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4844
5154
|
}
|
|
4845
5155
|
}
|
|
4846
5156
|
async replaceProjectAssignments(client, projectId, teamAssignments) {
|
|
4847
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
5157
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4848
5158
|
await client.$executeRawUnsafe(`UPDATE operations_project_assignment
|
|
4849
5159
|
SET deleted_at = NOW(),
|
|
4850
5160
|
updated_at = NOW()
|
|
@@ -4863,17 +5173,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4863
5173
|
role_label,
|
|
4864
5174
|
allocation_percent,
|
|
4865
5175
|
weekly_hours,
|
|
4866
|
-
is_billable,
|
|
4867
5176
|
start_date,
|
|
4868
5177
|
end_date,
|
|
4869
5178
|
status,
|
|
4870
5179
|
created_at,
|
|
4871
5180
|
updated_at
|
|
4872
5181
|
) VALUES (
|
|
4873
|
-
$1, $2, $3, $4, $5, $6, $7
|
|
4874
|
-
$
|
|
5182
|
+
$1, $2, $3, $4, $5, $6, $7::date, $8::date,
|
|
5183
|
+
$9::operations_project_assignment_status_155b459bbf_enum,
|
|
4875
5184
|
NOW(), NOW()
|
|
4876
|
-
)`, 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.
|
|
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');
|
|
4877
5186
|
}
|
|
4878
5187
|
}
|
|
4879
5188
|
async replaceCollaboratorEquityParticipation(client, collaboratorId, equityParticipation) {
|
|
@@ -5125,106 +5434,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5125
5434
|
updated_at = NOW()
|
|
5126
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);
|
|
5127
5436
|
}
|
|
5128
|
-
async createProjectContractDraft(client, createdByUserId, input) {
|
|
5129
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
5130
|
-
const templateRows = input.contractTemplateId
|
|
5131
|
-
? (await client.$queryRawUnsafe(`SELECT id,
|
|
5132
|
-
code,
|
|
5133
|
-
name,
|
|
5134
|
-
description,
|
|
5135
|
-
contract_category AS "contractCategory",
|
|
5136
|
-
contract_type AS "contractType",
|
|
5137
|
-
billing_model AS "billingModel",
|
|
5138
|
-
signature_status AS "signatureStatus",
|
|
5139
|
-
content_html AS "contentHtml"
|
|
5140
|
-
FROM operations_contract_template
|
|
5141
|
-
WHERE id = $1
|
|
5142
|
-
AND deleted_at IS NULL
|
|
5143
|
-
LIMIT 1`, input.contractTemplateId))
|
|
5144
|
-
: [];
|
|
5145
|
-
const selectedTemplate = (_a = templateRows[0]) !== null && _a !== void 0 ? _a : null;
|
|
5146
|
-
const templateContext = {
|
|
5147
|
-
project_code: input.projectCode,
|
|
5148
|
-
project_name: input.projectName,
|
|
5149
|
-
client_name: input.clientName,
|
|
5150
|
-
start_date: (_b = input.startDate) !== null && _b !== void 0 ? _b : '',
|
|
5151
|
-
end_date: (_c = input.endDate) !== null && _c !== void 0 ? _c : '',
|
|
5152
|
-
budget_amount: input.budgetAmount !== null && input.budgetAmount !== undefined
|
|
5153
|
-
? String(input.budgetAmount)
|
|
5154
|
-
: '',
|
|
5155
|
-
monthly_hour_cap: input.monthlyHourCap !== null && input.monthlyHourCap !== undefined
|
|
5156
|
-
? String(input.monthlyHourCap)
|
|
5157
|
-
: '',
|
|
5158
|
-
};
|
|
5159
|
-
const applyTemplateVariables = (value) => {
|
|
5160
|
-
const source = value !== null && value !== void 0 ? value : '';
|
|
5161
|
-
return Object.entries(templateContext).reduce((result, [key, replacement]) => result.split(`{{${key}}}`).join(replacement || ''), source);
|
|
5162
|
-
};
|
|
5163
|
-
const templateCodePrefix = this.normalizeOptionalText(selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.code);
|
|
5164
|
-
const generatedContractCode = ((_e = (_d = this.normalizeOptionalText(input.contractCode)) !== null && _d !== void 0 ? _d : (templateCodePrefix
|
|
5165
|
-
? `${templateCodePrefix}-${input.projectCode}`
|
|
5166
|
-
: null)) !== null && _e !== void 0 ? _e : `PRJ-${input.projectCode}`).slice(0, 40);
|
|
5167
|
-
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`;
|
|
5168
|
-
const generatedDescription = this.normalizeOptionalText(applyTemplateVariables((_h = input.description) !== null && _h !== void 0 ? _h : selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.description));
|
|
5169
|
-
const generatedContentHtml = this.normalizeOptionalText(applyTemplateVariables(selectedTemplate === null || selectedTemplate === void 0 ? void 0 : selectedTemplate.contentHtml));
|
|
5170
|
-
const created = await client.$queryRawUnsafe(`INSERT INTO operations_contract (
|
|
5171
|
-
code,
|
|
5172
|
-
name,
|
|
5173
|
-
contract_category,
|
|
5174
|
-
contract_type,
|
|
5175
|
-
client_name,
|
|
5176
|
-
signature_status,
|
|
5177
|
-
is_active,
|
|
5178
|
-
billing_model,
|
|
5179
|
-
account_manager_collaborator_id,
|
|
5180
|
-
related_collaborator_id,
|
|
5181
|
-
contract_template_id,
|
|
5182
|
-
origin_type,
|
|
5183
|
-
origin_id,
|
|
5184
|
-
start_date,
|
|
5185
|
-
end_date,
|
|
5186
|
-
signed_at,
|
|
5187
|
-
effective_date,
|
|
5188
|
-
budget_amount,
|
|
5189
|
-
monthly_hour_cap,
|
|
5190
|
-
status,
|
|
5191
|
-
description,
|
|
5192
|
-
content_html,
|
|
5193
|
-
created_by_user_id,
|
|
5194
|
-
updated_by_user_id,
|
|
5195
|
-
created_at,
|
|
5196
|
-
updated_at
|
|
5197
|
-
) VALUES (
|
|
5198
|
-
$1,
|
|
5199
|
-
$2,
|
|
5200
|
-
$3::operations_contract_contract_category_70d553ea09_enum,
|
|
5201
|
-
$4::operations_contract_contract_type_48331e2ebf_enum,
|
|
5202
|
-
$5,
|
|
5203
|
-
$6::operations_contract_signature_status_2cb7282a7b_enum,
|
|
5204
|
-
true,
|
|
5205
|
-
$7::operations_contract_billing_model_409dc7fea2_enum,
|
|
5206
|
-
$8,
|
|
5207
|
-
NULL,
|
|
5208
|
-
$9,
|
|
5209
|
-
'client_project',
|
|
5210
|
-
$10,
|
|
5211
|
-
$11::date,
|
|
5212
|
-
$12::date,
|
|
5213
|
-
NULL,
|
|
5214
|
-
$11::date,
|
|
5215
|
-
$13,
|
|
5216
|
-
$14,
|
|
5217
|
-
'draft',
|
|
5218
|
-
$15,
|
|
5219
|
-
$16,
|
|
5220
|
-
$17,
|
|
5221
|
-
$17,
|
|
5222
|
-
NOW(),
|
|
5223
|
-
NOW()
|
|
5224
|
-
)
|
|
5225
|
-
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);
|
|
5226
|
-
return (_t = created[0]) === null || _t === void 0 ? void 0 : _t.id;
|
|
5227
|
-
}
|
|
5228
5437
|
async replaceContractParties(client, contractId, parties) {
|
|
5229
5438
|
var _a, _b, _c, _d, _e, _f;
|
|
5230
5439
|
await client.$executeRawUnsafe(`UPDATE operations_contract_party
|
|
@@ -5252,88 +5461,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5252
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);
|
|
5253
5462
|
}
|
|
5254
5463
|
}
|
|
5255
|
-
async replaceContractSignatures(client, contractId, signatures) {
|
|
5256
|
-
var _a, _b, _c, _d;
|
|
5257
|
-
await client.$executeRawUnsafe(`UPDATE operations_contract_signature
|
|
5258
|
-
SET deleted_at = NOW(),
|
|
5259
|
-
updated_at = NOW()
|
|
5260
|
-
WHERE contract_id = $1
|
|
5261
|
-
AND deleted_at IS NULL`, contractId);
|
|
5262
|
-
for (const signature of signatures !== null && signatures !== void 0 ? signatures : []) {
|
|
5263
|
-
await client.$executeRawUnsafe(`INSERT INTO operations_contract_signature (
|
|
5264
|
-
contract_id,
|
|
5265
|
-
signer_name,
|
|
5266
|
-
signer_role,
|
|
5267
|
-
signer_email,
|
|
5268
|
-
signer_status,
|
|
5269
|
-
signed_at,
|
|
5270
|
-
created_at,
|
|
5271
|
-
updated_at
|
|
5272
|
-
) VALUES (
|
|
5273
|
-
$1, $2, $3, $4,
|
|
5274
|
-
$5::operations_contract_signature_signer_status_1e6fbe2519_enum,
|
|
5275
|
-
$6::timestamp, NOW(), NOW()
|
|
5276
|
-
)`, 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);
|
|
5277
|
-
}
|
|
5278
|
-
}
|
|
5279
|
-
async replaceContractFinancialTerms(client, contractId, financialTerms) {
|
|
5280
|
-
var _a, _b, _c, _d;
|
|
5281
|
-
await client.$executeRawUnsafe(`UPDATE operations_contract_financial_term
|
|
5282
|
-
SET deleted_at = NOW(),
|
|
5283
|
-
updated_at = NOW()
|
|
5284
|
-
WHERE contract_id = $1
|
|
5285
|
-
AND deleted_at IS NULL`, contractId);
|
|
5286
|
-
for (const term of financialTerms !== null && financialTerms !== void 0 ? financialTerms : []) {
|
|
5287
|
-
await client.$executeRawUnsafe(`INSERT INTO operations_contract_financial_term (
|
|
5288
|
-
contract_id,
|
|
5289
|
-
term_type,
|
|
5290
|
-
label,
|
|
5291
|
-
amount,
|
|
5292
|
-
recurrence,
|
|
5293
|
-
due_day,
|
|
5294
|
-
notes,
|
|
5295
|
-
created_at,
|
|
5296
|
-
updated_at
|
|
5297
|
-
) VALUES (
|
|
5298
|
-
$1,
|
|
5299
|
-
$2::operations_contract_financial_term_term_type_700635c06a_enum,
|
|
5300
|
-
$3,
|
|
5301
|
-
$4,
|
|
5302
|
-
$5::operations_contract_financial_term_recurrence_ba90bbe3bf_enum,
|
|
5303
|
-
$6,
|
|
5304
|
-
$7,
|
|
5305
|
-
NOW(), NOW()
|
|
5306
|
-
)`, 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);
|
|
5307
|
-
}
|
|
5308
|
-
}
|
|
5309
|
-
async replaceContractRevisions(client, contractId, revisions) {
|
|
5310
|
-
var _a, _b, _c, _d;
|
|
5311
|
-
await client.$executeRawUnsafe(`UPDATE operations_contract_revision
|
|
5312
|
-
SET deleted_at = NOW(),
|
|
5313
|
-
updated_at = NOW()
|
|
5314
|
-
WHERE contract_id = $1
|
|
5315
|
-
AND deleted_at IS NULL`, contractId);
|
|
5316
|
-
for (const revision of revisions !== null && revisions !== void 0 ? revisions : []) {
|
|
5317
|
-
await client.$executeRawUnsafe(`INSERT INTO operations_contract_revision (
|
|
5318
|
-
contract_id,
|
|
5319
|
-
revision_type,
|
|
5320
|
-
title,
|
|
5321
|
-
effective_date,
|
|
5322
|
-
status,
|
|
5323
|
-
summary,
|
|
5324
|
-
created_at,
|
|
5325
|
-
updated_at
|
|
5326
|
-
) VALUES (
|
|
5327
|
-
$1,
|
|
5328
|
-
$2::operations_contract_revision_revision_type_cf5ba1a538_enum,
|
|
5329
|
-
$3,
|
|
5330
|
-
$4::date,
|
|
5331
|
-
$5::operations_contract_revision_status_f44f35bb66_enum,
|
|
5332
|
-
$6,
|
|
5333
|
-
NOW(), NOW()
|
|
5334
|
-
)`, 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);
|
|
5335
|
-
}
|
|
5336
|
-
}
|
|
5337
5464
|
async replaceContractDocument(client, contractId, documentType, document) {
|
|
5338
5465
|
var _a, _b, _c, _d, _e;
|
|
5339
5466
|
await client.$executeRawUnsafe(`UPDATE operations_contract_document
|
|
@@ -5341,7 +5468,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5341
5468
|
updated_at = NOW()
|
|
5342
5469
|
WHERE contract_id = $1
|
|
5343
5470
|
AND deleted_at IS NULL
|
|
5344
|
-
AND document_type = $2`, contractId, documentType);
|
|
5471
|
+
AND document_type::text = $2`, contractId, documentType);
|
|
5345
5472
|
await client.$executeRawUnsafe(`INSERT INTO operations_contract_document (
|
|
5346
5473
|
contract_id,
|
|
5347
5474
|
document_type,
|
|
@@ -5356,7 +5483,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5356
5483
|
created_at,
|
|
5357
5484
|
updated_at
|
|
5358
5485
|
) VALUES (
|
|
5359
|
-
$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()
|
|
5360
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);
|
|
5361
5488
|
}
|
|
5362
5489
|
async resolveContractExtractionFile(userId, data) {
|
|
@@ -5441,7 +5568,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5441
5568
|
}
|
|
5442
5569
|
}
|
|
5443
5570
|
async buildContractPdfHtml(contract) {
|
|
5444
|
-
var _a, _b, _c, _d
|
|
5571
|
+
var _a, _b, _c, _d;
|
|
5445
5572
|
const logoUrl = await this.resolveContractPdfLogoUrl();
|
|
5446
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>';
|
|
5447
5574
|
const partiesHtml = ((_d = contract.parties) !== null && _d !== void 0 ? _d : []).length
|
|
@@ -5453,17 +5580,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5453
5580
|
</li>`)
|
|
5454
5581
|
.join('')
|
|
5455
5582
|
: '<li><strong>No parties registered yet.</strong><span>Complete this contract later if needed.</span></li>';
|
|
5456
|
-
const financialTermsHtml = ((_e = contract.financialTerms) !== null && _e !== void 0 ? _e : []).length
|
|
5457
|
-
? contract.financialTerms
|
|
5458
|
-
.map((term) => `
|
|
5459
|
-
<li>
|
|
5460
|
-
<strong>${this.escapeHtml(term.label || 'Term')}</strong>
|
|
5461
|
-
<span>${this.escapeHtml([term.termType, term.amount, term.recurrence]
|
|
5462
|
-
.filter((item) => item !== null && item !== undefined && String(item).trim())
|
|
5463
|
-
.join(' • '))}</span>
|
|
5464
|
-
</li>`)
|
|
5465
|
-
.join('')
|
|
5466
|
-
: '<li><strong>No financial terms registered yet.</strong><span>The draft is intentionally lightweight.</span></li>';
|
|
5467
5583
|
return `<!DOCTYPE html>
|
|
5468
5584
|
<html lang="en">
|
|
5469
5585
|
<head>
|
|
@@ -5608,11 +5724,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5608
5724
|
<ul>${partiesHtml}</ul>
|
|
5609
5725
|
</section>
|
|
5610
5726
|
|
|
5611
|
-
<section>
|
|
5612
|
-
<h2>Financial Terms</h2>
|
|
5613
|
-
<ul>${financialTermsHtml}</ul>
|
|
5614
|
-
</section>
|
|
5615
|
-
|
|
5616
5727
|
<section>
|
|
5617
5728
|
<h2>Contract Body</h2>
|
|
5618
5729
|
<div class="content">${contentHtml}</div>
|
|
@@ -5853,7 +5964,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5853
5964
|
effectiveDate: contract.effectiveDate,
|
|
5854
5965
|
signatureStatus: contract.signatureStatus,
|
|
5855
5966
|
parties: contract.parties,
|
|
5856
|
-
financialTerms: contract.financialTerms,
|
|
5857
5967
|
}, null, 2)}`,
|
|
5858
5968
|
`Contract HTML:\n${(_b = this.normalizeOptionalText(contract.contentHtml)) !== null && _b !== void 0 ? _b : ''}`,
|
|
5859
5969
|
].join('\n\n'),
|
|
@@ -5893,7 +6003,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5893
6003
|
}
|
|
5894
6004
|
}
|
|
5895
6005
|
buildHeuristicContractLegalReview(contract) {
|
|
5896
|
-
var _a, _b, _c, _d, _e, _f
|
|
6006
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5897
6007
|
const contentText = ((_a = this.normalizeOptionalText(contract.contentHtml)) !== null && _a !== void 0 ? _a : '')
|
|
5898
6008
|
.replace(/<[^>]+>/g, ' ')
|
|
5899
6009
|
.replace(/\s+/g, ' ')
|
|
@@ -5931,12 +6041,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
5931
6041
|
if (!this.normalizeOptionalText(primaryParty === null || primaryParty === void 0 ? void 0 : primaryParty.documentNumber)) {
|
|
5932
6042
|
warnings.push('Consider confirming the primary party document number.');
|
|
5933
6043
|
}
|
|
5934
|
-
if (
|
|
5935
|
-
warnings.push('Commercial conditions are still generic; define
|
|
5936
|
-
checklist.push('Attention: validate billing
|
|
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.');
|
|
5937
6047
|
}
|
|
5938
6048
|
else {
|
|
5939
|
-
checklist.push('OK: there is
|
|
6049
|
+
checklist.push('OK: there is a budget reference to review.');
|
|
5940
6050
|
}
|
|
5941
6051
|
if (!contentText) {
|
|
5942
6052
|
missingFields.push('Contract body content');
|
|
@@ -6102,34 +6212,6 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6102
6212
|
isPrimary: this.normalizeExtractionBoolean(party.isPrimary),
|
|
6103
6213
|
}))
|
|
6104
6214
|
.filter((party) => party.displayName),
|
|
6105
|
-
signatures: this.normalizeObjectList(raw.signatures)
|
|
6106
|
-
.map((signature) => ({
|
|
6107
|
-
signerName: this.normalizeExtractionString(signature.signerName),
|
|
6108
|
-
signerRole: this.normalizeExtractionString(signature.signerRole),
|
|
6109
|
-
signerEmail: this.normalizeExtractionString(signature.signerEmail),
|
|
6110
|
-
status: this.normalizeEnumValue(signature.status, SIGNATURE_ITEM_STATUS_VALUES, 'pending'),
|
|
6111
|
-
signedAt: this.normalizeExtractionDate(signature.signedAt),
|
|
6112
|
-
}))
|
|
6113
|
-
.filter((signature) => signature.signerName),
|
|
6114
|
-
financialTerms: this.normalizeObjectList(raw.financialTerms)
|
|
6115
|
-
.map((term) => ({
|
|
6116
|
-
label: this.normalizeExtractionString(term.label),
|
|
6117
|
-
termType: this.normalizeEnumValue(term.termType, FINANCIAL_TERM_TYPE_VALUES, 'value'),
|
|
6118
|
-
amount: this.normalizeExtractionNumber(term.amount),
|
|
6119
|
-
recurrence: this.normalizeEnumValue(term.recurrence, RECURRENCE_VALUES, 'one_time'),
|
|
6120
|
-
dueDay: this.normalizeExtractionNumber(term.dueDay),
|
|
6121
|
-
notes: this.normalizeExtractionString(term.notes),
|
|
6122
|
-
}))
|
|
6123
|
-
.filter((term) => term.label),
|
|
6124
|
-
revisions: this.normalizeObjectList(raw.revisions)
|
|
6125
|
-
.map((revision) => ({
|
|
6126
|
-
title: this.normalizeExtractionString(revision.title),
|
|
6127
|
-
revisionType: this.normalizeEnumValue(revision.revisionType, REVISION_TYPE_VALUES, 'revision'),
|
|
6128
|
-
effectiveDate: this.normalizeExtractionDate(revision.effectiveDate),
|
|
6129
|
-
status: this.normalizeEnumValue(revision.status, REVISION_STATUS_VALUES, 'draft'),
|
|
6130
|
-
summary: this.normalizeExtractionString(revision.summary),
|
|
6131
|
-
}))
|
|
6132
|
-
.filter((revision) => revision.title),
|
|
6133
6215
|
};
|
|
6134
6216
|
if (!draft.name)
|
|
6135
6217
|
missingFields.push('Contract title');
|
|
@@ -6231,6 +6313,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6231
6313
|
offset: (page - 1) * pageSize,
|
|
6232
6314
|
};
|
|
6233
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
|
+
}
|
|
6234
6325
|
buildPaginationResult(data, total, page, pageSize) {
|
|
6235
6326
|
const lastPage = Math.max(1, Math.ceil(total / Math.max(pageSize, 1)));
|
|
6236
6327
|
return {
|
|
@@ -6375,6 +6466,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6375
6466
|
async execute(sql, params = []) {
|
|
6376
6467
|
return this.prisma.$executeRawUnsafe(sql, ...params);
|
|
6377
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
|
+
}
|
|
6378
6475
|
};
|
|
6379
6476
|
exports.OperationsService = OperationsService;
|
|
6380
6477
|
exports.OperationsService = OperationsService = OperationsService_1 = __decorate([
|