@platform-modules/civil-aviation-authority 2.3.265 → 2.3.267

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.env +7 -22
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1 -0
  4. package/dist/models/AnnualIncrementRequestEmployeeModel.d.ts +2 -1
  5. package/dist/models/AnnualIncrementRequestEmployeeModel.js +7 -2
  6. package/dist/models/AnnualIncrementRequestModel.d.ts +2 -1
  7. package/dist/models/AnnualIncrementRequestModel.js +7 -2
  8. package/dist/models/EmployeeSecurityDriveOwnerModel.d.ts +13 -0
  9. package/dist/models/EmployeeSecurityDriveOwnerModel.js +54 -0
  10. package/dist/models/ITApprovalSettings.d.ts +7 -0
  11. package/dist/models/ITApprovalSettings.js +40 -0
  12. package/dist/models/ITServicesTypesMuscatModel.d.ts +6 -0
  13. package/dist/models/ITServicesTypesMuscatModel.js +34 -0
  14. package/dist/models/ITServicesTypesSalalahModel.d.ts +6 -0
  15. package/dist/models/ITServicesTypesSalalahModel.js +34 -0
  16. package/dist/models/SlaApprovalsViewModel.js +47 -47
  17. package/dist/models/SlaMyRequestsViewModel.js +50 -50
  18. package/dist/models/Workflows.d.ts +9 -0
  19. package/dist/models/Workflows.js +31 -0
  20. package/package.json +1 -1
  21. package/scripts/sync-sla-reports-sql.js +98 -94
  22. package/sql/README.md +21 -25
  23. package/sql/sla-reports-sync.manifest.json +7 -7
  24. package/sql/sla_reports_admin_procedures.sql +283 -283
  25. package/sql/sla_reports_approvals_workbook.sql +383 -383
  26. package/sql/sla_reports_procedures.sql +874 -874
  27. package/sql/vw_sla_approvals.sql +108 -108
  28. package/sql/vw_sla_my_requests.sql +53 -53
  29. package/src/data-source.ts +517 -517
  30. package/src/index.ts +496 -495
  31. package/src/models/AccessCardRequestModel.ts +135 -135
  32. package/src/models/AccommodationApprovalModel.ts +8 -8
  33. package/src/models/AccommodationRequestModel.ts +8 -8
  34. package/src/models/AirportEntryPermitModel.ts +276 -276
  35. package/src/models/AnnualIncrementRequestEmployeeModel.ts +6 -1
  36. package/src/models/AnnualIncrementRequestModel.ts +6 -1
  37. package/src/models/AnnualTrainingPlanRequestModel.ts +153 -153
  38. package/src/models/AppealAgainstAdministrativeDecisionRequestModel.ts +23 -23
  39. package/src/models/CAAServices.ts +33 -33
  40. package/src/models/CAASubServices.ts +33 -33
  41. package/src/models/CAIRatingMasterModel.ts +39 -39
  42. package/src/models/CSRMBusinessImpactRatingMasterModel.ts +25 -25
  43. package/src/models/CSRMLikelihoodMasterModel.ts +25 -25
  44. package/src/models/CashAllowanceLeaveRequestModel.ts +11 -11
  45. package/src/models/DepartmentsModel.ts +25 -25
  46. package/src/models/DocumentDriveModel.ts +28 -28
  47. package/src/models/DocumentFolderModel.ts +45 -45
  48. package/src/models/EmployeeSecurityDriveOwnerModel.ts +32 -0
  49. package/src/models/HotelApprovalModel.ts +83 -83
  50. package/src/models/HousingContractCancelApprovalModel.ts +64 -64
  51. package/src/models/HousingContractCancelChatModel.ts +56 -56
  52. package/src/models/HousingContractRenewalApprovalModel.ts +64 -64
  53. package/src/models/HousingContractRenewalChatModel.ts +59 -59
  54. package/src/models/HrServiceRequestModel.ts +193 -193
  55. package/src/models/ITRequestAttachmentModel.ts +73 -73
  56. package/src/models/ITRequestChatModel.ts +74 -74
  57. package/src/models/ItApprovalsModel.ts +84 -84
  58. package/src/models/ItWorkflowModel.ts +55 -55
  59. package/src/models/LegalConsultationApprovalModel.ts +65 -65
  60. package/src/models/MissionTravelPassportExpiryNotificationConfigModel.ts +36 -36
  61. package/src/models/NotificationModel.ts +89 -89
  62. package/src/models/PerformanceCyclePeriodModel.ts +26 -26
  63. package/src/models/PerformanceGoalMasterModel.ts +27 -27
  64. package/src/models/PerformanceManagementRequestGoalModel.ts +46 -46
  65. package/src/models/PerformanceManagementRequestModel.ts +14 -14
  66. package/src/models/PromotionRequestModel.ts +11 -11
  67. package/src/models/ResidentialUnitRentalApprovalModel.ts +143 -143
  68. package/src/models/ResidentialUnitRentalChatModel.ts +56 -56
  69. package/src/models/ResidentialUnitRentalRequestModel.ts +218 -218
  70. package/src/models/RespondToEnquiriesRequestModel.ts +31 -31
  71. package/src/models/ServiceExtensionAfter60ApprovalModel.ts +87 -87
  72. package/src/models/ServiceSlaApprovalModel.ts +64 -64
  73. package/src/models/ServicesNotificationConfigModel.ts +55 -55
  74. package/src/models/SkillsEnhancementRequestModel.ts +17 -17
  75. package/src/models/SlaApprovalsViewModel.ts +135 -135
  76. package/src/models/SlaMyRequestsViewModel.ts +170 -170
  77. package/src/models/SlaRequestModel.ts +65 -65
  78. package/src/models/StudyLeaveRequestModel.ts +144 -144
  79. package/src/models/TrainingRequestModel.ts +164 -164
  80. package/src/models/TrainingRoomBookingRequestModel.ts +142 -142
  81. package/src/models/TrainingRoomNotificationConfigModel.ts +30 -30
  82. package/src/models/UserSkillModel.ts +56 -56
  83. package/src/models/role.ts +34 -34
  84. package/src/models/user.ts +233 -233
  85. package/src/sla/sla-table-sync.service.ts +90 -90
  86. package/dist/models/DocumentMetadataModel.d.ts +0 -45
  87. package/dist/models/DocumentMetadataModel.js +0 -171
  88. package/dist/models/DocumentationDepartmentsModel.d.ts +0 -13
  89. package/dist/models/DocumentationDepartmentsModel.js +0 -53
  90. package/dist/models/FolderModel.d.ts +0 -16
  91. package/dist/models/FolderModel.js +0 -85
  92. package/dist/models/ImportExportMaterialModels.d.ts +0 -92
  93. package/dist/models/ImportExportMaterialModels.js +0 -307
  94. package/dist/models/PermissionModel.d.ts +0 -18
  95. package/dist/models/PermissionModel.js +0 -68
  96. package/dist/models/SecurityAccessApprovalModel.d.ts +0 -23
  97. package/dist/models/SecurityAccessApprovalModel.js +0 -82
  98. package/dist/models/SecurityAccessAttachmentModel.d.ts +0 -12
  99. package/dist/models/SecurityAccessAttachmentModel.js +0 -56
  100. package/dist/models/SecurityAccessChatModel.d.ts +0 -12
  101. package/dist/models/SecurityAccessChatModel.js +0 -56
  102. package/dist/models/SecurityAccessRequestModel.d.ts +0 -25
  103. package/dist/models/SecurityAccessRequestModel.js +0 -80
  104. package/dist/models/SecurityAccessWorkflowModel.d.ts +0 -24
  105. package/dist/models/SecurityAccessWorkflowModel.js +0 -84
  106. package/dist/models/ServiceExtensionAfterAge60Models.d.ts +0 -93
  107. package/dist/models/ServiceExtensionAfterAge60Models.js +0 -312
  108. package/dist/models/UUIDBaseModel.d.ts +0 -14
  109. package/dist/models/UUIDBaseModel.js +0 -66
  110. package/dist/models/WorkingHoursExtensionModels.d.ts +0 -88
  111. package/dist/models/WorkingHoursExtensionModels.js +0 -295
@@ -1,874 +1,874 @@
1
- -- SLA Reports stored procedures (Reports_Service)
2
- -- Applied via: npm run sync:sla-sql (shared_models/scripts/sync-sla-reports-sql.js)
3
- -- Or: psql -U <user> -d <database> -f shared_models/sql/sla_reports_procedures.sql
4
-
5
- -- ---------------------------------------------------------------------------
6
- -- Helper: approver row matches user (same semantics as User_Service SLA dashboard)
7
- -- ---------------------------------------------------------------------------
8
- CREATE OR REPLACE FUNCTION sla_approval_matches_user(p_sa_alias TEXT, p_user_id INT)
9
- RETURNS TEXT
10
- LANGUAGE sql
11
- IMMUTABLE
12
- AS $$
13
- SELECT format('(
14
- %1$s.approver_user_id = %2$s
15
- OR %1$s.delegate_user_id = %2$s
16
- OR (
17
- %1$s.approver_user_id IS NULL
18
- AND %1$s.delegate_user_id IS NULL
19
- AND %1$s.approver_role_id IS NOT NULL
20
- AND %1$s.department_id IS NOT NULL
21
- AND EXISTS (
22
- SELECT 1 FROM user_role ur
23
- WHERE ur.user_id = %2$s
24
- AND ur.role_id = %1$s.approver_role_id
25
- AND ur.department_id = %1$s.department_id
26
- AND (
27
- %1$s.section_id IS NULL
28
- OR (%1$s.section_id IS NOT NULL AND (ur.section_id = %1$s.section_id OR ur.section_id IS NULL))
29
- )
30
- AND ur.is_deleted = false AND ur.is_active = true
31
- )
32
- )
33
- OR (
34
- %1$s.approver_user_id IS NULL
35
- AND %1$s.delegate_user_id IS NULL
36
- AND %1$s.approver_role_id IS NULL
37
- AND %1$s.department_id IS NOT NULL
38
- AND %1$s.section_id IS NOT NULL
39
- AND EXISTS (
40
- SELECT 1 FROM users u
41
- WHERE u.id = %2$s AND COALESCE(u.is_deleted, false) = false
42
- AND u.department = %1$s.department_id AND u.section = %1$s.section_id
43
- )
44
- )
45
- OR (
46
- %1$s.approver_user_id IS NULL
47
- AND %1$s.delegate_user_id IS NULL
48
- AND %1$s.approver_role_id IS NULL
49
- AND %1$s.department_id IS NOT NULL
50
- AND %1$s.section_id IS NULL
51
- AND EXISTS (
52
- SELECT 1 FROM users u
53
- WHERE u.id = %2$s AND COALESCE(u.is_deleted, false) = false
54
- AND u.department = %1$s.department_id
55
- )
56
- )
57
- )', p_sa_alias, p_user_id);
58
- $$;
59
-
60
- -- ---------------------------------------------------------------------------
61
- -- MY REQUESTS — paginated view
62
- -- ---------------------------------------------------------------------------
63
- CREATE OR REPLACE FUNCTION sp_sla_my_requests_view(
64
- p_user_id INT,
65
- p_service_ids INT[] DEFAULT NULL,
66
- p_sub_service_ids INT[] DEFAULT NULL,
67
- p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
68
- p_from_date DATE DEFAULT NULL,
69
- p_to_date DATE DEFAULT NULL,
70
- p_search_text TEXT DEFAULT NULL,
71
- p_page INT DEFAULT 1,
72
- p_limit INT DEFAULT 50,
73
- p_sort_by TEXT DEFAULT 'created_at',
74
- p_sort_order TEXT DEFAULT 'DESC'
75
- )
76
- RETURNS TABLE (
77
- sla_request_id INT,
78
- request_id INT,
79
- service_id INT,
80
- sub_service_id INT,
81
- service_name TEXT,
82
- sub_service_name TEXT,
83
- status TEXT,
84
- user_id INT,
85
- user_name TEXT,
86
- workflow_execution_id TEXT,
87
- created_at TIMESTAMPTZ,
88
- updated_at TIMESTAMPTZ,
89
- total_count BIGINT
90
- )
91
- LANGUAGE plpgsql
92
- STABLE
93
- AS $$
94
- DECLARE
95
- v_offset INT;
96
- v_limit INT;
97
- v_sort_col TEXT;
98
- v_sort_dir TEXT;
99
- v_sql TEXT;
100
- BEGIN
101
- v_limit := LEAST(GREATEST(COALESCE(p_limit, 50), 1), 200);
102
- v_offset := GREATEST(COALESCE(p_page, 1) - 1, 0) * v_limit;
103
-
104
- v_sort_col := CASE lower(trim(p_sort_by))
105
- WHEN 'updated_at' THEN 'sr.updated_at'
106
- WHEN 'id' THEN 'sr.id'
107
- WHEN 'status' THEN 'sr.status'
108
- WHEN 'request_id' THEN 'sr.request_id'
109
- ELSE 'sr.created_at'
110
- END;
111
-
112
- v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
113
-
114
- v_sql := format($q$
115
- SELECT
116
- sr.id::INT,
117
- sr.request_id::INT,
118
- sr.service_id::INT,
119
- sr.sub_service_id::INT,
120
- svc.name::TEXT,
121
- subsvc.sub_service_name::TEXT,
122
- sr.status::TEXT,
123
- sr.user_id::INT,
124
- TRIM(COALESCE(u.employee_name, ''))::TEXT,
125
- sr.workflow_execution_id::TEXT,
126
- sr.created_at,
127
- sr.updated_at,
128
- COUNT(*) OVER()::BIGINT
129
- FROM sla_requests sr
130
- LEFT JOIN caa_services svc ON svc.id = sr.service_id
131
- LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
132
- LEFT JOIN users u ON u.id = sr.user_id
133
- WHERE sr.is_deleted = false
134
- AND sr.user_id = %1$s
135
- AND sr.status::TEXT = ANY($1)
136
- AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
137
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
138
- AND ($4 IS NULL OR sr.created_at::date >= $4)
139
- AND ($5 IS NULL OR sr.created_at::date <= $5)
140
- AND ($6 IS NULL OR trim($6) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
141
- ORDER BY %2$s %3$s
142
- LIMIT %4$s OFFSET %5$s
143
- $q$, p_user_id, v_sort_col, v_sort_dir, v_limit, v_offset);
144
-
145
- RETURN QUERY EXECUTE v_sql
146
- USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text;
147
- END;
148
- $$;
149
-
150
- -- ---------------------------------------------------------------------------
151
- -- MY REQUESTS — full export (download)
152
- -- ---------------------------------------------------------------------------
153
- CREATE OR REPLACE FUNCTION sp_sla_my_requests_download(
154
- p_user_id INT,
155
- p_service_ids INT[] DEFAULT NULL,
156
- p_sub_service_ids INT[] DEFAULT NULL,
157
- p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
158
- p_from_date DATE DEFAULT NULL,
159
- p_to_date DATE DEFAULT NULL,
160
- p_search_text TEXT DEFAULT NULL,
161
- p_sort_by TEXT DEFAULT 'created_at',
162
- p_sort_order TEXT DEFAULT 'DESC'
163
- )
164
- RETURNS TABLE (
165
- sla_request_id INT,
166
- request_id INT,
167
- service_id INT,
168
- sub_service_id INT,
169
- service_name TEXT,
170
- sub_service_name TEXT,
171
- status TEXT,
172
- user_id INT,
173
- user_name TEXT,
174
- workflow_execution_id TEXT,
175
- created_at TIMESTAMPTZ,
176
- updated_at TIMESTAMPTZ
177
- )
178
- LANGUAGE plpgsql
179
- STABLE
180
- AS $$
181
- DECLARE
182
- v_sort_col TEXT;
183
- v_sort_dir TEXT;
184
- v_sql TEXT;
185
- BEGIN
186
- v_sort_col := CASE lower(trim(p_sort_by))
187
- WHEN 'updated_at' THEN 'sr.updated_at'
188
- WHEN 'id' THEN 'sr.id'
189
- WHEN 'status' THEN 'sr.status'
190
- WHEN 'request_id' THEN 'sr.request_id'
191
- ELSE 'sr.created_at'
192
- END;
193
- v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
194
-
195
- v_sql := format($q$
196
- SELECT
197
- sr.id::INT,
198
- sr.request_id::INT,
199
- sr.service_id::INT,
200
- sr.sub_service_id::INT,
201
- svc.name::TEXT,
202
- subsvc.sub_service_name::TEXT,
203
- sr.status::TEXT,
204
- sr.user_id::INT,
205
- TRIM(COALESCE(u.employee_name, ''))::TEXT,
206
- sr.workflow_execution_id::TEXT,
207
- sr.created_at,
208
- sr.updated_at
209
- FROM sla_requests sr
210
- LEFT JOIN caa_services svc ON svc.id = sr.service_id
211
- LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
212
- LEFT JOIN users u ON u.id = sr.user_id
213
- WHERE sr.is_deleted = false
214
- AND sr.user_id = %1$s
215
- AND sr.status::TEXT = ANY($1)
216
- AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
217
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
218
- AND ($4 IS NULL OR sr.created_at::date >= $4)
219
- AND ($5 IS NULL OR sr.created_at::date <= $5)
220
- AND ($6 IS NULL OR trim($6) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
221
- ORDER BY %2$s %3$s
222
- $q$, p_user_id, v_sort_col, v_sort_dir);
223
-
224
- RETURN QUERY EXECUTE v_sql
225
- USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text;
226
- END;
227
- $$;
228
-
229
- -- ---------------------------------------------------------------------------
230
- -- APPROVALS — paginated view
231
- -- ---------------------------------------------------------------------------
232
- DROP FUNCTION IF EXISTS sp_sla_approvals_view(
233
- INT,
234
- INT[],
235
- INT[],
236
- TEXT[],
237
- TEXT[],
238
- DATE,
239
- DATE,
240
- TEXT,
241
- INT,
242
- INT,
243
- TEXT,
244
- TEXT
245
- );
246
- CREATE OR REPLACE FUNCTION sp_sla_approvals_view(
247
- p_approver_user_id INT,
248
- p_service_ids INT[] DEFAULT NULL,
249
- p_sub_service_ids INT[] DEFAULT NULL,
250
- p_request_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
251
- p_approval_statuses TEXT[] DEFAULT ARRAY['In Progress','Approved','Rejected'],
252
- p_from_date DATE DEFAULT NULL,
253
- p_to_date DATE DEFAULT NULL,
254
- p_search_text TEXT DEFAULT NULL,
255
- p_page INT DEFAULT 1,
256
- p_limit INT DEFAULT 50,
257
- p_sort_by TEXT DEFAULT 'created_at',
258
- p_sort_order TEXT DEFAULT 'DESC'
259
- )
260
- RETURNS TABLE (
261
- sla_approval_id INT,
262
- source_approval_id INT,
263
- request_id INT,
264
- service_id INT,
265
- sub_service_id INT,
266
- service_name TEXT,
267
- sub_service_name TEXT,
268
- department_id INT,
269
- department_name TEXT,
270
- section_id INT,
271
- section_name TEXT,
272
- role_id INT,
273
- role_name TEXT,
274
- approval_status TEXT,
275
- request_status TEXT,
276
- level INT,
277
- approver_user_id INT,
278
- approver_user_name TEXT,
279
- delegate_user_id INT,
280
- delegate_user_name TEXT,
281
- approved_by INT,
282
- approved_by_name TEXT,
283
- comment TEXT,
284
- created_at TIMESTAMPTZ,
285
- updated_at TIMESTAMPTZ,
286
- total_count BIGINT
287
- )
288
- LANGUAGE plpgsql
289
- STABLE
290
- AS $$
291
- DECLARE
292
- v_offset INT;
293
- v_limit INT;
294
- v_sort_col TEXT;
295
- v_sort_dir TEXT;
296
- v_match TEXT;
297
- v_sql TEXT;
298
- BEGIN
299
- v_limit := LEAST(GREATEST(COALESCE(p_limit, 50), 1), 200);
300
- v_offset := GREATEST(COALESCE(p_page, 1) - 1, 0) * v_limit;
301
- v_match := sla_approval_matches_user('sa', p_approver_user_id);
302
-
303
- v_sort_col := CASE lower(trim(p_sort_by))
304
- WHEN 'updated_at' THEN 'sa.updated_at'
305
- WHEN 'id' THEN 'sa.id'
306
- WHEN 'approval_status' THEN 'sa.approval_status'
307
- WHEN 'level' THEN 'sa.level'
308
- WHEN 'request_id' THEN 'sa.request_id'
309
- ELSE 'sa.created_at'
310
- END;
311
- v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
312
-
313
- v_sql := format($q$
314
- SELECT
315
- sa.id::INT,
316
- sa.source_approval_id::INT,
317
- sa.request_id::INT,
318
- sa.service_id::INT,
319
- sa.sub_service_id::INT,
320
- svc.name::TEXT,
321
- subsvc.sub_service_name::TEXT,
322
- sa.department_id::INT,
323
- TRIM(COALESCE(dept.department_name, ''))::TEXT,
324
- sa.section_id::INT,
325
- TRIM(COALESCE(sec.section_name, ''))::TEXT,
326
- sa.approver_role_id::INT,
327
- TRIM(COALESCE(r.name, ''))::TEXT,
328
- sa.approval_status::TEXT,
329
- sr.status::TEXT,
330
- sa.level::INT,
331
- sa.approver_user_id::INT,
332
- TRIM(COALESCE(au.employee_name, ''))::TEXT,
333
- sa.delegate_user_id::INT,
334
- TRIM(COALESCE(du.employee_name, ''))::TEXT,
335
- sa.approved_by::INT,
336
- TRIM(COALESCE(ab.employee_name, ''))::TEXT,
337
- sa.comment::TEXT,
338
- sa.created_at,
339
- sa.updated_at,
340
- COUNT(*) OVER()::BIGINT
341
- FROM sla_approval sa
342
- INNER JOIN sla_requests sr
343
- ON sr.request_id = sa.request_id AND sr.is_deleted = false
344
- LEFT JOIN caa_services svc ON svc.id = sa.service_id
345
- LEFT JOIN caa_sub_services subsvc ON subsvc.id = sa.sub_service_id
346
- LEFT JOIN departments dept ON dept.id = sa.department_id AND COALESCE(dept.is_deleted, false) = false
347
- LEFT JOIN sections sec ON sec.id = sa.section_id AND COALESCE(sec.is_deleted, false) = false
348
- LEFT JOIN role r ON r.id = sa.approver_role_id AND COALESCE(r.is_deleted, false) = false
349
- LEFT JOIN users au ON au.id = sa.approver_user_id
350
- LEFT JOIN users du ON du.id = sa.delegate_user_id
351
- LEFT JOIN users ab ON ab.id = sa.approved_by
352
- WHERE sa.is_deleted = false
353
- AND sr.user_id != %1$s
354
- AND (sa.service_id = sr.service_id OR sa.service_id IS NULL)
355
- AND (sa.sub_service_id = sr.sub_service_id OR sa.sub_service_id IS NULL)
356
- AND %2$s
357
- AND (sa.approval_status = ANY($1) OR sa.approval_status IS NULL)
358
- AND sr.status::TEXT = ANY($2)
359
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.service_id = ANY($3))
360
- AND ($4 IS NULL OR cardinality($4) = 0 OR sr.sub_service_id = ANY($4))
361
- AND ($5 IS NULL OR sr.created_at::date >= $5)
362
- AND ($6 IS NULL OR sr.created_at::date <= $6)
363
- AND ($7 IS NULL OR trim($7) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($7) || '%%')
364
- ORDER BY %3$s %4$s, sa.id DESC
365
- LIMIT %5$s OFFSET %6$s
366
- $q$, p_approver_user_id, v_match, v_sort_col, v_sort_dir, v_limit, v_offset);
367
-
368
- RETURN QUERY EXECUTE v_sql
369
- USING p_approval_statuses, p_request_statuses, p_service_ids, p_sub_service_ids,
370
- p_from_date, p_to_date, p_search_text;
371
- END;
372
- $$;
373
-
374
- -- ---------------------------------------------------------------------------
375
- -- APPROVALS — full export (download)
376
- -- ---------------------------------------------------------------------------
377
- CREATE OR REPLACE FUNCTION sp_sla_approvals_download(
378
- p_approver_user_id INT,
379
- p_service_ids INT[] DEFAULT NULL,
380
- p_sub_service_ids INT[] DEFAULT NULL,
381
- p_request_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
382
- p_approval_statuses TEXT[] DEFAULT ARRAY['In Progress','Approved','Rejected'],
383
- p_from_date DATE DEFAULT NULL,
384
- p_to_date DATE DEFAULT NULL,
385
- p_search_text TEXT DEFAULT NULL,
386
- p_sort_by TEXT DEFAULT 'created_at',
387
- p_sort_order TEXT DEFAULT 'DESC'
388
- )
389
- RETURNS TABLE (
390
- sla_approval_id INT,
391
- source_approval_id INT,
392
- request_id INT,
393
- service_id INT,
394
- sub_service_id INT,
395
- service_name TEXT,
396
- sub_service_name TEXT,
397
- approval_status TEXT,
398
- request_status TEXT,
399
- level INT,
400
- approver_user_id INT,
401
- approver_user_name TEXT,
402
- delegate_user_id INT,
403
- delegate_user_name TEXT,
404
- approved_by INT,
405
- approved_by_name TEXT,
406
- comment TEXT,
407
- created_at TIMESTAMPTZ,
408
- updated_at TIMESTAMPTZ
409
- )
410
- LANGUAGE plpgsql
411
- STABLE
412
- AS $$
413
- DECLARE
414
- v_sort_col TEXT;
415
- v_sort_dir TEXT;
416
- v_match TEXT;
417
- v_sql TEXT;
418
- BEGIN
419
- v_match := sla_approval_matches_user('sa', p_approver_user_id);
420
-
421
- v_sort_col := CASE lower(trim(p_sort_by))
422
- WHEN 'updated_at' THEN 'sa.updated_at'
423
- WHEN 'id' THEN 'sa.id'
424
- WHEN 'approval_status' THEN 'sa.approval_status'
425
- WHEN 'level' THEN 'sa.level'
426
- WHEN 'request_id' THEN 'sa.request_id'
427
- ELSE 'sa.created_at'
428
- END;
429
- v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
430
-
431
- v_sql := format($q$
432
- SELECT
433
- sa.id::INT,
434
- sa.source_approval_id::INT,
435
- sa.request_id::INT,
436
- sa.service_id::INT,
437
- sa.sub_service_id::INT,
438
- svc.name::TEXT,
439
- subsvc.sub_service_name::TEXT,
440
- sa.approval_status::TEXT,
441
- sr.status::TEXT,
442
- sa.level::INT,
443
- sa.approver_user_id::INT,
444
- TRIM(COALESCE(au.employee_name, ''))::TEXT,
445
- sa.delegate_user_id::INT,
446
- TRIM(COALESCE(du.employee_name, ''))::TEXT,
447
- sa.approved_by::INT,
448
- TRIM(COALESCE(ab.employee_name, ''))::TEXT,
449
- sa.comment::TEXT,
450
- sa.created_at,
451
- sa.updated_at
452
- FROM sla_approval sa
453
- INNER JOIN sla_requests sr
454
- ON sr.request_id = sa.request_id AND sr.is_deleted = false
455
- LEFT JOIN caa_services svc ON svc.id = sa.service_id
456
- LEFT JOIN caa_sub_services subsvc ON subsvc.id = sa.sub_service_id
457
- LEFT JOIN users au ON au.id = sa.approver_user_id
458
- LEFT JOIN users du ON du.id = sa.delegate_user_id
459
- LEFT JOIN users ab ON ab.id = sa.approved_by
460
- WHERE sa.is_deleted = false
461
- AND sr.user_id != %1$s
462
- AND (sa.service_id = sr.service_id OR sa.service_id IS NULL)
463
- AND (sa.sub_service_id = sr.sub_service_id OR sa.sub_service_id IS NULL)
464
- AND %2$s
465
- AND (sa.approval_status = ANY($1) OR sa.approval_status IS NULL)
466
- AND sr.status::TEXT = ANY($2)
467
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.service_id = ANY($3))
468
- AND ($4 IS NULL OR cardinality($4) = 0 OR sr.sub_service_id = ANY($4))
469
- AND ($5 IS NULL OR sr.created_at::date >= $5)
470
- AND ($6 IS NULL OR sr.created_at::date <= $6)
471
- AND ($7 IS NULL OR trim($7) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($7) || '%%')
472
- ORDER BY %3$s %4$s, sa.id DESC
473
- $q$, p_approver_user_id, v_match, v_sort_col, v_sort_dir);
474
-
475
- RETURN QUERY EXECUTE v_sql
476
- USING p_approval_statuses, p_request_statuses, p_service_ids, p_sub_service_ids,
477
- p_from_date, p_to_date, p_search_text;
478
- END;
479
- $$;
480
-
481
- -- ---------------------------------------------------------------------------
482
- -- Helpers: Excel export (my requests)
483
- -- ---------------------------------------------------------------------------
484
- CREATE OR REPLACE FUNCTION sla_excel_header_label(p_key TEXT)
485
- RETURNS TEXT
486
- LANGUAGE sql
487
- IMMUTABLE
488
- AS $$
489
- SELECT trim(
490
- initcap(
491
- regexp_replace(
492
- regexp_replace(COALESCE(p_key, ''), '[_-]+', ' ', 'g'),
493
- '\s+',
494
- ' ',
495
- 'g'
496
- )
497
- )
498
- );
499
- $$;
500
-
501
- CREATE OR REPLACE FUNCTION sla_request_field_cell(p_fields JSONB, p_key TEXT)
502
- RETURNS TEXT
503
- LANGUAGE sql
504
- IMMUTABLE
505
- AS $$
506
- SELECT CASE
507
- WHEN p_fields IS NULL OR NOT (p_fields ? p_key) THEN NULL
508
- WHEN jsonb_typeof(p_fields -> p_key) IN ('object', 'array') THEN (p_fields -> p_key)::TEXT
509
- ELSE p_fields ->> p_key
510
- END;
511
- $$;
512
-
513
- CREATE OR REPLACE FUNCTION sla_excel_sheet_name(p_sub_service_name TEXT)
514
- RETURNS TEXT
515
- LANGUAGE sql
516
- IMMUTABLE
517
- AS $$
518
- SELECT LEFT(
519
- CASE
520
- WHEN COALESCE(trim(p_sub_service_name), '') = '' THEN 'Requests'
521
- ELSE trim(p_sub_service_name) || ' Requests'
522
- END,
523
- 31
524
- );
525
- $$;
526
-
527
- DROP FUNCTION IF EXISTS sla_excel_approvals_sheet_name(TEXT);
528
-
529
- CREATE OR REPLACE FUNCTION sla_excel_approvals_sheet_name(p_sub_service_name TEXT)
530
- RETURNS TEXT
531
- LANGUAGE sql
532
- IMMUTABLE
533
- AS $$
534
- SELECT LEFT(
535
- CASE
536
- WHEN COALESCE(trim(p_sub_service_name), '') = '' THEN 'Approvals'
537
- ELSE trim(p_sub_service_name) || ' Approvals'
538
- END,
539
- 31
540
- );
541
- $$;
542
-
543
- -- ---------------------------------------------------------------------------
544
- -- MY REQUESTS — Approvals sheet (vw_sla_approvals joined to filtered my-requests)
545
- -- p_user_scope_sql: e.g. 'v.user_id = 5' or 'TRUE' (admin all users)
546
- -- ---------------------------------------------------------------------------
547
- DROP FUNCTION IF EXISTS sla_my_requests_approvals_excel_sheet(TEXT, TEXT[], INT[], INT[], DATE, DATE, TEXT, INT);
548
-
549
- CREATE OR REPLACE FUNCTION sla_my_requests_approvals_excel_sheet(
550
- p_user_scope_sql TEXT,
551
- p_statuses TEXT[],
552
- p_service_ids INT[],
553
- p_sub_service_ids INT[],
554
- p_from_date DATE,
555
- p_to_date DATE,
556
- p_search_text TEXT,
557
- p_start_sheet_order INT
558
- )
559
- RETURNS TABLE (
560
- sheet_order INT,
561
- sub_service_id INT,
562
- sub_service_name TEXT,
563
- sheet_name TEXT,
564
- headers JSONB,
565
- rows JSONB
566
- )
567
- LANGUAGE plpgsql
568
- STABLE
569
- AS $$
570
- DECLARE
571
- v_sub RECORD;
572
- v_approval RECORD;
573
- v_sheet_order INT;
574
- v_sheet_label TEXT;
575
- v_headers_labels TEXT[] := ARRAY[
576
- 'Request ID', 'Service ID', 'Service Name', 'Sub Service ID', 'Sub Service Name',
577
- 'SLA Approval ID', 'Source Approval ID', 'Level', 'Approval Status', 'Request Status',
578
- 'Approval Role ID', 'Approval Role Name', 'Approval Department ID', 'Approval Department Name',
579
- 'Approval Section ID', 'Approval Section Name',
580
- 'Approver User ID', 'Approver User Name', 'Delegate User ID', 'Delegate User Name',
581
- 'Approved By ID', 'Approved By Name', 'Comment', 'Created At', 'Updated At'
582
- ];
583
- v_cells TEXT[];
584
- v_all_rows JSONB;
585
- BEGIN
586
- v_sheet_order := p_start_sheet_order;
587
-
588
- FOR v_sub IN
589
- EXECUTE format($q$
590
- SELECT DISTINCT
591
- COALESCE(sr.sub_service_id, 0) AS sub_service_id_num,
592
- COALESCE(NULLIF(trim(subsvc.sub_service_name), ''), NULLIF(trim(v.sub_service_name), ''), 'Uncategorized') AS sub_service_name
593
- FROM vw_sla_my_requests v
594
- INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
595
- LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
596
- WHERE %s
597
- AND v.status = ANY($1)
598
- AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
599
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
600
- AND ($4 IS NULL OR v.created_at::date >= $4)
601
- AND ($5 IS NULL OR v.created_at::date <= $5)
602
- AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
603
- ORDER BY sub_service_name ASC
604
- $q$, p_user_scope_sql)
605
- USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
606
- LOOP
607
- v_sheet_order := v_sheet_order + 1;
608
- v_sheet_label := sla_excel_approvals_sheet_name(v_sub.sub_service_name);
609
- v_all_rows := '[]'::jsonb;
610
-
611
- FOR v_approval IN
612
- EXECUTE format($q$
613
- SELECT
614
- va.request_id,
615
- sr.service_id,
616
- va.service_name,
617
- sr.sub_service_id,
618
- va.sub_service_name,
619
- va.sla_approval_id,
620
- va.source_approval_id,
621
- va.level,
622
- va.approval_status,
623
- va.request_status,
624
- va.approval_role_id,
625
- va.approval_role_name,
626
- va.approval_department_id,
627
- va.approval_department_name,
628
- va.approval_section_id,
629
- va.approval_section_name,
630
- va.approver_user_id,
631
- va.approver_user_name,
632
- va.delegate_user_id,
633
- va.delegate_user_name,
634
- va.approved_by,
635
- va.approved_by_name,
636
- va.comment,
637
- va.created_at,
638
- va.updated_at
639
- FROM vw_sla_my_requests v
640
- INNER JOIN sla_requests sr
641
- ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
642
- INNER JOIN vw_sla_approvals va
643
- ON va.request_id = v.request_id
644
- AND (va.service_id = sr.service_id OR va.service_id IS NULL)
645
- AND (va.sub_service_id = sr.sub_service_id OR va.sub_service_id IS NULL)
646
- WHERE %s
647
- AND v.status = ANY($1)
648
- AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
649
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
650
- AND ($4 IS NULL OR v.created_at::date >= $4)
651
- AND ($5 IS NULL OR v.created_at::date <= $5)
652
- AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
653
- AND COALESCE(sr.sub_service_id, 0) = %2$s
654
- ORDER BY va.request_id ASC, va.level ASC NULLS LAST, va.sla_approval_id ASC
655
- $q$, p_user_scope_sql, v_sub.sub_service_id_num)
656
- USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
657
- LOOP
658
- v_cells := ARRAY[
659
- COALESCE(v_approval.request_id::TEXT, ''),
660
- COALESCE(v_approval.service_id::TEXT, ''),
661
- COALESCE(v_approval.service_name, ''),
662
- COALESCE(v_approval.sub_service_id::TEXT, ''),
663
- COALESCE(v_approval.sub_service_name, ''),
664
- COALESCE(v_approval.sla_approval_id::TEXT, ''),
665
- COALESCE(v_approval.source_approval_id::TEXT, ''),
666
- COALESCE(v_approval.level::TEXT, ''),
667
- COALESCE(v_approval.approval_status, ''),
668
- COALESCE(v_approval.request_status, ''),
669
- COALESCE(v_approval.approval_role_id::TEXT, ''),
670
- COALESCE(v_approval.approval_role_name, ''),
671
- COALESCE(v_approval.approval_department_id::TEXT, ''),
672
- COALESCE(v_approval.approval_department_name, ''),
673
- COALESCE(v_approval.approval_section_id::TEXT, ''),
674
- COALESCE(v_approval.approval_section_name, ''),
675
- COALESCE(v_approval.approver_user_id::TEXT, ''),
676
- COALESCE(v_approval.approver_user_name, ''),
677
- COALESCE(v_approval.delegate_user_id::TEXT, ''),
678
- COALESCE(v_approval.delegate_user_name, ''),
679
- COALESCE(v_approval.approved_by::TEXT, ''),
680
- COALESCE(v_approval.approved_by_name, ''),
681
- COALESCE(v_approval.comment, ''),
682
- COALESCE(to_char(v_approval.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS'), ''),
683
- COALESCE(to_char(v_approval.updated_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS'), '')
684
- ];
685
- v_all_rows := v_all_rows || jsonb_build_array(to_jsonb(v_cells));
686
- END LOOP;
687
-
688
- sheet_order := v_sheet_order;
689
- sub_service_id := NULLIF(v_sub.sub_service_id_num, 0);
690
- sub_service_name := v_sub.sub_service_name;
691
- sheet_name := v_sheet_label;
692
- headers := to_jsonb(v_headers_labels);
693
- rows := v_all_rows;
694
- RETURN NEXT;
695
- END LOOP;
696
- END;
697
- $$;
698
-
699
- -- ---------------------------------------------------------------------------
700
- -- MY REQUESTS — Excel workbook payload (one result row per sub-service sheet)
701
- -- Uses vw_sla_my_requests; flattening and dynamic columns are built in the database.
702
- -- ---------------------------------------------------------------------------
703
- CREATE OR REPLACE FUNCTION sp_sla_my_requests_excel_export(
704
- p_user_id INT,
705
- p_service_ids INT[] DEFAULT NULL,
706
- p_sub_service_ids INT[] DEFAULT NULL,
707
- p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
708
- p_from_date DATE DEFAULT NULL,
709
- p_to_date DATE DEFAULT NULL,
710
- p_search_text TEXT DEFAULT NULL,
711
- p_sort_by TEXT DEFAULT 'created_at',
712
- p_sort_order TEXT DEFAULT 'DESC'
713
- )
714
- RETURNS TABLE (
715
- sheet_order INT,
716
- sub_service_id INT,
717
- sub_service_name TEXT,
718
- sheet_name TEXT,
719
- headers JSONB,
720
- rows JSONB
721
- )
722
- LANGUAGE plpgsql
723
- STABLE
724
- AS $$
725
- DECLARE
726
- v_sort_col TEXT;
727
- v_sort_dir TEXT;
728
- v_sub RECORD;
729
- v_row RECORD;
730
- v_dyn_keys TEXT[];
731
- v_dyn_labels TEXT[];
732
- v_common_labels TEXT[] := ARRAY[
733
- 'SLA Request ID', 'Request ID', 'User', 'Status', 'Created At',
734
- 'Created By', 'Department', 'Section'
735
- ];
736
- v_headers_labels TEXT[];
737
- v_headers_json JSONB;
738
- v_cells TEXT[];
739
- v_all_rows JSONB;
740
- v_k TEXT;
741
- v_sheet_order INT := 0;
742
- BEGIN
743
- v_sort_col := CASE lower(trim(p_sort_by))
744
- WHEN 'updated_at' THEN 'v.created_at'
745
- WHEN 'id' THEN 'v.sla_request_id'
746
- WHEN 'status' THEN 'v.status'
747
- WHEN 'request_id' THEN 'v.request_id'
748
- ELSE 'v.created_at'
749
- END;
750
- v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
751
-
752
- FOR v_sub IN
753
- EXECUTE format($q$
754
- SELECT DISTINCT
755
- COALESCE(sr.sub_service_id, 0) AS sub_service_id_num,
756
- COALESCE(NULLIF(trim(subsvc.sub_service_name), ''), NULLIF(trim(v.sub_service_name), ''), 'Uncategorized') AS sub_service_name
757
- FROM vw_sla_my_requests v
758
- INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
759
- LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
760
- WHERE v.user_id = %1$s
761
- AND v.status = ANY($1)
762
- AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
763
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
764
- AND ($4 IS NULL OR v.created_at::date >= $4)
765
- AND ($5 IS NULL OR v.created_at::date <= $5)
766
- AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
767
- ORDER BY sub_service_name ASC
768
- $q$, p_user_id)
769
- USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
770
- LOOP
771
- v_sheet_order := v_sheet_order + 1;
772
-
773
- SELECT COALESCE(array_agg(DISTINCT keys.k ORDER BY keys.k), ARRAY[]::TEXT[])
774
- INTO v_dyn_keys
775
- FROM vw_sla_my_requests v
776
- INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
777
- CROSS JOIN LATERAL jsonb_object_keys(COALESCE(v.request_fields, '{}'::jsonb)) AS keys(k)
778
- WHERE v.user_id = p_user_id
779
- AND v.status = ANY(p_statuses)
780
- AND (p_service_ids IS NULL OR cardinality(p_service_ids) = 0 OR sr.service_id = ANY(p_service_ids))
781
- AND (p_sub_service_ids IS NULL OR cardinality(p_sub_service_ids) = 0 OR sr.sub_service_id = ANY(p_sub_service_ids))
782
- AND (p_from_date IS NULL OR v.created_at::date >= p_from_date)
783
- AND (p_to_date IS NULL OR v.created_at::date <= p_to_date)
784
- AND (p_search_text IS NULL OR trim(p_search_text) = '' OR CAST(v.request_id AS TEXT) ILIKE '%' || trim(p_search_text) || '%')
785
- AND COALESCE(sr.sub_service_id, 0) = v_sub.sub_service_id_num;
786
-
787
- v_dyn_labels := ARRAY[]::TEXT[];
788
- IF v_dyn_keys IS NOT NULL THEN
789
- FOREACH v_k IN ARRAY v_dyn_keys
790
- LOOP
791
- v_dyn_labels := array_append(v_dyn_labels, sla_excel_header_label(v_k));
792
- END LOOP;
793
- END IF;
794
-
795
- v_headers_labels := ARRAY(
796
- SELECT sla_excel_header_label(lbl)
797
- FROM unnest(v_common_labels || COALESCE(v_dyn_labels, ARRAY[]::TEXT[])) AS u(lbl)
798
- );
799
- v_headers_json := to_jsonb(v_headers_labels);
800
- v_all_rows := '[]'::jsonb;
801
-
802
- FOR v_row IN
803
- EXECUTE format($q$
804
- SELECT
805
- v.sla_request_id,
806
- v.request_id,
807
- v.status,
808
- v.created_at,
809
- v.created_by,
810
- v.req_user_department_id,
811
- v.req_user_section_id,
812
- v.request_fields,
813
- TRIM(COALESCE(u.employee_name, '')) AS user_name
814
- FROM vw_sla_my_requests v
815
- INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
816
- LEFT JOIN users u ON u.id = v.user_id AND COALESCE(u.is_deleted, false) = false
817
- WHERE v.user_id = %1$s
818
- AND v.status = ANY($1)
819
- AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
820
- AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
821
- AND ($4 IS NULL OR v.created_at::date >= $4)
822
- AND ($5 IS NULL OR v.created_at::date <= $5)
823
- AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
824
- AND COALESCE(sr.sub_service_id, 0) = %2$s
825
- ORDER BY %3$s %4$s, v.sla_request_id ASC
826
- $q$, p_user_id, v_sub.sub_service_id_num, v_sort_col, v_sort_dir)
827
- USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
828
- LOOP
829
- v_cells := ARRAY[
830
- COALESCE(v_row.sla_request_id::TEXT, ''),
831
- COALESCE(v_row.request_id::TEXT, ''),
832
- COALESCE(v_row.user_name, ''),
833
- COALESCE(v_row.status, ''),
834
- COALESCE(to_char(v_row.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS'), ''),
835
- COALESCE(v_row.created_by, ''),
836
- COALESCE(v_row.req_user_department_id, ''),
837
- COALESCE(v_row.req_user_section_id, '')
838
- ];
839
-
840
- IF v_dyn_keys IS NOT NULL THEN
841
- FOREACH v_k IN ARRAY v_dyn_keys
842
- LOOP
843
- v_cells := array_append(v_cells, COALESCE(sla_request_field_cell(v_row.request_fields, v_k), ''));
844
- END LOOP;
845
- END IF;
846
-
847
- v_all_rows := v_all_rows || jsonb_build_array(to_jsonb(v_cells));
848
- END LOOP;
849
-
850
- sheet_order := v_sheet_order;
851
- sub_service_id := NULLIF(v_sub.sub_service_id_num, 0);
852
- sub_service_name := v_sub.sub_service_name;
853
- sheet_name := sla_excel_sheet_name(v_sub.sub_service_name);
854
- headers := v_headers_json;
855
- rows := v_all_rows;
856
- RETURN NEXT;
857
- END LOOP;
858
-
859
- RETURN QUERY
860
- SELECT *
861
- FROM sla_my_requests_approvals_excel_sheet(
862
- format('v.user_id = %s', p_user_id),
863
- p_statuses,
864
- p_service_ids,
865
- p_sub_service_ids,
866
- p_from_date,
867
- p_to_date,
868
- p_search_text,
869
- v_sheet_order + 1
870
- );
871
-
872
- RETURN;
873
- END;
874
- $$;
1
+ -- SLA Reports stored procedures (Reports_Service)
2
+ -- Applied via: npm run sync:sla-sql (shared_models/scripts/sync-sla-reports-sql.js)
3
+ -- Or: psql -U <user> -d <database> -f shared_models/sql/sla_reports_procedures.sql
4
+
5
+ -- ---------------------------------------------------------------------------
6
+ -- Helper: approver row matches user (same semantics as User_Service SLA dashboard)
7
+ -- ---------------------------------------------------------------------------
8
+ CREATE OR REPLACE FUNCTION sla_approval_matches_user(p_sa_alias TEXT, p_user_id INT)
9
+ RETURNS TEXT
10
+ LANGUAGE sql
11
+ IMMUTABLE
12
+ AS $$
13
+ SELECT format('(
14
+ %1$s.approver_user_id = %2$s
15
+ OR %1$s.delegate_user_id = %2$s
16
+ OR (
17
+ %1$s.approver_user_id IS NULL
18
+ AND %1$s.delegate_user_id IS NULL
19
+ AND %1$s.approver_role_id IS NOT NULL
20
+ AND %1$s.department_id IS NOT NULL
21
+ AND EXISTS (
22
+ SELECT 1 FROM user_role ur
23
+ WHERE ur.user_id = %2$s
24
+ AND ur.role_id = %1$s.approver_role_id
25
+ AND ur.department_id = %1$s.department_id
26
+ AND (
27
+ %1$s.section_id IS NULL
28
+ OR (%1$s.section_id IS NOT NULL AND (ur.section_id = %1$s.section_id OR ur.section_id IS NULL))
29
+ )
30
+ AND ur.is_deleted = false AND ur.is_active = true
31
+ )
32
+ )
33
+ OR (
34
+ %1$s.approver_user_id IS NULL
35
+ AND %1$s.delegate_user_id IS NULL
36
+ AND %1$s.approver_role_id IS NULL
37
+ AND %1$s.department_id IS NOT NULL
38
+ AND %1$s.section_id IS NOT NULL
39
+ AND EXISTS (
40
+ SELECT 1 FROM users u
41
+ WHERE u.id = %2$s AND COALESCE(u.is_deleted, false) = false
42
+ AND u.department = %1$s.department_id AND u.section = %1$s.section_id
43
+ )
44
+ )
45
+ OR (
46
+ %1$s.approver_user_id IS NULL
47
+ AND %1$s.delegate_user_id IS NULL
48
+ AND %1$s.approver_role_id IS NULL
49
+ AND %1$s.department_id IS NOT NULL
50
+ AND %1$s.section_id IS NULL
51
+ AND EXISTS (
52
+ SELECT 1 FROM users u
53
+ WHERE u.id = %2$s AND COALESCE(u.is_deleted, false) = false
54
+ AND u.department = %1$s.department_id
55
+ )
56
+ )
57
+ )', p_sa_alias, p_user_id);
58
+ $$;
59
+
60
+ -- ---------------------------------------------------------------------------
61
+ -- MY REQUESTS — paginated view
62
+ -- ---------------------------------------------------------------------------
63
+ CREATE OR REPLACE FUNCTION sp_sla_my_requests_view(
64
+ p_user_id INT,
65
+ p_service_ids INT[] DEFAULT NULL,
66
+ p_sub_service_ids INT[] DEFAULT NULL,
67
+ p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
68
+ p_from_date DATE DEFAULT NULL,
69
+ p_to_date DATE DEFAULT NULL,
70
+ p_search_text TEXT DEFAULT NULL,
71
+ p_page INT DEFAULT 1,
72
+ p_limit INT DEFAULT 50,
73
+ p_sort_by TEXT DEFAULT 'created_at',
74
+ p_sort_order TEXT DEFAULT 'DESC'
75
+ )
76
+ RETURNS TABLE (
77
+ sla_request_id INT,
78
+ request_id INT,
79
+ service_id INT,
80
+ sub_service_id INT,
81
+ service_name TEXT,
82
+ sub_service_name TEXT,
83
+ status TEXT,
84
+ user_id INT,
85
+ user_name TEXT,
86
+ workflow_execution_id TEXT,
87
+ created_at TIMESTAMPTZ,
88
+ updated_at TIMESTAMPTZ,
89
+ total_count BIGINT
90
+ )
91
+ LANGUAGE plpgsql
92
+ STABLE
93
+ AS $$
94
+ DECLARE
95
+ v_offset INT;
96
+ v_limit INT;
97
+ v_sort_col TEXT;
98
+ v_sort_dir TEXT;
99
+ v_sql TEXT;
100
+ BEGIN
101
+ v_limit := LEAST(GREATEST(COALESCE(p_limit, 50), 1), 200);
102
+ v_offset := GREATEST(COALESCE(p_page, 1) - 1, 0) * v_limit;
103
+
104
+ v_sort_col := CASE lower(trim(p_sort_by))
105
+ WHEN 'updated_at' THEN 'sr.updated_at'
106
+ WHEN 'id' THEN 'sr.id'
107
+ WHEN 'status' THEN 'sr.status'
108
+ WHEN 'request_id' THEN 'sr.request_id'
109
+ ELSE 'sr.created_at'
110
+ END;
111
+
112
+ v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
113
+
114
+ v_sql := format($q$
115
+ SELECT
116
+ sr.id::INT,
117
+ sr.request_id::INT,
118
+ sr.service_id::INT,
119
+ sr.sub_service_id::INT,
120
+ svc.name::TEXT,
121
+ subsvc.sub_service_name::TEXT,
122
+ sr.status::TEXT,
123
+ sr.user_id::INT,
124
+ TRIM(COALESCE(u.employee_name, ''))::TEXT,
125
+ sr.workflow_execution_id::TEXT,
126
+ sr.created_at,
127
+ sr.updated_at,
128
+ COUNT(*) OVER()::BIGINT
129
+ FROM sla_requests sr
130
+ LEFT JOIN caa_services svc ON svc.id = sr.service_id
131
+ LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
132
+ LEFT JOIN users u ON u.id = sr.user_id
133
+ WHERE sr.is_deleted = false
134
+ AND sr.user_id = %1$s
135
+ AND sr.status::TEXT = ANY($1)
136
+ AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
137
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
138
+ AND ($4 IS NULL OR sr.created_at::date >= $4)
139
+ AND ($5 IS NULL OR sr.created_at::date <= $5)
140
+ AND ($6 IS NULL OR trim($6) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
141
+ ORDER BY %2$s %3$s
142
+ LIMIT %4$s OFFSET %5$s
143
+ $q$, p_user_id, v_sort_col, v_sort_dir, v_limit, v_offset);
144
+
145
+ RETURN QUERY EXECUTE v_sql
146
+ USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text;
147
+ END;
148
+ $$;
149
+
150
+ -- ---------------------------------------------------------------------------
151
+ -- MY REQUESTS — full export (download)
152
+ -- ---------------------------------------------------------------------------
153
+ CREATE OR REPLACE FUNCTION sp_sla_my_requests_download(
154
+ p_user_id INT,
155
+ p_service_ids INT[] DEFAULT NULL,
156
+ p_sub_service_ids INT[] DEFAULT NULL,
157
+ p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
158
+ p_from_date DATE DEFAULT NULL,
159
+ p_to_date DATE DEFAULT NULL,
160
+ p_search_text TEXT DEFAULT NULL,
161
+ p_sort_by TEXT DEFAULT 'created_at',
162
+ p_sort_order TEXT DEFAULT 'DESC'
163
+ )
164
+ RETURNS TABLE (
165
+ sla_request_id INT,
166
+ request_id INT,
167
+ service_id INT,
168
+ sub_service_id INT,
169
+ service_name TEXT,
170
+ sub_service_name TEXT,
171
+ status TEXT,
172
+ user_id INT,
173
+ user_name TEXT,
174
+ workflow_execution_id TEXT,
175
+ created_at TIMESTAMPTZ,
176
+ updated_at TIMESTAMPTZ
177
+ )
178
+ LANGUAGE plpgsql
179
+ STABLE
180
+ AS $$
181
+ DECLARE
182
+ v_sort_col TEXT;
183
+ v_sort_dir TEXT;
184
+ v_sql TEXT;
185
+ BEGIN
186
+ v_sort_col := CASE lower(trim(p_sort_by))
187
+ WHEN 'updated_at' THEN 'sr.updated_at'
188
+ WHEN 'id' THEN 'sr.id'
189
+ WHEN 'status' THEN 'sr.status'
190
+ WHEN 'request_id' THEN 'sr.request_id'
191
+ ELSE 'sr.created_at'
192
+ END;
193
+ v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
194
+
195
+ v_sql := format($q$
196
+ SELECT
197
+ sr.id::INT,
198
+ sr.request_id::INT,
199
+ sr.service_id::INT,
200
+ sr.sub_service_id::INT,
201
+ svc.name::TEXT,
202
+ subsvc.sub_service_name::TEXT,
203
+ sr.status::TEXT,
204
+ sr.user_id::INT,
205
+ TRIM(COALESCE(u.employee_name, ''))::TEXT,
206
+ sr.workflow_execution_id::TEXT,
207
+ sr.created_at,
208
+ sr.updated_at
209
+ FROM sla_requests sr
210
+ LEFT JOIN caa_services svc ON svc.id = sr.service_id
211
+ LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
212
+ LEFT JOIN users u ON u.id = sr.user_id
213
+ WHERE sr.is_deleted = false
214
+ AND sr.user_id = %1$s
215
+ AND sr.status::TEXT = ANY($1)
216
+ AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
217
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
218
+ AND ($4 IS NULL OR sr.created_at::date >= $4)
219
+ AND ($5 IS NULL OR sr.created_at::date <= $5)
220
+ AND ($6 IS NULL OR trim($6) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
221
+ ORDER BY %2$s %3$s
222
+ $q$, p_user_id, v_sort_col, v_sort_dir);
223
+
224
+ RETURN QUERY EXECUTE v_sql
225
+ USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text;
226
+ END;
227
+ $$;
228
+
229
+ -- ---------------------------------------------------------------------------
230
+ -- APPROVALS — paginated view
231
+ -- ---------------------------------------------------------------------------
232
+ DROP FUNCTION IF EXISTS sp_sla_approvals_view(
233
+ INT,
234
+ INT[],
235
+ INT[],
236
+ TEXT[],
237
+ TEXT[],
238
+ DATE,
239
+ DATE,
240
+ TEXT,
241
+ INT,
242
+ INT,
243
+ TEXT,
244
+ TEXT
245
+ );
246
+ CREATE OR REPLACE FUNCTION sp_sla_approvals_view(
247
+ p_approver_user_id INT,
248
+ p_service_ids INT[] DEFAULT NULL,
249
+ p_sub_service_ids INT[] DEFAULT NULL,
250
+ p_request_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
251
+ p_approval_statuses TEXT[] DEFAULT ARRAY['In Progress','Approved','Rejected'],
252
+ p_from_date DATE DEFAULT NULL,
253
+ p_to_date DATE DEFAULT NULL,
254
+ p_search_text TEXT DEFAULT NULL,
255
+ p_page INT DEFAULT 1,
256
+ p_limit INT DEFAULT 50,
257
+ p_sort_by TEXT DEFAULT 'created_at',
258
+ p_sort_order TEXT DEFAULT 'DESC'
259
+ )
260
+ RETURNS TABLE (
261
+ sla_approval_id INT,
262
+ source_approval_id INT,
263
+ request_id INT,
264
+ service_id INT,
265
+ sub_service_id INT,
266
+ service_name TEXT,
267
+ sub_service_name TEXT,
268
+ department_id INT,
269
+ department_name TEXT,
270
+ section_id INT,
271
+ section_name TEXT,
272
+ role_id INT,
273
+ role_name TEXT,
274
+ approval_status TEXT,
275
+ request_status TEXT,
276
+ level INT,
277
+ approver_user_id INT,
278
+ approver_user_name TEXT,
279
+ delegate_user_id INT,
280
+ delegate_user_name TEXT,
281
+ approved_by INT,
282
+ approved_by_name TEXT,
283
+ comment TEXT,
284
+ created_at TIMESTAMPTZ,
285
+ updated_at TIMESTAMPTZ,
286
+ total_count BIGINT
287
+ )
288
+ LANGUAGE plpgsql
289
+ STABLE
290
+ AS $$
291
+ DECLARE
292
+ v_offset INT;
293
+ v_limit INT;
294
+ v_sort_col TEXT;
295
+ v_sort_dir TEXT;
296
+ v_match TEXT;
297
+ v_sql TEXT;
298
+ BEGIN
299
+ v_limit := LEAST(GREATEST(COALESCE(p_limit, 50), 1), 200);
300
+ v_offset := GREATEST(COALESCE(p_page, 1) - 1, 0) * v_limit;
301
+ v_match := sla_approval_matches_user('sa', p_approver_user_id);
302
+
303
+ v_sort_col := CASE lower(trim(p_sort_by))
304
+ WHEN 'updated_at' THEN 'sa.updated_at'
305
+ WHEN 'id' THEN 'sa.id'
306
+ WHEN 'approval_status' THEN 'sa.approval_status'
307
+ WHEN 'level' THEN 'sa.level'
308
+ WHEN 'request_id' THEN 'sa.request_id'
309
+ ELSE 'sa.created_at'
310
+ END;
311
+ v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
312
+
313
+ v_sql := format($q$
314
+ SELECT
315
+ sa.id::INT,
316
+ sa.source_approval_id::INT,
317
+ sa.request_id::INT,
318
+ sa.service_id::INT,
319
+ sa.sub_service_id::INT,
320
+ svc.name::TEXT,
321
+ subsvc.sub_service_name::TEXT,
322
+ sa.department_id::INT,
323
+ TRIM(COALESCE(dept.department_name, ''))::TEXT,
324
+ sa.section_id::INT,
325
+ TRIM(COALESCE(sec.section_name, ''))::TEXT,
326
+ sa.approver_role_id::INT,
327
+ TRIM(COALESCE(r.name, ''))::TEXT,
328
+ sa.approval_status::TEXT,
329
+ sr.status::TEXT,
330
+ sa.level::INT,
331
+ sa.approver_user_id::INT,
332
+ TRIM(COALESCE(au.employee_name, ''))::TEXT,
333
+ sa.delegate_user_id::INT,
334
+ TRIM(COALESCE(du.employee_name, ''))::TEXT,
335
+ sa.approved_by::INT,
336
+ TRIM(COALESCE(ab.employee_name, ''))::TEXT,
337
+ sa.comment::TEXT,
338
+ sa.created_at,
339
+ sa.updated_at,
340
+ COUNT(*) OVER()::BIGINT
341
+ FROM sla_approval sa
342
+ INNER JOIN sla_requests sr
343
+ ON sr.request_id = sa.request_id AND sr.is_deleted = false
344
+ LEFT JOIN caa_services svc ON svc.id = sa.service_id
345
+ LEFT JOIN caa_sub_services subsvc ON subsvc.id = sa.sub_service_id
346
+ LEFT JOIN departments dept ON dept.id = sa.department_id AND COALESCE(dept.is_deleted, false) = false
347
+ LEFT JOIN sections sec ON sec.id = sa.section_id AND COALESCE(sec.is_deleted, false) = false
348
+ LEFT JOIN role r ON r.id = sa.approver_role_id AND COALESCE(r.is_deleted, false) = false
349
+ LEFT JOIN users au ON au.id = sa.approver_user_id
350
+ LEFT JOIN users du ON du.id = sa.delegate_user_id
351
+ LEFT JOIN users ab ON ab.id = sa.approved_by
352
+ WHERE sa.is_deleted = false
353
+ AND sr.user_id != %1$s
354
+ AND (sa.service_id = sr.service_id OR sa.service_id IS NULL)
355
+ AND (sa.sub_service_id = sr.sub_service_id OR sa.sub_service_id IS NULL)
356
+ AND %2$s
357
+ AND (sa.approval_status = ANY($1) OR sa.approval_status IS NULL)
358
+ AND sr.status::TEXT = ANY($2)
359
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.service_id = ANY($3))
360
+ AND ($4 IS NULL OR cardinality($4) = 0 OR sr.sub_service_id = ANY($4))
361
+ AND ($5 IS NULL OR sr.created_at::date >= $5)
362
+ AND ($6 IS NULL OR sr.created_at::date <= $6)
363
+ AND ($7 IS NULL OR trim($7) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($7) || '%%')
364
+ ORDER BY %3$s %4$s, sa.id DESC
365
+ LIMIT %5$s OFFSET %6$s
366
+ $q$, p_approver_user_id, v_match, v_sort_col, v_sort_dir, v_limit, v_offset);
367
+
368
+ RETURN QUERY EXECUTE v_sql
369
+ USING p_approval_statuses, p_request_statuses, p_service_ids, p_sub_service_ids,
370
+ p_from_date, p_to_date, p_search_text;
371
+ END;
372
+ $$;
373
+
374
+ -- ---------------------------------------------------------------------------
375
+ -- APPROVALS — full export (download)
376
+ -- ---------------------------------------------------------------------------
377
+ CREATE OR REPLACE FUNCTION sp_sla_approvals_download(
378
+ p_approver_user_id INT,
379
+ p_service_ids INT[] DEFAULT NULL,
380
+ p_sub_service_ids INT[] DEFAULT NULL,
381
+ p_request_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
382
+ p_approval_statuses TEXT[] DEFAULT ARRAY['In Progress','Approved','Rejected'],
383
+ p_from_date DATE DEFAULT NULL,
384
+ p_to_date DATE DEFAULT NULL,
385
+ p_search_text TEXT DEFAULT NULL,
386
+ p_sort_by TEXT DEFAULT 'created_at',
387
+ p_sort_order TEXT DEFAULT 'DESC'
388
+ )
389
+ RETURNS TABLE (
390
+ sla_approval_id INT,
391
+ source_approval_id INT,
392
+ request_id INT,
393
+ service_id INT,
394
+ sub_service_id INT,
395
+ service_name TEXT,
396
+ sub_service_name TEXT,
397
+ approval_status TEXT,
398
+ request_status TEXT,
399
+ level INT,
400
+ approver_user_id INT,
401
+ approver_user_name TEXT,
402
+ delegate_user_id INT,
403
+ delegate_user_name TEXT,
404
+ approved_by INT,
405
+ approved_by_name TEXT,
406
+ comment TEXT,
407
+ created_at TIMESTAMPTZ,
408
+ updated_at TIMESTAMPTZ
409
+ )
410
+ LANGUAGE plpgsql
411
+ STABLE
412
+ AS $$
413
+ DECLARE
414
+ v_sort_col TEXT;
415
+ v_sort_dir TEXT;
416
+ v_match TEXT;
417
+ v_sql TEXT;
418
+ BEGIN
419
+ v_match := sla_approval_matches_user('sa', p_approver_user_id);
420
+
421
+ v_sort_col := CASE lower(trim(p_sort_by))
422
+ WHEN 'updated_at' THEN 'sa.updated_at'
423
+ WHEN 'id' THEN 'sa.id'
424
+ WHEN 'approval_status' THEN 'sa.approval_status'
425
+ WHEN 'level' THEN 'sa.level'
426
+ WHEN 'request_id' THEN 'sa.request_id'
427
+ ELSE 'sa.created_at'
428
+ END;
429
+ v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
430
+
431
+ v_sql := format($q$
432
+ SELECT
433
+ sa.id::INT,
434
+ sa.source_approval_id::INT,
435
+ sa.request_id::INT,
436
+ sa.service_id::INT,
437
+ sa.sub_service_id::INT,
438
+ svc.name::TEXT,
439
+ subsvc.sub_service_name::TEXT,
440
+ sa.approval_status::TEXT,
441
+ sr.status::TEXT,
442
+ sa.level::INT,
443
+ sa.approver_user_id::INT,
444
+ TRIM(COALESCE(au.employee_name, ''))::TEXT,
445
+ sa.delegate_user_id::INT,
446
+ TRIM(COALESCE(du.employee_name, ''))::TEXT,
447
+ sa.approved_by::INT,
448
+ TRIM(COALESCE(ab.employee_name, ''))::TEXT,
449
+ sa.comment::TEXT,
450
+ sa.created_at,
451
+ sa.updated_at
452
+ FROM sla_approval sa
453
+ INNER JOIN sla_requests sr
454
+ ON sr.request_id = sa.request_id AND sr.is_deleted = false
455
+ LEFT JOIN caa_services svc ON svc.id = sa.service_id
456
+ LEFT JOIN caa_sub_services subsvc ON subsvc.id = sa.sub_service_id
457
+ LEFT JOIN users au ON au.id = sa.approver_user_id
458
+ LEFT JOIN users du ON du.id = sa.delegate_user_id
459
+ LEFT JOIN users ab ON ab.id = sa.approved_by
460
+ WHERE sa.is_deleted = false
461
+ AND sr.user_id != %1$s
462
+ AND (sa.service_id = sr.service_id OR sa.service_id IS NULL)
463
+ AND (sa.sub_service_id = sr.sub_service_id OR sa.sub_service_id IS NULL)
464
+ AND %2$s
465
+ AND (sa.approval_status = ANY($1) OR sa.approval_status IS NULL)
466
+ AND sr.status::TEXT = ANY($2)
467
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.service_id = ANY($3))
468
+ AND ($4 IS NULL OR cardinality($4) = 0 OR sr.sub_service_id = ANY($4))
469
+ AND ($5 IS NULL OR sr.created_at::date >= $5)
470
+ AND ($6 IS NULL OR sr.created_at::date <= $6)
471
+ AND ($7 IS NULL OR trim($7) = '' OR CAST(sr.request_id AS TEXT) ILIKE '%%' || trim($7) || '%%')
472
+ ORDER BY %3$s %4$s, sa.id DESC
473
+ $q$, p_approver_user_id, v_match, v_sort_col, v_sort_dir);
474
+
475
+ RETURN QUERY EXECUTE v_sql
476
+ USING p_approval_statuses, p_request_statuses, p_service_ids, p_sub_service_ids,
477
+ p_from_date, p_to_date, p_search_text;
478
+ END;
479
+ $$;
480
+
481
+ -- ---------------------------------------------------------------------------
482
+ -- Helpers: Excel export (my requests)
483
+ -- ---------------------------------------------------------------------------
484
+ CREATE OR REPLACE FUNCTION sla_excel_header_label(p_key TEXT)
485
+ RETURNS TEXT
486
+ LANGUAGE sql
487
+ IMMUTABLE
488
+ AS $$
489
+ SELECT trim(
490
+ initcap(
491
+ regexp_replace(
492
+ regexp_replace(COALESCE(p_key, ''), '[_-]+', ' ', 'g'),
493
+ '\s+',
494
+ ' ',
495
+ 'g'
496
+ )
497
+ )
498
+ );
499
+ $$;
500
+
501
+ CREATE OR REPLACE FUNCTION sla_request_field_cell(p_fields JSONB, p_key TEXT)
502
+ RETURNS TEXT
503
+ LANGUAGE sql
504
+ IMMUTABLE
505
+ AS $$
506
+ SELECT CASE
507
+ WHEN p_fields IS NULL OR NOT (p_fields ? p_key) THEN NULL
508
+ WHEN jsonb_typeof(p_fields -> p_key) IN ('object', 'array') THEN (p_fields -> p_key)::TEXT
509
+ ELSE p_fields ->> p_key
510
+ END;
511
+ $$;
512
+
513
+ CREATE OR REPLACE FUNCTION sla_excel_sheet_name(p_sub_service_name TEXT)
514
+ RETURNS TEXT
515
+ LANGUAGE sql
516
+ IMMUTABLE
517
+ AS $$
518
+ SELECT LEFT(
519
+ CASE
520
+ WHEN COALESCE(trim(p_sub_service_name), '') = '' THEN 'Requests'
521
+ ELSE trim(p_sub_service_name) || ' Requests'
522
+ END,
523
+ 31
524
+ );
525
+ $$;
526
+
527
+ DROP FUNCTION IF EXISTS sla_excel_approvals_sheet_name(TEXT);
528
+
529
+ CREATE OR REPLACE FUNCTION sla_excel_approvals_sheet_name(p_sub_service_name TEXT)
530
+ RETURNS TEXT
531
+ LANGUAGE sql
532
+ IMMUTABLE
533
+ AS $$
534
+ SELECT LEFT(
535
+ CASE
536
+ WHEN COALESCE(trim(p_sub_service_name), '') = '' THEN 'Approvals'
537
+ ELSE trim(p_sub_service_name) || ' Approvals'
538
+ END,
539
+ 31
540
+ );
541
+ $$;
542
+
543
+ -- ---------------------------------------------------------------------------
544
+ -- MY REQUESTS — Approvals sheet (vw_sla_approvals joined to filtered my-requests)
545
+ -- p_user_scope_sql: e.g. 'v.user_id = 5' or 'TRUE' (admin all users)
546
+ -- ---------------------------------------------------------------------------
547
+ DROP FUNCTION IF EXISTS sla_my_requests_approvals_excel_sheet(TEXT, TEXT[], INT[], INT[], DATE, DATE, TEXT, INT);
548
+
549
+ CREATE OR REPLACE FUNCTION sla_my_requests_approvals_excel_sheet(
550
+ p_user_scope_sql TEXT,
551
+ p_statuses TEXT[],
552
+ p_service_ids INT[],
553
+ p_sub_service_ids INT[],
554
+ p_from_date DATE,
555
+ p_to_date DATE,
556
+ p_search_text TEXT,
557
+ p_start_sheet_order INT
558
+ )
559
+ RETURNS TABLE (
560
+ sheet_order INT,
561
+ sub_service_id INT,
562
+ sub_service_name TEXT,
563
+ sheet_name TEXT,
564
+ headers JSONB,
565
+ rows JSONB
566
+ )
567
+ LANGUAGE plpgsql
568
+ STABLE
569
+ AS $$
570
+ DECLARE
571
+ v_sub RECORD;
572
+ v_approval RECORD;
573
+ v_sheet_order INT;
574
+ v_sheet_label TEXT;
575
+ v_headers_labels TEXT[] := ARRAY[
576
+ 'Request ID', 'Service ID', 'Service Name', 'Sub Service ID', 'Sub Service Name',
577
+ 'SLA Approval ID', 'Source Approval ID', 'Level', 'Approval Status', 'Request Status',
578
+ 'Approval Role ID', 'Approval Role Name', 'Approval Department ID', 'Approval Department Name',
579
+ 'Approval Section ID', 'Approval Section Name',
580
+ 'Approver User ID', 'Approver User Name', 'Delegate User ID', 'Delegate User Name',
581
+ 'Approved By ID', 'Approved By Name', 'Comment', 'Created At', 'Updated At'
582
+ ];
583
+ v_cells TEXT[];
584
+ v_all_rows JSONB;
585
+ BEGIN
586
+ v_sheet_order := p_start_sheet_order;
587
+
588
+ FOR v_sub IN
589
+ EXECUTE format($q$
590
+ SELECT DISTINCT
591
+ COALESCE(sr.sub_service_id, 0) AS sub_service_id_num,
592
+ COALESCE(NULLIF(trim(subsvc.sub_service_name), ''), NULLIF(trim(v.sub_service_name), ''), 'Uncategorized') AS sub_service_name
593
+ FROM vw_sla_my_requests v
594
+ INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
595
+ LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
596
+ WHERE %s
597
+ AND v.status = ANY($1)
598
+ AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
599
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
600
+ AND ($4 IS NULL OR v.created_at::date >= $4)
601
+ AND ($5 IS NULL OR v.created_at::date <= $5)
602
+ AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
603
+ ORDER BY sub_service_name ASC
604
+ $q$, p_user_scope_sql)
605
+ USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
606
+ LOOP
607
+ v_sheet_order := v_sheet_order + 1;
608
+ v_sheet_label := sla_excel_approvals_sheet_name(v_sub.sub_service_name);
609
+ v_all_rows := '[]'::jsonb;
610
+
611
+ FOR v_approval IN
612
+ EXECUTE format($q$
613
+ SELECT
614
+ va.request_id,
615
+ sr.service_id,
616
+ va.service_name,
617
+ sr.sub_service_id,
618
+ va.sub_service_name,
619
+ va.sla_approval_id,
620
+ va.source_approval_id,
621
+ va.level,
622
+ va.approval_status,
623
+ va.request_status,
624
+ va.approval_role_id,
625
+ va.approval_role_name,
626
+ va.approval_department_id,
627
+ va.approval_department_name,
628
+ va.approval_section_id,
629
+ va.approval_section_name,
630
+ va.approver_user_id,
631
+ va.approver_user_name,
632
+ va.delegate_user_id,
633
+ va.delegate_user_name,
634
+ va.approved_by,
635
+ va.approved_by_name,
636
+ va.comment,
637
+ va.created_at,
638
+ va.updated_at
639
+ FROM vw_sla_my_requests v
640
+ INNER JOIN sla_requests sr
641
+ ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
642
+ INNER JOIN vw_sla_approvals va
643
+ ON va.request_id = v.request_id
644
+ AND (va.service_id = sr.service_id OR va.service_id IS NULL)
645
+ AND (va.sub_service_id = sr.sub_service_id OR va.sub_service_id IS NULL)
646
+ WHERE %s
647
+ AND v.status = ANY($1)
648
+ AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
649
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
650
+ AND ($4 IS NULL OR v.created_at::date >= $4)
651
+ AND ($5 IS NULL OR v.created_at::date <= $5)
652
+ AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
653
+ AND COALESCE(sr.sub_service_id, 0) = %2$s
654
+ ORDER BY va.request_id ASC, va.level ASC NULLS LAST, va.sla_approval_id ASC
655
+ $q$, p_user_scope_sql, v_sub.sub_service_id_num)
656
+ USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
657
+ LOOP
658
+ v_cells := ARRAY[
659
+ COALESCE(v_approval.request_id::TEXT, ''),
660
+ COALESCE(v_approval.service_id::TEXT, ''),
661
+ COALESCE(v_approval.service_name, ''),
662
+ COALESCE(v_approval.sub_service_id::TEXT, ''),
663
+ COALESCE(v_approval.sub_service_name, ''),
664
+ COALESCE(v_approval.sla_approval_id::TEXT, ''),
665
+ COALESCE(v_approval.source_approval_id::TEXT, ''),
666
+ COALESCE(v_approval.level::TEXT, ''),
667
+ COALESCE(v_approval.approval_status, ''),
668
+ COALESCE(v_approval.request_status, ''),
669
+ COALESCE(v_approval.approval_role_id::TEXT, ''),
670
+ COALESCE(v_approval.approval_role_name, ''),
671
+ COALESCE(v_approval.approval_department_id::TEXT, ''),
672
+ COALESCE(v_approval.approval_department_name, ''),
673
+ COALESCE(v_approval.approval_section_id::TEXT, ''),
674
+ COALESCE(v_approval.approval_section_name, ''),
675
+ COALESCE(v_approval.approver_user_id::TEXT, ''),
676
+ COALESCE(v_approval.approver_user_name, ''),
677
+ COALESCE(v_approval.delegate_user_id::TEXT, ''),
678
+ COALESCE(v_approval.delegate_user_name, ''),
679
+ COALESCE(v_approval.approved_by::TEXT, ''),
680
+ COALESCE(v_approval.approved_by_name, ''),
681
+ COALESCE(v_approval.comment, ''),
682
+ COALESCE(to_char(v_approval.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS'), ''),
683
+ COALESCE(to_char(v_approval.updated_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS'), '')
684
+ ];
685
+ v_all_rows := v_all_rows || jsonb_build_array(to_jsonb(v_cells));
686
+ END LOOP;
687
+
688
+ sheet_order := v_sheet_order;
689
+ sub_service_id := NULLIF(v_sub.sub_service_id_num, 0);
690
+ sub_service_name := v_sub.sub_service_name;
691
+ sheet_name := v_sheet_label;
692
+ headers := to_jsonb(v_headers_labels);
693
+ rows := v_all_rows;
694
+ RETURN NEXT;
695
+ END LOOP;
696
+ END;
697
+ $$;
698
+
699
+ -- ---------------------------------------------------------------------------
700
+ -- MY REQUESTS — Excel workbook payload (one result row per sub-service sheet)
701
+ -- Uses vw_sla_my_requests; flattening and dynamic columns are built in the database.
702
+ -- ---------------------------------------------------------------------------
703
+ CREATE OR REPLACE FUNCTION sp_sla_my_requests_excel_export(
704
+ p_user_id INT,
705
+ p_service_ids INT[] DEFAULT NULL,
706
+ p_sub_service_ids INT[] DEFAULT NULL,
707
+ p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
708
+ p_from_date DATE DEFAULT NULL,
709
+ p_to_date DATE DEFAULT NULL,
710
+ p_search_text TEXT DEFAULT NULL,
711
+ p_sort_by TEXT DEFAULT 'created_at',
712
+ p_sort_order TEXT DEFAULT 'DESC'
713
+ )
714
+ RETURNS TABLE (
715
+ sheet_order INT,
716
+ sub_service_id INT,
717
+ sub_service_name TEXT,
718
+ sheet_name TEXT,
719
+ headers JSONB,
720
+ rows JSONB
721
+ )
722
+ LANGUAGE plpgsql
723
+ STABLE
724
+ AS $$
725
+ DECLARE
726
+ v_sort_col TEXT;
727
+ v_sort_dir TEXT;
728
+ v_sub RECORD;
729
+ v_row RECORD;
730
+ v_dyn_keys TEXT[];
731
+ v_dyn_labels TEXT[];
732
+ v_common_labels TEXT[] := ARRAY[
733
+ 'SLA Request ID', 'Request ID', 'User', 'Status', 'Created At',
734
+ 'Created By', 'Department', 'Section'
735
+ ];
736
+ v_headers_labels TEXT[];
737
+ v_headers_json JSONB;
738
+ v_cells TEXT[];
739
+ v_all_rows JSONB;
740
+ v_k TEXT;
741
+ v_sheet_order INT := 0;
742
+ BEGIN
743
+ v_sort_col := CASE lower(trim(p_sort_by))
744
+ WHEN 'updated_at' THEN 'v.created_at'
745
+ WHEN 'id' THEN 'v.sla_request_id'
746
+ WHEN 'status' THEN 'v.status'
747
+ WHEN 'request_id' THEN 'v.request_id'
748
+ ELSE 'v.created_at'
749
+ END;
750
+ v_sort_dir := CASE WHEN upper(trim(p_sort_order)) = 'ASC' THEN 'ASC' ELSE 'DESC' END;
751
+
752
+ FOR v_sub IN
753
+ EXECUTE format($q$
754
+ SELECT DISTINCT
755
+ COALESCE(sr.sub_service_id, 0) AS sub_service_id_num,
756
+ COALESCE(NULLIF(trim(subsvc.sub_service_name), ''), NULLIF(trim(v.sub_service_name), ''), 'Uncategorized') AS sub_service_name
757
+ FROM vw_sla_my_requests v
758
+ INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
759
+ LEFT JOIN caa_sub_services subsvc ON subsvc.id = sr.sub_service_id
760
+ WHERE v.user_id = %1$s
761
+ AND v.status = ANY($1)
762
+ AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
763
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
764
+ AND ($4 IS NULL OR v.created_at::date >= $4)
765
+ AND ($5 IS NULL OR v.created_at::date <= $5)
766
+ AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
767
+ ORDER BY sub_service_name ASC
768
+ $q$, p_user_id)
769
+ USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
770
+ LOOP
771
+ v_sheet_order := v_sheet_order + 1;
772
+
773
+ SELECT COALESCE(array_agg(DISTINCT keys.k ORDER BY keys.k), ARRAY[]::TEXT[])
774
+ INTO v_dyn_keys
775
+ FROM vw_sla_my_requests v
776
+ INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
777
+ CROSS JOIN LATERAL jsonb_object_keys(COALESCE(v.request_fields, '{}'::jsonb)) AS keys(k)
778
+ WHERE v.user_id = p_user_id
779
+ AND v.status = ANY(p_statuses)
780
+ AND (p_service_ids IS NULL OR cardinality(p_service_ids) = 0 OR sr.service_id = ANY(p_service_ids))
781
+ AND (p_sub_service_ids IS NULL OR cardinality(p_sub_service_ids) = 0 OR sr.sub_service_id = ANY(p_sub_service_ids))
782
+ AND (p_from_date IS NULL OR v.created_at::date >= p_from_date)
783
+ AND (p_to_date IS NULL OR v.created_at::date <= p_to_date)
784
+ AND (p_search_text IS NULL OR trim(p_search_text) = '' OR CAST(v.request_id AS TEXT) ILIKE '%' || trim(p_search_text) || '%')
785
+ AND COALESCE(sr.sub_service_id, 0) = v_sub.sub_service_id_num;
786
+
787
+ v_dyn_labels := ARRAY[]::TEXT[];
788
+ IF v_dyn_keys IS NOT NULL THEN
789
+ FOREACH v_k IN ARRAY v_dyn_keys
790
+ LOOP
791
+ v_dyn_labels := array_append(v_dyn_labels, sla_excel_header_label(v_k));
792
+ END LOOP;
793
+ END IF;
794
+
795
+ v_headers_labels := ARRAY(
796
+ SELECT sla_excel_header_label(lbl)
797
+ FROM unnest(v_common_labels || COALESCE(v_dyn_labels, ARRAY[]::TEXT[])) AS u(lbl)
798
+ );
799
+ v_headers_json := to_jsonb(v_headers_labels);
800
+ v_all_rows := '[]'::jsonb;
801
+
802
+ FOR v_row IN
803
+ EXECUTE format($q$
804
+ SELECT
805
+ v.sla_request_id,
806
+ v.request_id,
807
+ v.status,
808
+ v.created_at,
809
+ v.created_by,
810
+ v.req_user_department_id,
811
+ v.req_user_section_id,
812
+ v.request_fields,
813
+ TRIM(COALESCE(u.employee_name, '')) AS user_name
814
+ FROM vw_sla_my_requests v
815
+ INNER JOIN sla_requests sr ON sr.id = v.sla_request_id AND COALESCE(sr.is_deleted, false) = false
816
+ LEFT JOIN users u ON u.id = v.user_id AND COALESCE(u.is_deleted, false) = false
817
+ WHERE v.user_id = %1$s
818
+ AND v.status = ANY($1)
819
+ AND ($2 IS NULL OR cardinality($2) = 0 OR sr.service_id = ANY($2))
820
+ AND ($3 IS NULL OR cardinality($3) = 0 OR sr.sub_service_id = ANY($3))
821
+ AND ($4 IS NULL OR v.created_at::date >= $4)
822
+ AND ($5 IS NULL OR v.created_at::date <= $5)
823
+ AND ($6 IS NULL OR trim($6) = '' OR CAST(v.request_id AS TEXT) ILIKE '%%' || trim($6) || '%%')
824
+ AND COALESCE(sr.sub_service_id, 0) = %2$s
825
+ ORDER BY %3$s %4$s, v.sla_request_id ASC
826
+ $q$, p_user_id, v_sub.sub_service_id_num, v_sort_col, v_sort_dir)
827
+ USING p_statuses, p_service_ids, p_sub_service_ids, p_from_date, p_to_date, p_search_text
828
+ LOOP
829
+ v_cells := ARRAY[
830
+ COALESCE(v_row.sla_request_id::TEXT, ''),
831
+ COALESCE(v_row.request_id::TEXT, ''),
832
+ COALESCE(v_row.user_name, ''),
833
+ COALESCE(v_row.status, ''),
834
+ COALESCE(to_char(v_row.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI:SS'), ''),
835
+ COALESCE(v_row.created_by, ''),
836
+ COALESCE(v_row.req_user_department_id, ''),
837
+ COALESCE(v_row.req_user_section_id, '')
838
+ ];
839
+
840
+ IF v_dyn_keys IS NOT NULL THEN
841
+ FOREACH v_k IN ARRAY v_dyn_keys
842
+ LOOP
843
+ v_cells := array_append(v_cells, COALESCE(sla_request_field_cell(v_row.request_fields, v_k), ''));
844
+ END LOOP;
845
+ END IF;
846
+
847
+ v_all_rows := v_all_rows || jsonb_build_array(to_jsonb(v_cells));
848
+ END LOOP;
849
+
850
+ sheet_order := v_sheet_order;
851
+ sub_service_id := NULLIF(v_sub.sub_service_id_num, 0);
852
+ sub_service_name := v_sub.sub_service_name;
853
+ sheet_name := sla_excel_sheet_name(v_sub.sub_service_name);
854
+ headers := v_headers_json;
855
+ rows := v_all_rows;
856
+ RETURN NEXT;
857
+ END LOOP;
858
+
859
+ RETURN QUERY
860
+ SELECT *
861
+ FROM sla_my_requests_approvals_excel_sheet(
862
+ format('v.user_id = %s', p_user_id),
863
+ p_statuses,
864
+ p_service_ids,
865
+ p_sub_service_ids,
866
+ p_from_date,
867
+ p_to_date,
868
+ p_search_text,
869
+ v_sheet_order + 1
870
+ );
871
+
872
+ RETURN;
873
+ END;
874
+ $$;