@platform-modules/civil-aviation-authority 2.3.292 → 2.3.293
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/data-source.js +2 -0
- package/dist/i18n/workflow-chat-i18n.json +9 -1
- package/dist/i18n/workflow-chat-message.builder.d.ts +12 -0
- package/dist/i18n/workflow-chat-message.builder.js +215 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/ContractServiceRequestDocumentModel.d.ts +13 -0
- package/dist/models/ContractServiceRequestDocumentModel.js +60 -0
- package/dist/models/ContractServiceRequestModel.d.ts +2 -0
- package/dist/models/ContractServiceRequestModel.js +8 -0
- package/dist/models/FollowUpReportItemModel.d.ts +4 -0
- package/dist/models/FollowUpReportItemModel.js +16 -0
- package/dist/models/FollowUpReportRequestModel.d.ts +7 -1
- package/dist/models/FollowUpReportRequestModel.js +7 -1
- package/dist/models/RespondToEnquiriesAttachmentModel.d.ts +2 -1
- package/dist/models/RespondToEnquiriesAttachmentModel.js +7 -2
- package/package.json +1 -1
- package/sql/caa_api_payload_changes_2026_07.sql +40 -0
- package/src/data-source.ts +2 -0
- package/src/i18n/workflow-chat-i18n.json +9 -1
- package/src/i18n/workflow-chat-message.builder.ts +261 -10
- package/src/index.ts +3 -1
- package/src/models/ContractServiceRequestDocumentModel.ts +35 -0
- package/src/models/ContractServiceRequestModel.ts +6 -0
- package/src/models/FollowUpReportItemModel.ts +12 -0
- package/src/models/FollowUpReportRequestModel.ts +9 -0
- package/src/models/RespondToEnquiriesAttachmentModel.ts +6 -1
package/dist/data-source.js
CHANGED
|
@@ -271,6 +271,7 @@ const ComplaintLostPropertyReportChatModel_1 = require("./models/ComplaintLostPr
|
|
|
271
271
|
const ComplaintLostPropertyReportRequestModel_1 = require("./models/ComplaintLostPropertyReportRequestModel");
|
|
272
272
|
const ComplaintLostPropertyReportWorkflowModel_1 = require("./models/ComplaintLostPropertyReportWorkflowModel");
|
|
273
273
|
const ContractServiceRequestModel_1 = require("./models/ContractServiceRequestModel");
|
|
274
|
+
const ContractServiceRequestDocumentModel_1 = require("./models/ContractServiceRequestDocumentModel");
|
|
274
275
|
const CountryMasterModel_1 = require("./models/CountryMasterModel");
|
|
275
276
|
const CyberSecurityAuditRequestModel_1 = require("./models/CyberSecurityAuditRequestModel");
|
|
276
277
|
const CyberSecurityRiskManagementRequestModel_1 = require("./models/CyberSecurityRiskManagementRequestModel");
|
|
@@ -593,6 +594,7 @@ exports.AppDataSource = new typeorm_1.DataSource({
|
|
|
593
594
|
ComplaintLostPropertyReportRequestModel_1.ComplaintLostPropertyReportRequest,
|
|
594
595
|
ComplaintLostPropertyReportWorkflowModel_1.ComplaintLostPropertyReportWorkflow,
|
|
595
596
|
ContractServiceRequestModel_1.ContractServiceRequest,
|
|
597
|
+
ContractServiceRequestDocumentModel_1.ContractServiceRequestDocument,
|
|
596
598
|
CountryMasterModel_1.CountryMaster,
|
|
597
599
|
CyberSecurityAuditRequestModel_1.CyberSecurityAuditRequest,
|
|
598
600
|
CyberSecurityRiskManagementRequestModel_1.CyberSecurityRiskManagementRequest,
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
"en": "Request Approved{{comment}}",
|
|
41
41
|
"ar": "تمت الموافقة على الطلب{{comment}}"
|
|
42
42
|
},
|
|
43
|
+
"request_approved_assigned_to_user": {
|
|
44
|
+
"en": "Request Approved: Request assigned to {{assignedUserName}} by {{roleName}} ({{actorUserName}} - {{employeeId}})",
|
|
45
|
+
"ar": "تمت الموافقة على الطلب: تم تعيين الطلب إلى {{assignedUserName}} من قبل {{roleName}} ({{actorUserName}} - {{employeeId}})"
|
|
46
|
+
},
|
|
47
|
+
"request_approved_assigned_to_technician": {
|
|
48
|
+
"en": "Request Approved: Request assigned to technician {{assignedUserName}} by {{roleName}} ({{actorUserName}} - {{employeeId}})",
|
|
49
|
+
"ar": "تمت الموافقة على الطلب: تم تعيين الطلب إلى الفني {{assignedUserName}} من قبل {{roleName}} ({{actorUserName}} - {{employeeId}})"
|
|
50
|
+
},
|
|
43
51
|
"request_rejected": {
|
|
44
52
|
"en": "Request Rejected{{comment}}",
|
|
45
53
|
"ar": "تم رفض الطلب{{comment}}"
|
|
@@ -106,7 +114,7 @@
|
|
|
106
114
|
},
|
|
107
115
|
"it_helpdesk_wf_status_by_role": {
|
|
108
116
|
"en": "Request {{status}} by {{approverRoleName}}{{comment}}",
|
|
109
|
-
"ar": "
|
|
117
|
+
"ar": "{{statusAr}} الطلب من قبل {{approverRoleName}}{{comment}}"
|
|
110
118
|
},
|
|
111
119
|
"it_helpdesk_wf_l3_step_rejected": {
|
|
112
120
|
"en": "{{roleName}} rejected the request{{comment}}",
|
|
@@ -69,6 +69,18 @@ export declare function buildSynchronizedWorkflowMessage(ctx: WorkflowMessageCon
|
|
|
69
69
|
* Use for every chat insert/update across all CAA modules.
|
|
70
70
|
*/
|
|
71
71
|
export declare function buildSynchronizedChatMessage(ctx: ChatMessageContext): BilingualText;
|
|
72
|
+
export type RequestApprovedAssignedWorkflowParams = {
|
|
73
|
+
assignedUserName: string;
|
|
74
|
+
roleName: string;
|
|
75
|
+
roleArabicName?: string | null;
|
|
76
|
+
actorUserName: string;
|
|
77
|
+
employeeId: string | number;
|
|
78
|
+
assigneeLabel?: 'user' | 'technician';
|
|
79
|
+
};
|
|
80
|
+
/** Parse legacy English-only assignment comments used as workflow log suffixes. */
|
|
81
|
+
export declare function tryParseRequestAssignedComment(comment?: string | null): Omit<RequestApprovedAssignedWorkflowParams, 'roleArabicName'> | null;
|
|
82
|
+
/** Bilingual workflow content for approved assignment steps (HR, IT, Tender, etc.). */
|
|
83
|
+
export declare function buildRequestApprovedAssignedWorkflowFields(params: RequestApprovedAssignedWorkflowParams): BilingualText;
|
|
72
84
|
/** Standard workflow log UPDATE fields for all CAA module repositories. */
|
|
73
85
|
export declare function buildWorkflowLogUpdateFields(params: {
|
|
74
86
|
approvalStatus: string;
|
|
@@ -20,6 +20,8 @@ exports.workflowTemplate = workflowTemplate;
|
|
|
20
20
|
exports.chatTemplate = chatTemplate;
|
|
21
21
|
exports.buildSynchronizedWorkflowMessage = buildSynchronizedWorkflowMessage;
|
|
22
22
|
exports.buildSynchronizedChatMessage = buildSynchronizedChatMessage;
|
|
23
|
+
exports.tryParseRequestAssignedComment = tryParseRequestAssignedComment;
|
|
24
|
+
exports.buildRequestApprovedAssignedWorkflowFields = buildRequestApprovedAssignedWorkflowFields;
|
|
23
25
|
exports.buildWorkflowLogUpdateFields = buildWorkflowLogUpdateFields;
|
|
24
26
|
exports.toWorkflowPersistFields = toWorkflowPersistFields;
|
|
25
27
|
exports.toChatPersistFields = toChatPersistFields;
|
|
@@ -233,9 +235,73 @@ function buildSynchronizedChatMessage(ctx) {
|
|
|
233
235
|
return chatTemplate('request_received');
|
|
234
236
|
}
|
|
235
237
|
}
|
|
236
|
-
|
|
238
|
+
/** Parse legacy English-only assignment comments used as workflow log suffixes. */
|
|
239
|
+
function tryParseRequestAssignedComment(comment) {
|
|
240
|
+
const text = comment?.trim();
|
|
241
|
+
if (!text)
|
|
242
|
+
return null;
|
|
243
|
+
const technicianMatch = text.match(/^Request assigned to technician (.+?) by (.+?) \((.+?) - (.+?)\)$/);
|
|
244
|
+
if (technicianMatch) {
|
|
245
|
+
return {
|
|
246
|
+
assignedUserName: technicianMatch[1].trim(),
|
|
247
|
+
roleName: technicianMatch[2].trim(),
|
|
248
|
+
actorUserName: technicianMatch[3].trim(),
|
|
249
|
+
employeeId: technicianMatch[4].trim(),
|
|
250
|
+
assigneeLabel: 'technician',
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const userMatch = text.match(/^Request assigned to (.+?) by (.+?) \((.+?) - (.+?)\)$/);
|
|
254
|
+
if (userMatch) {
|
|
255
|
+
return {
|
|
256
|
+
assignedUserName: userMatch[1].trim(),
|
|
257
|
+
roleName: userMatch[2].trim(),
|
|
258
|
+
actorUserName: userMatch[3].trim(),
|
|
259
|
+
employeeId: userMatch[4].trim(),
|
|
260
|
+
assigneeLabel: 'user',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/** Bilingual workflow content for approved assignment steps (HR, IT, Tender, etc.). */
|
|
266
|
+
function buildRequestApprovedAssignedWorkflowFields(params) {
|
|
267
|
+
const assignedUserEn = params.assignedUserName?.trim() || 'User';
|
|
268
|
+
const roleEn = params.roleName?.trim() || 'Approver';
|
|
269
|
+
const roleAr = resolveBilingualName(roleEn, params.roleArabicName);
|
|
270
|
+
const actorEn = params.actorUserName?.trim() || 'User';
|
|
271
|
+
const employeeId = String(params.employeeId ?? 'N/A');
|
|
272
|
+
const templateKey = params.assigneeLabel === 'technician'
|
|
273
|
+
? 'request_approved_assigned_to_technician'
|
|
274
|
+
: 'request_approved_assigned_to_user';
|
|
275
|
+
return renderSynchronizedWorkflowMessage(templateKey, {
|
|
276
|
+
assignedUserName: assignedUserEn,
|
|
277
|
+
roleName: roleEn,
|
|
278
|
+
actorUserName: actorEn,
|
|
279
|
+
employeeId,
|
|
280
|
+
}, {
|
|
281
|
+
assignedUserName: assignedUserEn,
|
|
282
|
+
roleName: roleAr,
|
|
283
|
+
actorUserName: actorEn,
|
|
284
|
+
employeeId,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
237
287
|
/** Standard workflow log UPDATE fields for all CAA module repositories. */
|
|
238
288
|
function buildWorkflowLogUpdateFields(params) {
|
|
289
|
+
if (params.approvalStatus === 'Approved') {
|
|
290
|
+
const assignment = tryParseRequestAssignedComment(params.comment);
|
|
291
|
+
if (assignment) {
|
|
292
|
+
const bilingual = buildRequestApprovedAssignedWorkflowFields({
|
|
293
|
+
...assignment,
|
|
294
|
+
roleName: params.roleName?.trim() || assignment.roleName,
|
|
295
|
+
roleArabicName: params.roleArabicName,
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
content: bilingual.en,
|
|
299
|
+
content_ar: bilingual.ar,
|
|
300
|
+
status: params.workflowStatus,
|
|
301
|
+
status_ar: resolveWorkflowStatusAr(params.workflowStatus),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
239
305
|
let action;
|
|
240
306
|
if (params.approvalStatus === 'Approved')
|
|
241
307
|
action = 'approved';
|
|
@@ -516,6 +582,22 @@ function buildItHelpdeskReassignModifiers(params) {
|
|
|
516
582
|
}
|
|
517
583
|
return { en, ar };
|
|
518
584
|
}
|
|
585
|
+
function resolveItHelpdeskGenericStatusAr(status) {
|
|
586
|
+
switch (status.trim()) {
|
|
587
|
+
case 'Approved':
|
|
588
|
+
return 'تمت الموافقة على';
|
|
589
|
+
case 'Rejected':
|
|
590
|
+
return 'تم رفض';
|
|
591
|
+
case 'Closed':
|
|
592
|
+
return 'تم إغلاق';
|
|
593
|
+
case 'Assigned':
|
|
594
|
+
return 'تم تعيين';
|
|
595
|
+
case 'Reassigned':
|
|
596
|
+
return 'تمت إعادة تعيين';
|
|
597
|
+
default:
|
|
598
|
+
return resolveChatStatusAr(status) ?? status.toLowerCase();
|
|
599
|
+
}
|
|
600
|
+
}
|
|
519
601
|
function buildItHelpdeskMuscatWorkflowLog(params) {
|
|
520
602
|
const comment = formatColonCommentSuffix('comment' in params ? params.comment : undefined);
|
|
521
603
|
const modifiers = 'modifiers' in params && params.modifiers ? params.modifiers : { en: '', ar: '' };
|
|
@@ -590,7 +672,7 @@ function buildItHelpdeskMuscatWorkflowLog(params) {
|
|
|
590
672
|
const approverEn = params.approverRoleName.trim();
|
|
591
673
|
const approverAr = resolveBilingualName(approverEn, params.approverRoleArabicName);
|
|
592
674
|
const statusEn = params.status.toLowerCase();
|
|
593
|
-
const statusAr =
|
|
675
|
+
const statusAr = resolveItHelpdeskGenericStatusAr(params.status);
|
|
594
676
|
return renderSynchronizedWorkflowMessage('it_helpdesk_wf_status_by_role', { status: statusEn, statusAr, approverRoleName: approverEn, comment }, { status: statusEn, statusAr, approverRoleName: approverAr, comment });
|
|
595
677
|
}
|
|
596
678
|
case 'technician_completed': {
|
|
@@ -692,13 +774,20 @@ function resolveArabicWorkflowActorName(actors, parsedEnglishName) {
|
|
|
692
774
|
}
|
|
693
775
|
/** Arabic role/user label for chat templates. */
|
|
694
776
|
function resolveArabicChatRoleName(actors, parsedEnglishRole) {
|
|
777
|
+
const parsed = parsedEnglishRole?.trim();
|
|
695
778
|
if (actors?.roleName?.trim()) {
|
|
696
|
-
|
|
779
|
+
const actorRole = actors.roleName.trim();
|
|
780
|
+
if (!parsed || parsed.toLowerCase() === actorRole.toLowerCase()) {
|
|
781
|
+
return resolveBilingualName(actorRole, actors.roleArabicName);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (actors?.roleArabicName?.trim() && parsed) {
|
|
785
|
+
return resolveBilingualName(parsed, actors.roleArabicName);
|
|
697
786
|
}
|
|
698
787
|
if (actors?.userName?.trim()) {
|
|
699
788
|
return resolveBilingualName(actors.userName, actors.userArabicName);
|
|
700
789
|
}
|
|
701
|
-
return resolveBilingualName(
|
|
790
|
+
return resolveBilingualName(parsed, actors?.roleArabicName);
|
|
702
791
|
}
|
|
703
792
|
function escapeRegexLiteral(value) {
|
|
704
793
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -707,16 +796,36 @@ function escapeRegexLiteral(value) {
|
|
|
707
796
|
function matchTemplateMessage(templateEn, message) {
|
|
708
797
|
if (!/\{\{\w+\}\}/.test(templateEn))
|
|
709
798
|
return null;
|
|
710
|
-
const
|
|
799
|
+
const captures = [];
|
|
711
800
|
let regexBody = '';
|
|
712
801
|
let cursor = 0;
|
|
802
|
+
let groupIndex = 0;
|
|
713
803
|
const tokenRe = /\{\{(\w+)\}\}/g;
|
|
714
804
|
let tokenMatch;
|
|
715
805
|
while ((tokenMatch = tokenRe.exec(templateEn)) !== null) {
|
|
716
806
|
regexBody += escapeRegexLiteral(templateEn.slice(cursor, tokenMatch.index));
|
|
717
807
|
const key = tokenMatch[1];
|
|
718
|
-
|
|
719
|
-
|
|
808
|
+
const afterToken = templateEn.slice(tokenMatch.index + tokenMatch[0].length);
|
|
809
|
+
if (key === 'comment') {
|
|
810
|
+
groupIndex += 1;
|
|
811
|
+
captures.push({ key, groupIndex });
|
|
812
|
+
regexBody += '(.*)';
|
|
813
|
+
cursor = tokenMatch.index + tokenMatch[0].length;
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (afterToken.startsWith('{{comment}}')) {
|
|
817
|
+
groupIndex += 1;
|
|
818
|
+
captures.push({ key, groupIndex });
|
|
819
|
+
groupIndex += 1;
|
|
820
|
+
captures.push({ key: 'comment', groupIndex });
|
|
821
|
+
regexBody += '([^:]+?)(: .*)?';
|
|
822
|
+
tokenRe.lastIndex = tokenMatch.index + tokenMatch[0].length + '{{comment}}'.length;
|
|
823
|
+
cursor = tokenRe.lastIndex;
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
groupIndex += 1;
|
|
827
|
+
captures.push({ key, groupIndex });
|
|
828
|
+
regexBody += '(.+?)';
|
|
720
829
|
cursor = tokenMatch.index + tokenMatch[0].length;
|
|
721
830
|
}
|
|
722
831
|
regexBody += escapeRegexLiteral(templateEn.slice(cursor));
|
|
@@ -724,8 +833,8 @@ function matchTemplateMessage(templateEn, message) {
|
|
|
724
833
|
if (!match)
|
|
725
834
|
return null;
|
|
726
835
|
const params = {};
|
|
727
|
-
|
|
728
|
-
params[key] = match[
|
|
836
|
+
captures.forEach(({ key, groupIndex: idx }) => {
|
|
837
|
+
params[key] = match[idx] ?? '';
|
|
729
838
|
});
|
|
730
839
|
return params;
|
|
731
840
|
}
|
|
@@ -741,14 +850,48 @@ function buildArabicChatTemplateParams(params, actors) {
|
|
|
741
850
|
}
|
|
742
851
|
return arParams;
|
|
743
852
|
}
|
|
853
|
+
function buildArabicWorkflowTemplateParams(params, actors) {
|
|
854
|
+
const arParams = { ...params };
|
|
855
|
+
if (params.roleName !== undefined) {
|
|
856
|
+
arParams.roleName = resolveArabicChatRoleName(actors, params.roleName.trim());
|
|
857
|
+
}
|
|
858
|
+
if (params.approverRoleName !== undefined) {
|
|
859
|
+
arParams.approverRoleName = resolveArabicChatRoleName(actors, params.approverRoleName.trim());
|
|
860
|
+
}
|
|
861
|
+
if (params.assignedRoleName !== undefined) {
|
|
862
|
+
arParams.assignedRoleName = resolveArabicChatRoleName(actors, params.assignedRoleName.trim());
|
|
863
|
+
}
|
|
864
|
+
if (params.name !== undefined) {
|
|
865
|
+
arParams.name = resolveArabicWorkflowActorName(actors, params.name.trim());
|
|
866
|
+
}
|
|
867
|
+
if (params.status !== undefined && params.statusAr === undefined) {
|
|
868
|
+
const normalizedStatus = params.status.charAt(0).toUpperCase() + params.status.slice(1).toLowerCase();
|
|
869
|
+
arParams.statusAr = resolveItHelpdeskGenericStatusAr(normalizedStatus);
|
|
870
|
+
}
|
|
871
|
+
return arParams;
|
|
872
|
+
}
|
|
873
|
+
function countTemplatePlaceholders(templateEn) {
|
|
874
|
+
return (templateEn.match(/\{\{\w+\}\}/g) || []).length;
|
|
875
|
+
}
|
|
876
|
+
function listWorkflowTemplatesBySpecificity() {
|
|
877
|
+
return Object.entries(WORKFLOW_TEMPLATES).sort(([, a], [, b]) => countTemplatePlaceholders(b.en) - countTemplatePlaceholders(a.en));
|
|
878
|
+
}
|
|
744
879
|
function resolveWorkflowContentAr(content, actors) {
|
|
745
880
|
if (content == null || content.trim() === '')
|
|
746
881
|
return null;
|
|
747
882
|
const normalized = content.trim();
|
|
748
883
|
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
749
|
-
if (tpl.en === normalized)
|
|
884
|
+
if (tpl.en === normalized || tpl.en.toLowerCase() === normalized.toLowerCase())
|
|
750
885
|
return tpl.ar;
|
|
751
886
|
}
|
|
887
|
+
for (const [, tpl] of listWorkflowTemplatesBySpecificity()) {
|
|
888
|
+
if (!/\{\{\w+\}\}/.test(tpl.en))
|
|
889
|
+
continue;
|
|
890
|
+
const params = matchTemplateMessage(tpl.en, normalized);
|
|
891
|
+
if (!params)
|
|
892
|
+
continue;
|
|
893
|
+
return interpolate(tpl.ar, buildArabicWorkflowTemplateParams(params, actors));
|
|
894
|
+
}
|
|
752
895
|
const commentIdx = normalized.indexOf(': ');
|
|
753
896
|
if (commentIdx > 0) {
|
|
754
897
|
const baseEn = normalized.slice(0, commentIdx).trim();
|
|
@@ -783,6 +926,28 @@ function resolveWorkflowContentAr(content, actors) {
|
|
|
783
926
|
const nameAr = resolveArabicWorkflowActorName(actors, namePartEn);
|
|
784
927
|
return interpolate(WORKFLOW_TEMPLATES.request_approval_pending_from.ar, { name: nameAr });
|
|
785
928
|
}
|
|
929
|
+
// Legacy HR worker strings (pre-i18n catalog)
|
|
930
|
+
if (normalized === 'Send Notification') {
|
|
931
|
+
return WORKFLOW_TEMPLATES.notification_sent_pending?.ar ?? null;
|
|
932
|
+
}
|
|
933
|
+
const notificationSuccessVariants = [
|
|
934
|
+
'notification sent successfully',
|
|
935
|
+
'notification sent sucessfully',
|
|
936
|
+
'notification sent',
|
|
937
|
+
];
|
|
938
|
+
if (notificationSuccessVariants.includes(normalized.toLowerCase())) {
|
|
939
|
+
return WORKFLOW_TEMPLATES.notification_sent_successfully?.ar ?? null;
|
|
940
|
+
}
|
|
941
|
+
const legacyPendingPrefix = 'Approval pending from ';
|
|
942
|
+
if (normalized.startsWith(legacyPendingPrefix)) {
|
|
943
|
+
const namePartEn = normalized.slice(legacyPendingPrefix.length).trim();
|
|
944
|
+
const nameAr = resolveArabicWorkflowActorName(actors, namePartEn);
|
|
945
|
+
return interpolate(WORKFLOW_TEMPLATES.request_approval_pending_from.ar, { name: nameAr });
|
|
946
|
+
}
|
|
947
|
+
if (normalized === 'Approval Pending from HR Section' ||
|
|
948
|
+
normalized === 'Approval Pending from Records Department') {
|
|
949
|
+
return WORKFLOW_TEMPLATES.request_approval_pending?.ar ?? null;
|
|
950
|
+
}
|
|
786
951
|
return null;
|
|
787
952
|
}
|
|
788
953
|
function resolveChatMessageAr(message, actors) {
|
|
@@ -812,9 +977,48 @@ function enrichWorkflowLog(log) {
|
|
|
812
977
|
const status = log.status != null ? String(log.status) : null;
|
|
813
978
|
const content = log.content != null ? String(log.content) : null;
|
|
814
979
|
const actors = extractBilingualActorsFromRecord(log);
|
|
980
|
+
const resolvedAr = resolveWorkflowContentAr(content, actors);
|
|
981
|
+
const existingAr = log.content_ar != null && String(log.content_ar).trim() !== ''
|
|
982
|
+
? String(log.content_ar)
|
|
983
|
+
: null;
|
|
984
|
+
// Re-resolve when stored AR still embeds the English actor parsed from `content`.
|
|
985
|
+
const pendingPrefix = WORKFLOW_TEMPLATES.request_approval_pending_from?.en.split('{{name}}')[0].trim();
|
|
986
|
+
let content_ar = existingAr ?? resolvedAr;
|
|
987
|
+
if (content &&
|
|
988
|
+
pendingPrefix &&
|
|
989
|
+
content.startsWith(pendingPrefix) &&
|
|
990
|
+
existingAr &&
|
|
991
|
+
resolvedAr &&
|
|
992
|
+
existingAr !== resolvedAr) {
|
|
993
|
+
const namePartEn = content.slice(pendingPrefix.length).trim();
|
|
994
|
+
if (namePartEn && existingAr.includes(namePartEn)) {
|
|
995
|
+
content_ar = resolvedAr;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (content &&
|
|
999
|
+
existingAr &&
|
|
1000
|
+
resolvedAr &&
|
|
1001
|
+
existingAr !== resolvedAr &&
|
|
1002
|
+
/Request assigned to/i.test(existingAr)) {
|
|
1003
|
+
content_ar = resolvedAr;
|
|
1004
|
+
}
|
|
1005
|
+
if (content &&
|
|
1006
|
+
existingAr &&
|
|
1007
|
+
resolvedAr &&
|
|
1008
|
+
existingAr !== resolvedAr &&
|
|
1009
|
+
actors?.roleName?.trim() &&
|
|
1010
|
+
actors?.roleArabicName?.trim() &&
|
|
1011
|
+
existingAr.includes(actors.roleName.trim())) {
|
|
1012
|
+
content_ar = resolvedAr;
|
|
1013
|
+
}
|
|
1014
|
+
if ((content_ar == null || content_ar === '') && content) {
|
|
1015
|
+
const notificationAr = resolveWorkflowContentAr(content, actors);
|
|
1016
|
+
if (notificationAr)
|
|
1017
|
+
content_ar = notificationAr;
|
|
1018
|
+
}
|
|
815
1019
|
return {
|
|
816
1020
|
...log,
|
|
817
|
-
content_ar
|
|
1021
|
+
content_ar,
|
|
818
1022
|
status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
|
|
819
1023
|
};
|
|
820
1024
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -409,6 +409,7 @@ export { RespondToEnquiriesRequestAttachment } from './models/RespondToEnquiries
|
|
|
409
409
|
export * from './models/RequestATenderRequestModel';
|
|
410
410
|
export * from './models/RequestTenderAnalysisRequestModel';
|
|
411
411
|
export * from './models/ContractServiceRequestModel';
|
|
412
|
+
export * from './models/ContractServiceRequestDocumentModel';
|
|
412
413
|
export * from './models/SlaConfigModel';
|
|
413
414
|
export * from './models/SlaRequestModel';
|
|
414
415
|
export * from './models/SlaMyRequestsViewModel';
|
package/dist/index.js
CHANGED
|
@@ -598,6 +598,7 @@ Object.defineProperty(exports, "RespondToEnquiriesRequestAttachment", { enumerab
|
|
|
598
598
|
__exportStar(require("./models/RequestATenderRequestModel"), exports);
|
|
599
599
|
__exportStar(require("./models/RequestTenderAnalysisRequestModel"), exports);
|
|
600
600
|
__exportStar(require("./models/ContractServiceRequestModel"), exports);
|
|
601
|
+
__exportStar(require("./models/ContractServiceRequestDocumentModel"), exports);
|
|
601
602
|
__exportStar(require("./models/SlaConfigModel"), exports);
|
|
602
603
|
__exportStar(require("./models/SlaRequestModel"), exports);
|
|
603
604
|
__exportStar(require("./models/SlaMyRequestsViewModel"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseModel } from './BaseModel';
|
|
2
|
+
export declare class ContractServiceRequestDocument extends BaseModel {
|
|
3
|
+
request_id: number;
|
|
4
|
+
service_id: number | null;
|
|
5
|
+
sub_service_id: number | null;
|
|
6
|
+
document_name: string;
|
|
7
|
+
description: string | null;
|
|
8
|
+
file_url: string;
|
|
9
|
+
file_name: string | null;
|
|
10
|
+
file_type: string | null;
|
|
11
|
+
file_size: number | null;
|
|
12
|
+
uploaded_by: number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ContractServiceRequestDocument = void 0;
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
const BaseModel_1 = require("./BaseModel");
|
|
15
|
+
let ContractServiceRequestDocument = class ContractServiceRequestDocument extends BaseModel_1.BaseModel {
|
|
16
|
+
};
|
|
17
|
+
exports.ContractServiceRequestDocument = ContractServiceRequestDocument;
|
|
18
|
+
__decorate([
|
|
19
|
+
(0, typeorm_1.Column)({ type: 'integer', nullable: false }),
|
|
20
|
+
__metadata("design:type", Number)
|
|
21
|
+
], ContractServiceRequestDocument.prototype, "request_id", void 0);
|
|
22
|
+
__decorate([
|
|
23
|
+
(0, typeorm_1.Column)({ type: 'integer', nullable: true }),
|
|
24
|
+
__metadata("design:type", Object)
|
|
25
|
+
], ContractServiceRequestDocument.prototype, "service_id", void 0);
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, typeorm_1.Column)({ type: 'integer', nullable: true }),
|
|
28
|
+
__metadata("design:type", Object)
|
|
29
|
+
], ContractServiceRequestDocument.prototype, "sub_service_id", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: false }),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], ContractServiceRequestDocument.prototype, "document_name", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
|
|
36
|
+
__metadata("design:type", Object)
|
|
37
|
+
], ContractServiceRequestDocument.prototype, "description", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: false }),
|
|
40
|
+
__metadata("design:type", String)
|
|
41
|
+
], ContractServiceRequestDocument.prototype, "file_url", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
|
|
44
|
+
__metadata("design:type", Object)
|
|
45
|
+
], ContractServiceRequestDocument.prototype, "file_name", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
|
|
48
|
+
__metadata("design:type", Object)
|
|
49
|
+
], ContractServiceRequestDocument.prototype, "file_type", void 0);
|
|
50
|
+
__decorate([
|
|
51
|
+
(0, typeorm_1.Column)({ type: 'bigint', nullable: true }),
|
|
52
|
+
__metadata("design:type", Object)
|
|
53
|
+
], ContractServiceRequestDocument.prototype, "file_size", void 0);
|
|
54
|
+
__decorate([
|
|
55
|
+
(0, typeorm_1.Column)({ type: 'integer', nullable: false }),
|
|
56
|
+
__metadata("design:type", Number)
|
|
57
|
+
], ContractServiceRequestDocument.prototype, "uploaded_by", void 0);
|
|
58
|
+
exports.ContractServiceRequestDocument = ContractServiceRequestDocument = __decorate([
|
|
59
|
+
(0, typeorm_1.Entity)({ name: 'contract_service_request_documents' })
|
|
60
|
+
], ContractServiceRequestDocument);
|
|
@@ -14,6 +14,8 @@ export declare class ContractServiceRequest extends BaseModel {
|
|
|
14
14
|
request_id: string | null;
|
|
15
15
|
project_or_budget_code: string;
|
|
16
16
|
company_name: string;
|
|
17
|
+
company_phone_number: string | null;
|
|
18
|
+
notes: string | null;
|
|
17
19
|
status: RespondToEnquiriesRequestStatus;
|
|
18
20
|
reviewer_user_id: number | null;
|
|
19
21
|
assigned_to_user_id: number | null;
|
|
@@ -68,6 +68,14 @@ __decorate([
|
|
|
68
68
|
(0, typeorm_1.Column)({ type: "varchar", length: 255, nullable: false }),
|
|
69
69
|
__metadata("design:type", String)
|
|
70
70
|
], ContractServiceRequest.prototype, "company_name", void 0);
|
|
71
|
+
__decorate([
|
|
72
|
+
(0, typeorm_1.Column)({ type: "varchar", length: 30, nullable: true }),
|
|
73
|
+
__metadata("design:type", Object)
|
|
74
|
+
], ContractServiceRequest.prototype, "company_phone_number", void 0);
|
|
75
|
+
__decorate([
|
|
76
|
+
(0, typeorm_1.Column)({ type: "text", nullable: true }),
|
|
77
|
+
__metadata("design:type", Object)
|
|
78
|
+
], ContractServiceRequest.prototype, "notes", void 0);
|
|
71
79
|
__decorate([
|
|
72
80
|
(0, typeorm_1.Column)({
|
|
73
81
|
type: "enum",
|
|
@@ -13,4 +13,8 @@ export declare class FollowUpReportItem extends BaseModel {
|
|
|
13
13
|
action_status: string | null;
|
|
14
14
|
action_taken: string | null;
|
|
15
15
|
delay_period: string | null;
|
|
16
|
+
file_url: string | null;
|
|
17
|
+
file_name: string | null;
|
|
18
|
+
file_type: string | null;
|
|
19
|
+
file_size: number | null;
|
|
16
20
|
}
|
|
@@ -69,6 +69,22 @@ __decorate([
|
|
|
69
69
|
(0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
|
|
70
70
|
__metadata("design:type", Object)
|
|
71
71
|
], FollowUpReportItem.prototype, "delay_period", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: true }),
|
|
74
|
+
__metadata("design:type", Object)
|
|
75
|
+
], FollowUpReportItem.prototype, "file_url", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
|
|
78
|
+
__metadata("design:type", Object)
|
|
79
|
+
], FollowUpReportItem.prototype, "file_name", void 0);
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
|
|
82
|
+
__metadata("design:type", Object)
|
|
83
|
+
], FollowUpReportItem.prototype, "file_type", void 0);
|
|
84
|
+
__decorate([
|
|
85
|
+
(0, typeorm_1.Column)({ type: 'bigint', nullable: true }),
|
|
86
|
+
__metadata("design:type", Object)
|
|
87
|
+
], FollowUpReportItem.prototype, "file_size", void 0);
|
|
72
88
|
exports.FollowUpReportItem = FollowUpReportItem = __decorate([
|
|
73
89
|
(0, typeorm_1.Entity)({ name: 'followup_report_items' })
|
|
74
90
|
], FollowUpReportItem);
|
|
@@ -5,8 +5,14 @@ export declare enum FollowUpReportSubjectClassification {
|
|
|
5
5
|
VERY_URGENT = "Very Urgent",
|
|
6
6
|
CONFIDENTIAL = "Confidential",
|
|
7
7
|
INFORMATIONAL = "Informational",
|
|
8
|
-
NORMAL = "Normal"
|
|
8
|
+
NORMAL = "Normal",
|
|
9
|
+
IMPORTANT = "Important",
|
|
10
|
+
HIGHLY_CONFIDENTIAL = "Highly Confidential",
|
|
11
|
+
RESTRICTED = "Restricted",
|
|
12
|
+
LIMITED = "Limited"
|
|
9
13
|
}
|
|
14
|
+
/** Allowed API/DB values for follow-up report subject classification. */
|
|
15
|
+
export declare const FOLLOW_UP_REPORT_SUBJECT_CLASSIFICATION_VALUES: FollowUpReportSubjectClassification[];
|
|
10
16
|
/** Concerned department under General Manager of Support Services */
|
|
11
17
|
export declare enum FollowUpReportConcernedDepartment {
|
|
12
18
|
IT = "IT",
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.FollowUpReportRequests = exports.FollowUpReportRequestStatus = exports.FollowUpReportConcernedDepartment = exports.FollowUpReportSubjectClassification = void 0;
|
|
12
|
+
exports.FollowUpReportRequests = exports.FollowUpReportRequestStatus = exports.FollowUpReportConcernedDepartment = exports.FOLLOW_UP_REPORT_SUBJECT_CLASSIFICATION_VALUES = exports.FollowUpReportSubjectClassification = void 0;
|
|
13
13
|
const typeorm_1 = require("typeorm");
|
|
14
14
|
const BaseModel_1 = require("./BaseModel");
|
|
15
15
|
/** Subject / handling priority classification */
|
|
@@ -20,7 +20,13 @@ var FollowUpReportSubjectClassification;
|
|
|
20
20
|
FollowUpReportSubjectClassification["CONFIDENTIAL"] = "Confidential";
|
|
21
21
|
FollowUpReportSubjectClassification["INFORMATIONAL"] = "Informational";
|
|
22
22
|
FollowUpReportSubjectClassification["NORMAL"] = "Normal";
|
|
23
|
+
FollowUpReportSubjectClassification["IMPORTANT"] = "Important";
|
|
24
|
+
FollowUpReportSubjectClassification["HIGHLY_CONFIDENTIAL"] = "Highly Confidential";
|
|
25
|
+
FollowUpReportSubjectClassification["RESTRICTED"] = "Restricted";
|
|
26
|
+
FollowUpReportSubjectClassification["LIMITED"] = "Limited";
|
|
23
27
|
})(FollowUpReportSubjectClassification || (exports.FollowUpReportSubjectClassification = FollowUpReportSubjectClassification = {}));
|
|
28
|
+
/** Allowed API/DB values for follow-up report subject classification. */
|
|
29
|
+
exports.FOLLOW_UP_REPORT_SUBJECT_CLASSIFICATION_VALUES = Object.values(FollowUpReportSubjectClassification);
|
|
24
30
|
/** Concerned department under General Manager of Support Services */
|
|
25
31
|
var FollowUpReportConcernedDepartment;
|
|
26
32
|
(function (FollowUpReportConcernedDepartment) {
|
|
@@ -9,5 +9,6 @@ export declare class RespondToEnquiriesRequestAttachment extends BaseModel {
|
|
|
9
9
|
file_type: string;
|
|
10
10
|
file_size: number | null;
|
|
11
11
|
description: string;
|
|
12
|
-
|
|
12
|
+
document_name: string | null;
|
|
13
|
+
constructor(request_id: number, uploaded_by: number, file_url: string, service_id?: number | null, sub_service_id?: number | null, file_name?: string, file_type?: string, file_size?: number, description?: string, document_name?: string);
|
|
13
14
|
}
|
|
@@ -13,7 +13,7 @@ exports.RespondToEnquiriesRequestAttachment = void 0;
|
|
|
13
13
|
const typeorm_1 = require("typeorm");
|
|
14
14
|
const BaseModel_1 = require("./BaseModel");
|
|
15
15
|
let RespondToEnquiriesRequestAttachment = class RespondToEnquiriesRequestAttachment extends BaseModel_1.BaseModel {
|
|
16
|
-
constructor(request_id, uploaded_by, file_url, service_id, sub_service_id, file_name, file_type, file_size, description) {
|
|
16
|
+
constructor(request_id, uploaded_by, file_url, service_id, sub_service_id, file_name, file_type, file_size, description, document_name) {
|
|
17
17
|
super();
|
|
18
18
|
this.request_id = request_id;
|
|
19
19
|
this.service_id = service_id || null;
|
|
@@ -24,6 +24,7 @@ let RespondToEnquiriesRequestAttachment = class RespondToEnquiriesRequestAttachm
|
|
|
24
24
|
this.file_type = file_type || "";
|
|
25
25
|
this.file_size = file_size || null;
|
|
26
26
|
this.description = description || "";
|
|
27
|
+
this.document_name = document_name || null;
|
|
27
28
|
}
|
|
28
29
|
};
|
|
29
30
|
exports.RespondToEnquiriesRequestAttachment = RespondToEnquiriesRequestAttachment;
|
|
@@ -63,7 +64,11 @@ __decorate([
|
|
|
63
64
|
(0, typeorm_1.Column)({ type: "text", nullable: true }),
|
|
64
65
|
__metadata("design:type", String)
|
|
65
66
|
], RespondToEnquiriesRequestAttachment.prototype, "description", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
(0, typeorm_1.Column)({ type: "varchar", length: 500, nullable: true }),
|
|
69
|
+
__metadata("design:type", Object)
|
|
70
|
+
], RespondToEnquiriesRequestAttachment.prototype, "document_name", void 0);
|
|
66
71
|
exports.RespondToEnquiriesRequestAttachment = RespondToEnquiriesRequestAttachment = __decorate([
|
|
67
72
|
(0, typeorm_1.Entity)({ name: "respond_to_enquiries_request_attachments" }),
|
|
68
|
-
__metadata("design:paramtypes", [Number, Number, String, Object, Object, String, String, Number, String])
|
|
73
|
+
__metadata("design:paramtypes", [Number, Number, String, Object, Object, String, String, Number, String, String])
|
|
69
74
|
], RespondToEnquiriesRequestAttachment);
|
package/package.json
CHANGED
|
@@ -30,3 +30,43 @@ ALTER TABLE request_a_tender_requests
|
|
|
30
30
|
ALTER TABLE cyber_security_risk_management_risks
|
|
31
31
|
ADD COLUMN IF NOT EXISTS treatment TEXT,
|
|
32
32
|
ADD COLUMN IF NOT EXISTS action TEXT;
|
|
33
|
+
|
|
34
|
+
-- 7. Follow-up report: subject_classification enum values
|
|
35
|
+
ALTER TYPE maintenance_subject_classification_en ADD VALUE IF NOT EXISTS 'Important';
|
|
36
|
+
ALTER TYPE maintenance_subject_classification_en ADD VALUE IF NOT EXISTS 'Highly Confidential';
|
|
37
|
+
ALTER TYPE maintenance_subject_classification_en ADD VALUE IF NOT EXISTS 'Restricted';
|
|
38
|
+
ALTER TYPE maintenance_subject_classification_en ADD VALUE IF NOT EXISTS 'Limited';
|
|
39
|
+
|
|
40
|
+
-- 8. Contract service request: notes, company phone, contract documents table
|
|
41
|
+
ALTER TABLE contract_service_requests
|
|
42
|
+
ADD COLUMN IF NOT EXISTS notes TEXT,
|
|
43
|
+
ADD COLUMN IF NOT EXISTS company_phone_number VARCHAR(30);
|
|
44
|
+
|
|
45
|
+
CREATE TABLE IF NOT EXISTS contract_service_request_documents (
|
|
46
|
+
id SERIAL PRIMARY KEY,
|
|
47
|
+
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
48
|
+
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
49
|
+
created_by INT,
|
|
50
|
+
updated_by INT,
|
|
51
|
+
is_deleted BOOLEAN DEFAULT FALSE,
|
|
52
|
+
request_id INT NOT NULL,
|
|
53
|
+
service_id INT,
|
|
54
|
+
sub_service_id INT,
|
|
55
|
+
document_name VARCHAR(500) NOT NULL,
|
|
56
|
+
description TEXT,
|
|
57
|
+
file_url VARCHAR(500) NOT NULL,
|
|
58
|
+
file_name VARCHAR(255),
|
|
59
|
+
file_type VARCHAR(100),
|
|
60
|
+
file_size BIGINT,
|
|
61
|
+
uploaded_by INT NOT NULL
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
ALTER TABLE respond_to_enquiries_request_attachments
|
|
65
|
+
ADD COLUMN IF NOT EXISTS document_name VARCHAR(500);
|
|
66
|
+
|
|
67
|
+
-- 9. Follow-up report items: per-item attachment fields
|
|
68
|
+
ALTER TABLE followup_report_items
|
|
69
|
+
ADD COLUMN IF NOT EXISTS file_url VARCHAR(500),
|
|
70
|
+
ADD COLUMN IF NOT EXISTS file_name VARCHAR(255),
|
|
71
|
+
ADD COLUMN IF NOT EXISTS file_type VARCHAR(100),
|
|
72
|
+
ADD COLUMN IF NOT EXISTS file_size BIGINT;
|
package/src/data-source.ts
CHANGED
|
@@ -269,6 +269,7 @@ import { ComplaintLostPropertyReportChat } from './models/ComplaintLostPropertyR
|
|
|
269
269
|
import { ComplaintLostPropertyReportRequest } from './models/ComplaintLostPropertyReportRequestModel';
|
|
270
270
|
import { ComplaintLostPropertyReportWorkflow } from './models/ComplaintLostPropertyReportWorkflowModel';
|
|
271
271
|
import { ContractServiceRequest } from './models/ContractServiceRequestModel';
|
|
272
|
+
import { ContractServiceRequestDocument } from './models/ContractServiceRequestDocumentModel';
|
|
272
273
|
import { CountryMaster } from './models/CountryMasterModel';
|
|
273
274
|
import { CyberSecurityAuditRequest } from './models/CyberSecurityAuditRequestModel';
|
|
274
275
|
import { CyberSecurityRiskManagementRequest } from './models/CyberSecurityRiskManagementRequestModel';
|
|
@@ -593,6 +594,7 @@ export const AppDataSource = new DataSource({
|
|
|
593
594
|
ComplaintLostPropertyReportRequest,
|
|
594
595
|
ComplaintLostPropertyReportWorkflow,
|
|
595
596
|
ContractServiceRequest,
|
|
597
|
+
ContractServiceRequestDocument,
|
|
596
598
|
CountryMaster,
|
|
597
599
|
CyberSecurityAuditRequest,
|
|
598
600
|
CyberSecurityRiskManagementRequest,
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
"en": "Request Approved{{comment}}",
|
|
41
41
|
"ar": "تمت الموافقة على الطلب{{comment}}"
|
|
42
42
|
},
|
|
43
|
+
"request_approved_assigned_to_user": {
|
|
44
|
+
"en": "Request Approved: Request assigned to {{assignedUserName}} by {{roleName}} ({{actorUserName}} - {{employeeId}})",
|
|
45
|
+
"ar": "تمت الموافقة على الطلب: تم تعيين الطلب إلى {{assignedUserName}} من قبل {{roleName}} ({{actorUserName}} - {{employeeId}})"
|
|
46
|
+
},
|
|
47
|
+
"request_approved_assigned_to_technician": {
|
|
48
|
+
"en": "Request Approved: Request assigned to technician {{assignedUserName}} by {{roleName}} ({{actorUserName}} - {{employeeId}})",
|
|
49
|
+
"ar": "تمت الموافقة على الطلب: تم تعيين الطلب إلى الفني {{assignedUserName}} من قبل {{roleName}} ({{actorUserName}} - {{employeeId}})"
|
|
50
|
+
},
|
|
43
51
|
"request_rejected": {
|
|
44
52
|
"en": "Request Rejected{{comment}}",
|
|
45
53
|
"ar": "تم رفض الطلب{{comment}}"
|
|
@@ -106,7 +114,7 @@
|
|
|
106
114
|
},
|
|
107
115
|
"it_helpdesk_wf_status_by_role": {
|
|
108
116
|
"en": "Request {{status}} by {{approverRoleName}}{{comment}}",
|
|
109
|
-
"ar": "
|
|
117
|
+
"ar": "{{statusAr}} الطلب من قبل {{approverRoleName}}{{comment}}"
|
|
110
118
|
},
|
|
111
119
|
"it_helpdesk_wf_l3_step_rejected": {
|
|
112
120
|
"en": "{{roleName}} rejected the request{{comment}}",
|
|
@@ -344,6 +344,81 @@ export function buildSynchronizedChatMessage(ctx: ChatMessageContext): Bilingual
|
|
|
344
344
|
|
|
345
345
|
// ─── Backward-compatible aliases used by Cancellation pilot ───────────────────
|
|
346
346
|
|
|
347
|
+
export type RequestApprovedAssignedWorkflowParams = {
|
|
348
|
+
assignedUserName: string;
|
|
349
|
+
roleName: string;
|
|
350
|
+
roleArabicName?: string | null;
|
|
351
|
+
actorUserName: string;
|
|
352
|
+
employeeId: string | number;
|
|
353
|
+
assigneeLabel?: 'user' | 'technician';
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
/** Parse legacy English-only assignment comments used as workflow log suffixes. */
|
|
357
|
+
export function tryParseRequestAssignedComment(
|
|
358
|
+
comment?: string | null,
|
|
359
|
+
): Omit<RequestApprovedAssignedWorkflowParams, 'roleArabicName'> | null {
|
|
360
|
+
const text = comment?.trim();
|
|
361
|
+
if (!text) return null;
|
|
362
|
+
|
|
363
|
+
const technicianMatch = text.match(
|
|
364
|
+
/^Request assigned to technician (.+?) by (.+?) \((.+?) - (.+?)\)$/,
|
|
365
|
+
);
|
|
366
|
+
if (technicianMatch) {
|
|
367
|
+
return {
|
|
368
|
+
assignedUserName: technicianMatch[1].trim(),
|
|
369
|
+
roleName: technicianMatch[2].trim(),
|
|
370
|
+
actorUserName: technicianMatch[3].trim(),
|
|
371
|
+
employeeId: technicianMatch[4].trim(),
|
|
372
|
+
assigneeLabel: 'technician',
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const userMatch = text.match(/^Request assigned to (.+?) by (.+?) \((.+?) - (.+?)\)$/);
|
|
377
|
+
if (userMatch) {
|
|
378
|
+
return {
|
|
379
|
+
assignedUserName: userMatch[1].trim(),
|
|
380
|
+
roleName: userMatch[2].trim(),
|
|
381
|
+
actorUserName: userMatch[3].trim(),
|
|
382
|
+
employeeId: userMatch[4].trim(),
|
|
383
|
+
assigneeLabel: 'user',
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Bilingual workflow content for approved assignment steps (HR, IT, Tender, etc.). */
|
|
391
|
+
export function buildRequestApprovedAssignedWorkflowFields(
|
|
392
|
+
params: RequestApprovedAssignedWorkflowParams,
|
|
393
|
+
): BilingualText {
|
|
394
|
+
const assignedUserEn = params.assignedUserName?.trim() || 'User';
|
|
395
|
+
const roleEn = params.roleName?.trim() || 'Approver';
|
|
396
|
+
const roleAr = resolveBilingualName(roleEn, params.roleArabicName);
|
|
397
|
+
const actorEn = params.actorUserName?.trim() || 'User';
|
|
398
|
+
const employeeId = String(params.employeeId ?? 'N/A');
|
|
399
|
+
|
|
400
|
+
const templateKey =
|
|
401
|
+
params.assigneeLabel === 'technician'
|
|
402
|
+
? 'request_approved_assigned_to_technician'
|
|
403
|
+
: 'request_approved_assigned_to_user';
|
|
404
|
+
|
|
405
|
+
return renderSynchronizedWorkflowMessage(
|
|
406
|
+
templateKey,
|
|
407
|
+
{
|
|
408
|
+
assignedUserName: assignedUserEn,
|
|
409
|
+
roleName: roleEn,
|
|
410
|
+
actorUserName: actorEn,
|
|
411
|
+
employeeId,
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
assignedUserName: assignedUserEn,
|
|
415
|
+
roleName: roleAr,
|
|
416
|
+
actorUserName: actorEn,
|
|
417
|
+
employeeId,
|
|
418
|
+
},
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
347
422
|
/** Standard workflow log UPDATE fields for all CAA module repositories. */
|
|
348
423
|
export function buildWorkflowLogUpdateFields(params: {
|
|
349
424
|
approvalStatus: string;
|
|
@@ -352,6 +427,23 @@ export function buildWorkflowLogUpdateFields(params: {
|
|
|
352
427
|
roleArabicName?: string | null;
|
|
353
428
|
comment?: string | null;
|
|
354
429
|
}): { content: string; content_ar: string; status: string; status_ar: string | null } {
|
|
430
|
+
if (params.approvalStatus === 'Approved') {
|
|
431
|
+
const assignment = tryParseRequestAssignedComment(params.comment);
|
|
432
|
+
if (assignment) {
|
|
433
|
+
const bilingual = buildRequestApprovedAssignedWorkflowFields({
|
|
434
|
+
...assignment,
|
|
435
|
+
roleName: params.roleName?.trim() || assignment.roleName,
|
|
436
|
+
roleArabicName: params.roleArabicName,
|
|
437
|
+
});
|
|
438
|
+
return {
|
|
439
|
+
content: bilingual.en,
|
|
440
|
+
content_ar: bilingual.ar,
|
|
441
|
+
status: params.workflowStatus,
|
|
442
|
+
status_ar: resolveWorkflowStatusAr(params.workflowStatus),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
355
447
|
let action: WorkflowMessageAction;
|
|
356
448
|
if (params.approvalStatus === 'Approved') action = 'approved';
|
|
357
449
|
else if (params.approvalStatus === 'Rejected') action = 'rejected';
|
|
@@ -845,6 +937,23 @@ export type ItHelpdeskMuscatWorkflowParams =
|
|
|
845
937
|
}
|
|
846
938
|
| { variant: 'routing_resolved' };
|
|
847
939
|
|
|
940
|
+
function resolveItHelpdeskGenericStatusAr(status: string): string {
|
|
941
|
+
switch (status.trim()) {
|
|
942
|
+
case 'Approved':
|
|
943
|
+
return 'تمت الموافقة على';
|
|
944
|
+
case 'Rejected':
|
|
945
|
+
return 'تم رفض';
|
|
946
|
+
case 'Closed':
|
|
947
|
+
return 'تم إغلاق';
|
|
948
|
+
case 'Assigned':
|
|
949
|
+
return 'تم تعيين';
|
|
950
|
+
case 'Reassigned':
|
|
951
|
+
return 'تمت إعادة تعيين';
|
|
952
|
+
default:
|
|
953
|
+
return resolveChatStatusAr(status) ?? status.toLowerCase();
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
848
957
|
export function buildItHelpdeskMuscatWorkflowLog(params: ItHelpdeskMuscatWorkflowParams): BilingualText {
|
|
849
958
|
const comment = formatColonCommentSuffix(
|
|
850
959
|
'comment' in params ? params.comment : undefined,
|
|
@@ -967,7 +1076,7 @@ export function buildItHelpdeskMuscatWorkflowLog(params: ItHelpdeskMuscatWorkflo
|
|
|
967
1076
|
const approverEn = params.approverRoleName.trim();
|
|
968
1077
|
const approverAr = resolveBilingualName(approverEn, params.approverRoleArabicName);
|
|
969
1078
|
const statusEn = params.status.toLowerCase();
|
|
970
|
-
const statusAr =
|
|
1079
|
+
const statusAr = resolveItHelpdeskGenericStatusAr(params.status);
|
|
971
1080
|
return renderSynchronizedWorkflowMessage(
|
|
972
1081
|
'it_helpdesk_wf_status_by_role',
|
|
973
1082
|
{ status: statusEn, statusAr, approverRoleName: approverEn, comment },
|
|
@@ -1126,13 +1235,20 @@ function resolveArabicChatRoleName(
|
|
|
1126
1235
|
actors: BilingualActorNames | undefined,
|
|
1127
1236
|
parsedEnglishRole?: string,
|
|
1128
1237
|
): string {
|
|
1238
|
+
const parsed = parsedEnglishRole?.trim();
|
|
1129
1239
|
if (actors?.roleName?.trim()) {
|
|
1130
|
-
|
|
1240
|
+
const actorRole = actors.roleName.trim();
|
|
1241
|
+
if (!parsed || parsed.toLowerCase() === actorRole.toLowerCase()) {
|
|
1242
|
+
return resolveBilingualName(actorRole, actors.roleArabicName);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
if (actors?.roleArabicName?.trim() && parsed) {
|
|
1246
|
+
return resolveBilingualName(parsed, actors.roleArabicName);
|
|
1131
1247
|
}
|
|
1132
1248
|
if (actors?.userName?.trim()) {
|
|
1133
1249
|
return resolveBilingualName(actors.userName, actors.userArabicName);
|
|
1134
1250
|
}
|
|
1135
|
-
return resolveBilingualName(
|
|
1251
|
+
return resolveBilingualName(parsed, actors?.roleArabicName);
|
|
1136
1252
|
}
|
|
1137
1253
|
|
|
1138
1254
|
function escapeRegexLiteral(value: string): string {
|
|
@@ -1146,17 +1262,40 @@ function matchTemplateMessage(
|
|
|
1146
1262
|
): Record<string, string> | null {
|
|
1147
1263
|
if (!/\{\{\w+\}\}/.test(templateEn)) return null;
|
|
1148
1264
|
|
|
1149
|
-
const
|
|
1265
|
+
const captures: Array<{ key: string; groupIndex: number }> = [];
|
|
1150
1266
|
let regexBody = '';
|
|
1151
1267
|
let cursor = 0;
|
|
1268
|
+
let groupIndex = 0;
|
|
1152
1269
|
const tokenRe = /\{\{(\w+)\}\}/g;
|
|
1153
1270
|
let tokenMatch: RegExpExecArray | null;
|
|
1154
1271
|
|
|
1155
1272
|
while ((tokenMatch = tokenRe.exec(templateEn)) !== null) {
|
|
1156
1273
|
regexBody += escapeRegexLiteral(templateEn.slice(cursor, tokenMatch.index));
|
|
1157
1274
|
const key = tokenMatch[1];
|
|
1158
|
-
|
|
1159
|
-
|
|
1275
|
+
const afterToken = templateEn.slice(tokenMatch.index + tokenMatch[0].length);
|
|
1276
|
+
|
|
1277
|
+
if (key === 'comment') {
|
|
1278
|
+
groupIndex += 1;
|
|
1279
|
+
captures.push({ key, groupIndex });
|
|
1280
|
+
regexBody += '(.*)';
|
|
1281
|
+
cursor = tokenMatch.index + tokenMatch[0].length;
|
|
1282
|
+
continue;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
if (afterToken.startsWith('{{comment}}')) {
|
|
1286
|
+
groupIndex += 1;
|
|
1287
|
+
captures.push({ key, groupIndex });
|
|
1288
|
+
groupIndex += 1;
|
|
1289
|
+
captures.push({ key: 'comment', groupIndex });
|
|
1290
|
+
regexBody += '([^:]+?)(: .*)?';
|
|
1291
|
+
tokenRe.lastIndex = tokenMatch.index + tokenMatch[0].length + '{{comment}}'.length;
|
|
1292
|
+
cursor = tokenRe.lastIndex;
|
|
1293
|
+
continue;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
groupIndex += 1;
|
|
1297
|
+
captures.push({ key, groupIndex });
|
|
1298
|
+
regexBody += '(.+?)';
|
|
1160
1299
|
cursor = tokenMatch.index + tokenMatch[0].length;
|
|
1161
1300
|
}
|
|
1162
1301
|
regexBody += escapeRegexLiteral(templateEn.slice(cursor));
|
|
@@ -1165,8 +1304,8 @@ function matchTemplateMessage(
|
|
|
1165
1304
|
if (!match) return null;
|
|
1166
1305
|
|
|
1167
1306
|
const params: Record<string, string> = {};
|
|
1168
|
-
|
|
1169
|
-
params[key] = match[
|
|
1307
|
+
captures.forEach(({ key, groupIndex: idx }) => {
|
|
1308
|
+
params[key] = match[idx] ?? '';
|
|
1170
1309
|
});
|
|
1171
1310
|
return params;
|
|
1172
1311
|
}
|
|
@@ -1187,6 +1326,41 @@ function buildArabicChatTemplateParams(
|
|
|
1187
1326
|
return arParams;
|
|
1188
1327
|
}
|
|
1189
1328
|
|
|
1329
|
+
function buildArabicWorkflowTemplateParams(
|
|
1330
|
+
params: Record<string, string>,
|
|
1331
|
+
actors?: BilingualActorNames,
|
|
1332
|
+
): Record<string, string> {
|
|
1333
|
+
const arParams: Record<string, string> = { ...params };
|
|
1334
|
+
if (params.roleName !== undefined) {
|
|
1335
|
+
arParams.roleName = resolveArabicChatRoleName(actors, params.roleName.trim());
|
|
1336
|
+
}
|
|
1337
|
+
if (params.approverRoleName !== undefined) {
|
|
1338
|
+
arParams.approverRoleName = resolveArabicChatRoleName(actors, params.approverRoleName.trim());
|
|
1339
|
+
}
|
|
1340
|
+
if (params.assignedRoleName !== undefined) {
|
|
1341
|
+
arParams.assignedRoleName = resolveArabicChatRoleName(actors, params.assignedRoleName.trim());
|
|
1342
|
+
}
|
|
1343
|
+
if (params.name !== undefined) {
|
|
1344
|
+
arParams.name = resolveArabicWorkflowActorName(actors, params.name.trim());
|
|
1345
|
+
}
|
|
1346
|
+
if (params.status !== undefined && params.statusAr === undefined) {
|
|
1347
|
+
const normalizedStatus =
|
|
1348
|
+
params.status.charAt(0).toUpperCase() + params.status.slice(1).toLowerCase();
|
|
1349
|
+
arParams.statusAr = resolveItHelpdeskGenericStatusAr(normalizedStatus);
|
|
1350
|
+
}
|
|
1351
|
+
return arParams;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function countTemplatePlaceholders(templateEn: string): number {
|
|
1355
|
+
return (templateEn.match(/\{\{\w+\}\}/g) || []).length;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function listWorkflowTemplatesBySpecificity(): Array<[string, BilingualText]> {
|
|
1359
|
+
return Object.entries(WORKFLOW_TEMPLATES).sort(
|
|
1360
|
+
([, a], [, b]) => countTemplatePlaceholders(b.en) - countTemplatePlaceholders(a.en),
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1190
1364
|
export function resolveWorkflowContentAr(
|
|
1191
1365
|
content: string | null | undefined,
|
|
1192
1366
|
actors?: BilingualActorNames,
|
|
@@ -1195,7 +1369,14 @@ export function resolveWorkflowContentAr(
|
|
|
1195
1369
|
const normalized = content.trim();
|
|
1196
1370
|
|
|
1197
1371
|
for (const tpl of Object.values(WORKFLOW_TEMPLATES)) {
|
|
1198
|
-
if (tpl.en === normalized) return tpl.ar;
|
|
1372
|
+
if (tpl.en === normalized || tpl.en.toLowerCase() === normalized.toLowerCase()) return tpl.ar;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
for (const [, tpl] of listWorkflowTemplatesBySpecificity()) {
|
|
1376
|
+
if (!/\{\{\w+\}\}/.test(tpl.en)) continue;
|
|
1377
|
+
const params = matchTemplateMessage(tpl.en, normalized);
|
|
1378
|
+
if (!params) continue;
|
|
1379
|
+
return interpolate(tpl.ar, buildArabicWorkflowTemplateParams(params, actors));
|
|
1199
1380
|
}
|
|
1200
1381
|
|
|
1201
1382
|
const commentIdx = normalized.indexOf(': ');
|
|
@@ -1233,6 +1414,31 @@ export function resolveWorkflowContentAr(
|
|
|
1233
1414
|
return interpolate(WORKFLOW_TEMPLATES.request_approval_pending_from.ar, { name: nameAr });
|
|
1234
1415
|
}
|
|
1235
1416
|
|
|
1417
|
+
// Legacy HR worker strings (pre-i18n catalog)
|
|
1418
|
+
if (normalized === 'Send Notification') {
|
|
1419
|
+
return WORKFLOW_TEMPLATES.notification_sent_pending?.ar ?? null;
|
|
1420
|
+
}
|
|
1421
|
+
const notificationSuccessVariants = [
|
|
1422
|
+
'notification sent successfully',
|
|
1423
|
+
'notification sent sucessfully',
|
|
1424
|
+
'notification sent',
|
|
1425
|
+
];
|
|
1426
|
+
if (notificationSuccessVariants.includes(normalized.toLowerCase())) {
|
|
1427
|
+
return WORKFLOW_TEMPLATES.notification_sent_successfully?.ar ?? null;
|
|
1428
|
+
}
|
|
1429
|
+
const legacyPendingPrefix = 'Approval pending from ';
|
|
1430
|
+
if (normalized.startsWith(legacyPendingPrefix)) {
|
|
1431
|
+
const namePartEn = normalized.slice(legacyPendingPrefix.length).trim();
|
|
1432
|
+
const nameAr = resolveArabicWorkflowActorName(actors, namePartEn);
|
|
1433
|
+
return interpolate(WORKFLOW_TEMPLATES.request_approval_pending_from.ar, { name: nameAr });
|
|
1434
|
+
}
|
|
1435
|
+
if (
|
|
1436
|
+
normalized === 'Approval Pending from HR Section' ||
|
|
1437
|
+
normalized === 'Approval Pending from Records Department'
|
|
1438
|
+
) {
|
|
1439
|
+
return WORKFLOW_TEMPLATES.request_approval_pending?.ar ?? null;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1236
1442
|
return null;
|
|
1237
1443
|
}
|
|
1238
1444
|
|
|
@@ -1270,9 +1476,54 @@ export function enrichWorkflowLog(log: Record<string, unknown>): Record<string,
|
|
|
1270
1476
|
const status = log.status != null ? String(log.status) : null;
|
|
1271
1477
|
const content = log.content != null ? String(log.content) : null;
|
|
1272
1478
|
const actors = extractBilingualActorsFromRecord(log);
|
|
1479
|
+
const resolvedAr = resolveWorkflowContentAr(content, actors);
|
|
1480
|
+
const existingAr =
|
|
1481
|
+
log.content_ar != null && String(log.content_ar).trim() !== ''
|
|
1482
|
+
? String(log.content_ar)
|
|
1483
|
+
: null;
|
|
1484
|
+
// Re-resolve when stored AR still embeds the English actor parsed from `content`.
|
|
1485
|
+
const pendingPrefix = WORKFLOW_TEMPLATES.request_approval_pending_from?.en.split('{{name}}')[0].trim();
|
|
1486
|
+
let content_ar = existingAr ?? resolvedAr;
|
|
1487
|
+
if (
|
|
1488
|
+
content &&
|
|
1489
|
+
pendingPrefix &&
|
|
1490
|
+
content.startsWith(pendingPrefix) &&
|
|
1491
|
+
existingAr &&
|
|
1492
|
+
resolvedAr &&
|
|
1493
|
+
existingAr !== resolvedAr
|
|
1494
|
+
) {
|
|
1495
|
+
const namePartEn = content.slice(pendingPrefix.length).trim();
|
|
1496
|
+
if (namePartEn && existingAr.includes(namePartEn)) {
|
|
1497
|
+
content_ar = resolvedAr;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (
|
|
1501
|
+
content &&
|
|
1502
|
+
existingAr &&
|
|
1503
|
+
resolvedAr &&
|
|
1504
|
+
existingAr !== resolvedAr &&
|
|
1505
|
+
/Request assigned to/i.test(existingAr)
|
|
1506
|
+
) {
|
|
1507
|
+
content_ar = resolvedAr;
|
|
1508
|
+
}
|
|
1509
|
+
if (
|
|
1510
|
+
content &&
|
|
1511
|
+
existingAr &&
|
|
1512
|
+
resolvedAr &&
|
|
1513
|
+
existingAr !== resolvedAr &&
|
|
1514
|
+
actors?.roleName?.trim() &&
|
|
1515
|
+
actors?.roleArabicName?.trim() &&
|
|
1516
|
+
existingAr.includes(actors.roleName.trim())
|
|
1517
|
+
) {
|
|
1518
|
+
content_ar = resolvedAr;
|
|
1519
|
+
}
|
|
1520
|
+
if ((content_ar == null || content_ar === '') && content) {
|
|
1521
|
+
const notificationAr = resolveWorkflowContentAr(content, actors);
|
|
1522
|
+
if (notificationAr) content_ar = notificationAr;
|
|
1523
|
+
}
|
|
1273
1524
|
return {
|
|
1274
1525
|
...log,
|
|
1275
|
-
content_ar
|
|
1526
|
+
content_ar,
|
|
1276
1527
|
status_ar: log.status_ar ?? resolveWorkflowStatusAr(status),
|
|
1277
1528
|
};
|
|
1278
1529
|
}
|
package/src/index.ts
CHANGED
|
@@ -498,7 +498,9 @@ export * from './models/RespondToEnquiriesAttachmentModel';
|
|
|
498
498
|
export {RespondToEnquiriesRequestAttachment} from './models/RespondToEnquiriesAttachmentModel';
|
|
499
499
|
export * from './models/RequestATenderRequestModel';
|
|
500
500
|
export * from './models/RequestTenderAnalysisRequestModel';
|
|
501
|
-
export * from './models/ContractServiceRequestModel';
|
|
501
|
+
export * from './models/ContractServiceRequestModel';
|
|
502
|
+
export * from './models/ContractServiceRequestDocumentModel';
|
|
503
|
+
export * from './models/SlaConfigModel';
|
|
502
504
|
export * from './models/SlaRequestModel';
|
|
503
505
|
export * from './models/SlaMyRequestsViewModel';
|
|
504
506
|
export * from './models/SlaApprovalsViewModel';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Column, Entity } from 'typeorm';
|
|
2
|
+
import { BaseModel } from './BaseModel';
|
|
3
|
+
|
|
4
|
+
@Entity({ name: 'contract_service_request_documents' })
|
|
5
|
+
export class ContractServiceRequestDocument extends BaseModel {
|
|
6
|
+
@Column({ type: 'integer', nullable: false })
|
|
7
|
+
request_id: number;
|
|
8
|
+
|
|
9
|
+
@Column({ type: 'integer', nullable: true })
|
|
10
|
+
service_id: number | null;
|
|
11
|
+
|
|
12
|
+
@Column({ type: 'integer', nullable: true })
|
|
13
|
+
sub_service_id: number | null;
|
|
14
|
+
|
|
15
|
+
@Column({ type: 'varchar', length: 500, nullable: false })
|
|
16
|
+
document_name: string;
|
|
17
|
+
|
|
18
|
+
@Column({ type: 'text', nullable: true })
|
|
19
|
+
description: string | null;
|
|
20
|
+
|
|
21
|
+
@Column({ type: 'varchar', length: 500, nullable: false })
|
|
22
|
+
file_url: string;
|
|
23
|
+
|
|
24
|
+
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
25
|
+
file_name: string | null;
|
|
26
|
+
|
|
27
|
+
@Column({ type: 'varchar', length: 100, nullable: true })
|
|
28
|
+
file_type: string | null;
|
|
29
|
+
|
|
30
|
+
@Column({ type: 'bigint', nullable: true })
|
|
31
|
+
file_size: number | null;
|
|
32
|
+
|
|
33
|
+
@Column({ type: 'integer', nullable: false })
|
|
34
|
+
uploaded_by: number;
|
|
35
|
+
}
|
|
@@ -43,6 +43,12 @@ export class ContractServiceRequest extends BaseModel {
|
|
|
43
43
|
@Column({ type: "varchar", length: 255, nullable: false })
|
|
44
44
|
company_name: string;
|
|
45
45
|
|
|
46
|
+
@Column({ type: "varchar", length: 30, nullable: true })
|
|
47
|
+
company_phone_number: string | null;
|
|
48
|
+
|
|
49
|
+
@Column({ type: "text", nullable: true })
|
|
50
|
+
notes: string | null;
|
|
51
|
+
|
|
46
52
|
@Column({
|
|
47
53
|
type: "enum",
|
|
48
54
|
enum: RespondToEnquiriesRequestStatus,
|
|
@@ -44,4 +44,16 @@ export class FollowUpReportItem extends BaseModel {
|
|
|
44
44
|
|
|
45
45
|
@Column({ type: 'varchar', length: 100, nullable: true })
|
|
46
46
|
delay_period: string | null;
|
|
47
|
+
|
|
48
|
+
@Column({ type: 'varchar', length: 500, nullable: true })
|
|
49
|
+
file_url: string | null;
|
|
50
|
+
|
|
51
|
+
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
52
|
+
file_name: string | null;
|
|
53
|
+
|
|
54
|
+
@Column({ type: 'varchar', length: 100, nullable: true })
|
|
55
|
+
file_type: string | null;
|
|
56
|
+
|
|
57
|
+
@Column({ type: 'bigint', nullable: true })
|
|
58
|
+
file_size: number | null;
|
|
47
59
|
}
|
|
@@ -8,8 +8,17 @@ export enum FollowUpReportSubjectClassification {
|
|
|
8
8
|
CONFIDENTIAL = 'Confidential',
|
|
9
9
|
INFORMATIONAL = 'Informational',
|
|
10
10
|
NORMAL = 'Normal',
|
|
11
|
+
IMPORTANT = 'Important',
|
|
12
|
+
HIGHLY_CONFIDENTIAL = 'Highly Confidential',
|
|
13
|
+
RESTRICTED = 'Restricted',
|
|
14
|
+
LIMITED = 'Limited',
|
|
11
15
|
}
|
|
12
16
|
|
|
17
|
+
/** Allowed API/DB values for follow-up report subject classification. */
|
|
18
|
+
export const FOLLOW_UP_REPORT_SUBJECT_CLASSIFICATION_VALUES = Object.values(
|
|
19
|
+
FollowUpReportSubjectClassification,
|
|
20
|
+
) as FollowUpReportSubjectClassification[];
|
|
21
|
+
|
|
13
22
|
/** Concerned department under General Manager of Support Services */
|
|
14
23
|
export enum FollowUpReportConcernedDepartment {
|
|
15
24
|
IT = 'IT',
|
|
@@ -30,6 +30,9 @@ export class RespondToEnquiriesRequestAttachment extends BaseModel {
|
|
|
30
30
|
@Column({ type: "text", nullable: true })
|
|
31
31
|
description: string;
|
|
32
32
|
|
|
33
|
+
@Column({ type: "varchar", length: 500, nullable: true })
|
|
34
|
+
document_name: string | null;
|
|
35
|
+
|
|
33
36
|
constructor(
|
|
34
37
|
request_id: number,
|
|
35
38
|
uploaded_by: number,
|
|
@@ -39,7 +42,8 @@ export class RespondToEnquiriesRequestAttachment extends BaseModel {
|
|
|
39
42
|
file_name?: string,
|
|
40
43
|
file_type?: string,
|
|
41
44
|
file_size?: number,
|
|
42
|
-
description?: string
|
|
45
|
+
description?: string,
|
|
46
|
+
document_name?: string
|
|
43
47
|
) {
|
|
44
48
|
super();
|
|
45
49
|
this.request_id = request_id;
|
|
@@ -51,5 +55,6 @@ export class RespondToEnquiriesRequestAttachment extends BaseModel {
|
|
|
51
55
|
this.file_type = file_type || "";
|
|
52
56
|
this.file_size = file_size || null;
|
|
53
57
|
this.description = description || "";
|
|
58
|
+
this.document_name = document_name || null;
|
|
54
59
|
}
|
|
55
60
|
}
|