@platform-modules/foreign-ministry 1.3.342 → 1.3.351

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.
@@ -332,6 +332,7 @@ const EmbassyEvaluationAttachmentModel_1 = require("./models/EmbassyEvaluationAt
332
332
  const SlaRequestModel_1 = require("./models/SlaRequestModel");
333
333
  const ServiceSlaApprovalModel_1 = require("./models/ServiceSlaApprovalModel");
334
334
  const SlaConfigModel_1 = require("./models/SlaConfigModel");
335
+ const ChatbotQnsAnsMappingModel_1 = require("./models/ChatbotQnsAnsMappingModel");
335
336
  exports.AppDataSource = new typeorm_1.DataSource({
336
337
  type: 'postgres',
337
338
  host: process.env.DB_HOST || 'localhost',
@@ -670,5 +671,6 @@ exports.AppDataSource = new typeorm_1.DataSource({
670
671
  SlaRequestModel_1.SlaRequest,
671
672
  ServiceSlaApprovalModel_1.ServiceSlaApproval,
672
673
  SlaConfigModel_1.SlaConfig,
674
+ ChatbotQnsAnsMappingModel_1.ChatbotQnsAnsMapping,
673
675
  ],
674
676
  });
@@ -16,6 +16,11 @@ export declare function normalizeEmployeeSubmissions(raw: unknown): {
16
16
  employees: NormalizedEmployeeSubmission[];
17
17
  error?: string;
18
18
  };
19
+ /** Relaxed validation for draft saves — employees/answers may be empty or partial. */
20
+ export declare function normalizeEmployeeDraftSubmissions(raw: unknown): {
21
+ employees: NormalizedEmployeeSubmission[];
22
+ error?: string;
23
+ };
19
24
  /** Resolve max employees from active exception cycle for the evaluation month. */
20
25
  export declare function resolveMaxEmployeesPerRequest(manager: EntityManager, month: number): Promise<number>;
21
26
  export declare function persistEmployeeEvaluationScores(manager: EntityManager, opts: {
@@ -23,6 +28,7 @@ export declare function persistEmployeeEvaluationScores(manager: EntityManager,
23
28
  formId: number;
24
29
  createdBy: string;
25
30
  employees: NormalizedEmployeeSubmission[];
31
+ isDraft?: boolean;
26
32
  }): Promise<{
27
33
  totalScore: number;
28
34
  averageScore: number;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_MAX_EMPLOYEES_PER_REQUEST = void 0;
4
4
  exports.normalizeEmployeeSubmissions = normalizeEmployeeSubmissions;
5
+ exports.normalizeEmployeeDraftSubmissions = normalizeEmployeeDraftSubmissions;
5
6
  exports.resolveMaxEmployeesPerRequest = resolveMaxEmployeesPerRequest;
6
7
  exports.persistEmployeeEvaluationScores = persistEmployeeEvaluationScores;
7
8
  const EmployeeEvaluationAnswerModel_1 = require("../models/EmployeeEvaluationAnswerModel");
@@ -69,6 +70,78 @@ function normalizeEmployeeSubmissions(raw) {
69
70
  }
70
71
  return { employees };
71
72
  }
73
+ /** Relaxed validation for draft saves — employees/answers may be empty or partial. */
74
+ function normalizeEmployeeDraftSubmissions(raw) {
75
+ if (raw == null || raw === undefined) {
76
+ return { employees: [] };
77
+ }
78
+ if (!Array.isArray(raw)) {
79
+ return { employees: [], error: 'employees must be an array when provided' };
80
+ }
81
+ if (raw.length === 0) {
82
+ return { employees: [] };
83
+ }
84
+ const seen = new Set();
85
+ const employees = [];
86
+ for (const item of raw) {
87
+ if (item == null || typeof item !== 'object') {
88
+ return { employees: [], error: 'Each employee entry must be an object' };
89
+ }
90
+ const o = item;
91
+ const user_id = Number(o.user_id ?? o.employee_id);
92
+ if (!Number.isFinite(user_id) || user_id <= 0) {
93
+ return { employees: [], error: 'Each employee must have a valid user_id' };
94
+ }
95
+ if (seen.has(user_id)) {
96
+ return { employees: [], error: `Duplicate user_id ${user_id} in employees list` };
97
+ }
98
+ seen.add(user_id);
99
+ const answersRaw = o.answers;
100
+ const answers = [];
101
+ if (answersRaw != null) {
102
+ if (!Array.isArray(answersRaw)) {
103
+ return { employees: [], error: `Employee ${user_id} answers must be an array when provided` };
104
+ }
105
+ const qSeen = new Set();
106
+ for (const a of answersRaw) {
107
+ if (a == null || typeof a !== 'object')
108
+ continue;
109
+ const ar = a;
110
+ const section_id = Number(ar.section_id);
111
+ const question_id = Number(ar.question_id);
112
+ const score = Number(ar.score);
113
+ if (!Number.isFinite(section_id) || !Number.isFinite(question_id)) {
114
+ return {
115
+ employees: [],
116
+ error: `section_id and question_id required for employee ${user_id} when answer is provided`,
117
+ };
118
+ }
119
+ if (!Number.isFinite(score) || score < 0) {
120
+ return {
121
+ employees: [],
122
+ error: `score must be a non-negative number for employee ${user_id}, question ${question_id}`,
123
+ };
124
+ }
125
+ if (qSeen.has(question_id)) {
126
+ return { employees: [], error: `Duplicate question_id ${question_id} for employee ${user_id}` };
127
+ }
128
+ qSeen.add(question_id);
129
+ answers.push({
130
+ section_id,
131
+ question_id,
132
+ score,
133
+ remarks: ar.remarks != null ? String(ar.remarks) : null,
134
+ });
135
+ }
136
+ }
137
+ employees.push({
138
+ user_id,
139
+ is_rca: o.is_rca !== undefined ? Boolean(o.is_rca) : false,
140
+ answers,
141
+ });
142
+ }
143
+ return { employees };
144
+ }
72
145
  /** Resolve max employees from active exception cycle for the evaluation month. */
73
146
  async function resolveMaxEmployeesPerRequest(manager, month) {
74
147
  const rows = await manager
@@ -86,7 +159,7 @@ async function resolveMaxEmployeesPerRequest(manager, month) {
86
159
  return exports.DEFAULT_MAX_EMPLOYEES_PER_REQUEST;
87
160
  }
88
161
  async function persistEmployeeEvaluationScores(manager, opts) {
89
- const { requestId, formId, createdBy, employees } = opts;
162
+ const { requestId, formId, createdBy, employees, isDraft = false } = opts;
90
163
  let requestTotal = 0;
91
164
  for (const emp of employees) {
92
165
  let personTotal = 0;
@@ -95,11 +168,16 @@ async function persistEmployeeEvaluationScores(manager, opts) {
95
168
  where: { id: ans.question_id, is_deleted: false },
96
169
  });
97
170
  const questionSectionId = qMeta?.form_section_id;
98
- if (!qMeta || questionSectionId == null || questionSectionId !== ans.section_id) {
99
- throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
171
+ if (!isDraft) {
172
+ if (!qMeta || questionSectionId == null || questionSectionId !== ans.section_id) {
173
+ throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
174
+ }
175
+ if (qMeta.max_score != null && ans.score > qMeta.max_score) {
176
+ throw new Error(`score ${ans.score} exceeds max_score ${qMeta.max_score} for question ${ans.question_id}`);
177
+ }
100
178
  }
101
- if (qMeta.max_score != null && ans.score > qMeta.max_score) {
102
- throw new Error(`score ${ans.score} exceeds max_score ${qMeta.max_score} for question ${ans.question_id}`);
179
+ else if (qMeta && qMeta.form_section_id != null && qMeta.form_section_id !== ans.section_id) {
180
+ throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
103
181
  }
104
182
  personTotal += ans.score;
105
183
  await manager.save(EmployeeEvaluationAnswerModel_1.EmployeeEvaluationAnswers, manager.create(EmployeeEvaluationAnswerModel_1.EmployeeEvaluationAnswers, {
package/dist/index.d.ts CHANGED
@@ -98,6 +98,7 @@ export * from './models/UpdateAttendanceWorkflowModel';
98
98
  export * from './models/SlaRequestModel';
99
99
  export * from './models/ServiceSlaApprovalModel';
100
100
  export * from './models/SlaConfigModel';
101
+ export * from './models/ChatbotQnsAnsMappingModel';
101
102
  export * from './models/GeneralServiceRequestsModel';
102
103
  export * from './models/GeneralServiceApprovalsModel';
103
104
  export * from './models/GeneralServiceWorkFlowModel';
@@ -410,7 +411,7 @@ export type { FmServicesNotificationConfigRecipient, CollectFmServicesNotificati
410
411
  export * from './models/MoodleUsersModel';
411
412
  export { EvaluationEligibilitySetting, EvaluationEligibilitySettingEmployee, } from './models/EvaluationEligibilitySettingModel';
412
413
  export { isEvaluationEligibilityWindowOpen, isCalendarMonthInEligibilityRange, mmDdWindowsOverlap, mmDdSortKey, parseMonthDay, parseEligibilityDateRange, parseEvaluationEndDay, parseMonthRange, } from './helpers/evaluation-eligibility.utils';
413
- export { DEFAULT_MAX_EMPLOYEES_PER_REQUEST, normalizeEmployeeSubmissions, resolveMaxEmployeesPerRequest, persistEmployeeEvaluationScores, } from './helpers/employee-evaluation-request.utils';
414
+ export { DEFAULT_MAX_EMPLOYEES_PER_REQUEST, normalizeEmployeeSubmissions, normalizeEmployeeDraftSubmissions, resolveMaxEmployeesPerRequest, persistEmployeeEvaluationScores, } from './helpers/employee-evaluation-request.utils';
414
415
  export type { EmployeeEvaluationSubmitAnswer, EmployeeEvaluationSubmitEmployee, NormalizedEmployeeSubmission, } from './helpers/employee-evaluation-request.utils';
415
416
  export { EmployeeEvaluationRequests, EmployeeEvaluationRequestStatus, EmployeeEvaluationRoutingBucket, } from './models/EmployeeEvaluationRequestModel';
416
417
  export { EmployeeEvaluation, EmployeeEvaluationUsFeedback } from './models/EmployeeEvaluationModel';
package/dist/index.js CHANGED
@@ -15,9 +15,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.collectFmServicesNotificationConfigRecipients = exports.ServicesNotificationTriggerType = exports.AppointmentWorkFlowStatus = exports.AppointmentMessageType = exports.AppointmentAttendeeStatus = exports.AppointmentApprovalStatus = exports.AppointmentAttendees = exports.AppointmentWorkFlow = exports.AppointmentRequestChat = exports.AppointmentRequestAttachment = exports.AppointmentApprovalDetails = exports.AppointmentRequestStatus = exports.AppointmentRequests = exports.TravelClass = exports.MissionTravelWorkFlowStatus = exports.MissionTravelApprovalStatus = exports.AllowanceRatio = exports.DecisionType = exports.MissionType = exports.MissionTravelStatus = exports.GatePassMessageType = exports.GatePassWorkFlowStatus = exports.GatePassApprovalStatus = exports.GatePassType = exports.GatePassRequestStatus = exports.SecurityDeptMessageType = exports.SecurityDeptAccessType = exports.SecurityDeptRequestStatus = exports.RetiredCardMessageType = exports.RetiredCardWorkFlowStatus = exports.RetiredCardApprovalStatus = exports.RetiredCardAccessType = exports.RetiredCardRequestStatus = exports.TitleCategory = exports.ProfileUpdateRequestStatus = exports.StayAfterHoursTransactionStatus = exports.StayAfterHoursTransaction = exports.StayAfterHoursBalance = exports.EarlyCheckoutTransactionStatus = exports.EarlyCheckoutTransaction = exports.EarlyCheckoutFrequency = exports.EarlyCheckoutConfiguration = exports.EarlyCheckoutBalance = exports.LeaveTransactionStatus = exports.LeaveTransaction = exports.LeaveConfigurationGrades = exports.enumFrequency = exports.LeaveConfiguration = exports.ParcelDepartmentCategory = exports.RegisterCandidateExperienceActivity = void 0;
18
- exports.InitiatorEmployeeNominationApprovalStatus = exports.InitiatorEmployeeNominationApprovalDetails = exports.InitiatorInitiativeType = exports.InitiatorEmployeeNominationRequestStatus = exports.InitiatorEmployeeNominationRequests = exports.IpeGrievanceRequestAttachment = exports.IpeGrievanceMessageType = exports.IpeGrievanceRequestChat = exports.IpeGrievanceWorkFlowStatus = exports.IpeGrievanceWorkFlow = exports.IpeGrievanceApprovalStatus = exports.IpeGrievanceApprovalDetails = exports.IpeGrievancePeriodRating = exports.IpeGrievanceExemptionReason = exports.IpeGrievancePeriodType = exports.IpeGrievanceFinalEvaluationResult = exports.IpeGrievanceRequestStatus = exports.IpeGrievanceRequests = exports.EmployeeEvaluationRequestAttachment = exports.EmployeeEvaluationMessageType = exports.EmployeeEvaluationRequestChat = exports.EmployeeEvaluationWorkFlowStatus = exports.EmployeeEvaluationWorkFlow = exports.EmployeeEvaluationApprovalStatus = exports.EmployeeEvaluationApprovalDetails = exports.EmployeeEvaluationPersonScore = exports.EmployeeEvaluationAnswers = exports.EmployeeEvaluationUsFeedback = exports.EmployeeEvaluation = exports.EmployeeEvaluationRoutingBucket = exports.EmployeeEvaluationRequestStatus = exports.EmployeeEvaluationRequests = exports.persistEmployeeEvaluationScores = exports.resolveMaxEmployeesPerRequest = exports.normalizeEmployeeSubmissions = exports.DEFAULT_MAX_EMPLOYEES_PER_REQUEST = exports.parseMonthRange = exports.parseEvaluationEndDay = exports.parseEligibilityDateRange = exports.parseMonthDay = exports.mmDdSortKey = exports.mmDdWindowsOverlap = exports.isCalendarMonthInEligibilityRange = exports.isEvaluationEligibilityWindowOpen = exports.EvaluationEligibilitySettingEmployee = exports.EvaluationEligibilitySetting = exports.isPortalAdminFromRequest = exports.userHasPortalAdminRole = exports.parsePortalUserIdFromRequest = exports.sendFmServicesNotificationConfigNotifications = void 0;
19
- exports.EvaluationFormQuestion = exports.EvaluationFormSection = exports.EvaluationFormType = exports.EvaluationForm = exports.EmbassyEvaluationRequestAttachment = exports.EmbassyEvaluationMessageType = exports.EmbassyEvaluationRequestChat = exports.EmbassyEvaluationWorkFlowStatus = exports.EmbassyEvaluationWorkFlow = exports.EmbassyEvaluationApprovalStatus = exports.EmbassyEvaluationApprovalDetails = exports.EmbassyEvaluationRequestStatus = exports.EmbassyEvaluationRequests = exports.EmbassyEvaluationResponse = exports.EmbassyEvaluationAssignmentStatus = exports.EmbassyEvaluationAssignment = exports.EmbassyEvaluationCycleStatus = exports.EmbassyEvaluationCycle = exports.EmployeeOfMonthSupportNominationRequestAttachment = exports.EmployeeOfMonthSupportNominationMessageType = exports.EmployeeOfMonthSupportNominationRequestChat = exports.EmployeeOfMonthSupportNominationWorkFlowStatus = exports.EmployeeOfMonthSupportNominationWorkFlow = exports.EmployeeOfMonthSupportNominationApprovalStatus = exports.EmployeeOfMonthSupportNominationApprovalDetails = exports.EmployeeOfMonthSupportNominationRequestStatus = exports.EmployeeOfMonthSupportNominationRequests = exports.EmployeeOfMonthNominationRequestAttachment = exports.EmployeeOfMonthNominationMessageType = exports.EmployeeOfMonthNominationRequestChat = exports.EmployeeOfMonthNominationWorkFlowStatus = exports.EmployeeOfMonthNominationWorkFlow = exports.EmployeeOfMonthNominationApprovalStatus = exports.EmployeeOfMonthNominationApprovalDetails = exports.EmployeeOfMonthNominationRequestStatus = exports.EmployeeOfMonthNominationRequests = exports.InnovativeEmployeeNominationRequestAttachment = exports.InnovativeEmployeeNominationMessageType = exports.InnovativeEmployeeNominationRequestChat = exports.InnovativeEmployeeNominationWorkFlowStatus = exports.InnovativeEmployeeNominationWorkFlow = exports.InnovativeEmployeeNominationApprovalStatus = exports.InnovativeEmployeeNominationApprovalDetails = exports.InnovativeEmployeeNominationRequestStatus = exports.InnovativeEmployeeNominationRequests = exports.InitiatorEmployeeNominationRequestAttachment = exports.InitiatorEmployeeNominationMessageType = exports.InitiatorEmployeeNominationRequestChat = exports.InitiatorEmployeeNominationWorkFlowStatus = exports.InitiatorEmployeeNominationWorkFlow = void 0;
20
- exports.EvaluationFormQuestionType = void 0;
18
+ exports.InitiatorEmployeeNominationApprovalDetails = exports.InitiatorInitiativeType = exports.InitiatorEmployeeNominationRequestStatus = exports.InitiatorEmployeeNominationRequests = exports.IpeGrievanceRequestAttachment = exports.IpeGrievanceMessageType = exports.IpeGrievanceRequestChat = exports.IpeGrievanceWorkFlowStatus = exports.IpeGrievanceWorkFlow = exports.IpeGrievanceApprovalStatus = exports.IpeGrievanceApprovalDetails = exports.IpeGrievancePeriodRating = exports.IpeGrievanceExemptionReason = exports.IpeGrievancePeriodType = exports.IpeGrievanceFinalEvaluationResult = exports.IpeGrievanceRequestStatus = exports.IpeGrievanceRequests = exports.EmployeeEvaluationRequestAttachment = exports.EmployeeEvaluationMessageType = exports.EmployeeEvaluationRequestChat = exports.EmployeeEvaluationWorkFlowStatus = exports.EmployeeEvaluationWorkFlow = exports.EmployeeEvaluationApprovalStatus = exports.EmployeeEvaluationApprovalDetails = exports.EmployeeEvaluationPersonScore = exports.EmployeeEvaluationAnswers = exports.EmployeeEvaluationUsFeedback = exports.EmployeeEvaluation = exports.EmployeeEvaluationRoutingBucket = exports.EmployeeEvaluationRequestStatus = exports.EmployeeEvaluationRequests = exports.persistEmployeeEvaluationScores = exports.resolveMaxEmployeesPerRequest = exports.normalizeEmployeeDraftSubmissions = exports.normalizeEmployeeSubmissions = exports.DEFAULT_MAX_EMPLOYEES_PER_REQUEST = exports.parseMonthRange = exports.parseEvaluationEndDay = exports.parseEligibilityDateRange = exports.parseMonthDay = exports.mmDdSortKey = exports.mmDdWindowsOverlap = exports.isCalendarMonthInEligibilityRange = exports.isEvaluationEligibilityWindowOpen = exports.EvaluationEligibilitySettingEmployee = exports.EvaluationEligibilitySetting = exports.isPortalAdminFromRequest = exports.userHasPortalAdminRole = exports.parsePortalUserIdFromRequest = exports.sendFmServicesNotificationConfigNotifications = void 0;
19
+ exports.EvaluationFormSection = exports.EvaluationFormType = exports.EvaluationForm = exports.EmbassyEvaluationRequestAttachment = exports.EmbassyEvaluationMessageType = exports.EmbassyEvaluationRequestChat = exports.EmbassyEvaluationWorkFlowStatus = exports.EmbassyEvaluationWorkFlow = exports.EmbassyEvaluationApprovalStatus = exports.EmbassyEvaluationApprovalDetails = exports.EmbassyEvaluationRequestStatus = exports.EmbassyEvaluationRequests = exports.EmbassyEvaluationResponse = exports.EmbassyEvaluationAssignmentStatus = exports.EmbassyEvaluationAssignment = exports.EmbassyEvaluationCycleStatus = exports.EmbassyEvaluationCycle = exports.EmployeeOfMonthSupportNominationRequestAttachment = exports.EmployeeOfMonthSupportNominationMessageType = exports.EmployeeOfMonthSupportNominationRequestChat = exports.EmployeeOfMonthSupportNominationWorkFlowStatus = exports.EmployeeOfMonthSupportNominationWorkFlow = exports.EmployeeOfMonthSupportNominationApprovalStatus = exports.EmployeeOfMonthSupportNominationApprovalDetails = exports.EmployeeOfMonthSupportNominationRequestStatus = exports.EmployeeOfMonthSupportNominationRequests = exports.EmployeeOfMonthNominationRequestAttachment = exports.EmployeeOfMonthNominationMessageType = exports.EmployeeOfMonthNominationRequestChat = exports.EmployeeOfMonthNominationWorkFlowStatus = exports.EmployeeOfMonthNominationWorkFlow = exports.EmployeeOfMonthNominationApprovalStatus = exports.EmployeeOfMonthNominationApprovalDetails = exports.EmployeeOfMonthNominationRequestStatus = exports.EmployeeOfMonthNominationRequests = exports.InnovativeEmployeeNominationRequestAttachment = exports.InnovativeEmployeeNominationMessageType = exports.InnovativeEmployeeNominationRequestChat = exports.InnovativeEmployeeNominationWorkFlowStatus = exports.InnovativeEmployeeNominationWorkFlow = exports.InnovativeEmployeeNominationApprovalStatus = exports.InnovativeEmployeeNominationApprovalDetails = exports.InnovativeEmployeeNominationRequestStatus = exports.InnovativeEmployeeNominationRequests = exports.InitiatorEmployeeNominationRequestAttachment = exports.InitiatorEmployeeNominationMessageType = exports.InitiatorEmployeeNominationRequestChat = exports.InitiatorEmployeeNominationWorkFlowStatus = exports.InitiatorEmployeeNominationWorkFlow = exports.InitiatorEmployeeNominationApprovalStatus = void 0;
20
+ exports.EvaluationFormQuestionType = exports.EvaluationFormQuestion = void 0;
21
21
  __exportStar(require("./models/user"), exports);
22
22
  __exportStar(require("./models/role"), exports);
23
23
  __exportStar(require("./models/user-sessions"), exports);
@@ -118,6 +118,7 @@ __exportStar(require("./models/UpdateAttendanceWorkflowModel"), exports);
118
118
  __exportStar(require("./models/SlaRequestModel"), exports);
119
119
  __exportStar(require("./models/ServiceSlaApprovalModel"), exports);
120
120
  __exportStar(require("./models/SlaConfigModel"), exports);
121
+ __exportStar(require("./models/ChatbotQnsAnsMappingModel"), exports);
121
122
  __exportStar(require("./models/GeneralServiceRequestsModel"), exports);
122
123
  __exportStar(require("./models/GeneralServiceApprovalsModel"), exports);
123
124
  __exportStar(require("./models/GeneralServiceWorkFlowModel"), exports);
@@ -516,6 +517,7 @@ Object.defineProperty(exports, "parseMonthRange", { enumerable: true, get: funct
516
517
  var employee_evaluation_request_utils_1 = require("./helpers/employee-evaluation-request.utils");
517
518
  Object.defineProperty(exports, "DEFAULT_MAX_EMPLOYEES_PER_REQUEST", { enumerable: true, get: function () { return employee_evaluation_request_utils_1.DEFAULT_MAX_EMPLOYEES_PER_REQUEST; } });
518
519
  Object.defineProperty(exports, "normalizeEmployeeSubmissions", { enumerable: true, get: function () { return employee_evaluation_request_utils_1.normalizeEmployeeSubmissions; } });
520
+ Object.defineProperty(exports, "normalizeEmployeeDraftSubmissions", { enumerable: true, get: function () { return employee_evaluation_request_utils_1.normalizeEmployeeDraftSubmissions; } });
519
521
  Object.defineProperty(exports, "resolveMaxEmployeesPerRequest", { enumerable: true, get: function () { return employee_evaluation_request_utils_1.resolveMaxEmployeesPerRequest; } });
520
522
  Object.defineProperty(exports, "persistEmployeeEvaluationScores", { enumerable: true, get: function () { return employee_evaluation_request_utils_1.persistEmployeeEvaluationScores; } });
521
523
  var EmployeeEvaluationRequestModel_1 = require("./models/EmployeeEvaluationRequestModel");
@@ -0,0 +1,8 @@
1
+ import { BaseModel } from "./BaseModel";
2
+ export declare class ChatbotQnsAnsMapping extends BaseModel {
3
+ service_id: number;
4
+ sub_service_id: number;
5
+ question: Record<string, unknown> | unknown[];
6
+ answer: Record<string, unknown> | unknown[];
7
+ constructor();
8
+ }
@@ -0,0 +1,40 @@
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.ChatbotQnsAnsMapping = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ const BaseModel_1 = require("./BaseModel");
15
+ let ChatbotQnsAnsMapping = class ChatbotQnsAnsMapping extends BaseModel_1.BaseModel {
16
+ constructor() {
17
+ super();
18
+ }
19
+ };
20
+ exports.ChatbotQnsAnsMapping = ChatbotQnsAnsMapping;
21
+ __decorate([
22
+ (0, typeorm_1.Column)({ type: "int", nullable: false }),
23
+ __metadata("design:type", Number)
24
+ ], ChatbotQnsAnsMapping.prototype, "service_id", void 0);
25
+ __decorate([
26
+ (0, typeorm_1.Column)({ type: "int", nullable: false }),
27
+ __metadata("design:type", Number)
28
+ ], ChatbotQnsAnsMapping.prototype, "sub_service_id", void 0);
29
+ __decorate([
30
+ (0, typeorm_1.Column)({ type: "jsonb", nullable: false }),
31
+ __metadata("design:type", Object)
32
+ ], ChatbotQnsAnsMapping.prototype, "question", void 0);
33
+ __decorate([
34
+ (0, typeorm_1.Column)({ type: "jsonb", nullable: false }),
35
+ __metadata("design:type", Object)
36
+ ], ChatbotQnsAnsMapping.prototype, "answer", void 0);
37
+ exports.ChatbotQnsAnsMapping = ChatbotQnsAnsMapping = __decorate([
38
+ (0, typeorm_1.Entity)({ name: "chatbot_qns_ans_mapping" }),
39
+ __metadata("design:paramtypes", [])
40
+ ], ChatbotQnsAnsMapping);
@@ -34,6 +34,7 @@ export declare class EmployeeEvaluationRequests extends BaseModel {
34
34
  total_score: number | null;
35
35
  average_score: number | null;
36
36
  employee_count: number;
37
+ is_draft: boolean;
37
38
  /** Form structure snapshot at request creation (sections/questions). */
38
39
  dynamic_evaluation_form: Record<string, unknown> | null;
39
40
  }
@@ -111,6 +111,10 @@ __decorate([
111
111
  (0, typeorm_1.Column)({ type: 'int', default: 0, nullable: false }),
112
112
  __metadata("design:type", Number)
113
113
  ], EmployeeEvaluationRequests.prototype, "employee_count", void 0);
114
+ __decorate([
115
+ (0, typeorm_1.Column)({ type: 'boolean', default: false, nullable: false }),
116
+ __metadata("design:type", Boolean)
117
+ ], EmployeeEvaluationRequests.prototype, "is_draft", void 0);
114
118
  __decorate([
115
119
  (0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
116
120
  __metadata("design:type", Object)
@@ -40,7 +40,14 @@ SELECT
40
40
  sa.updated_at AS updated_at
41
41
  FROM sla_approval sa
42
42
  INNER JOIN sla_requests sr
43
- ON sr.request_id = sa.request_id AND COALESCE(sr.is_deleted, false) = false
43
+ ON sr.id = (
44
+ SELECT MIN(sr2.id)
45
+ FROM sla_requests sr2
46
+ WHERE sr2.request_id = sa.request_id
47
+ AND COALESCE(sr2.is_deleted, false) = false
48
+ AND (sa.service_id IS NULL OR sr2.service_id = sa.service_id)
49
+ AND (sa.sub_service_id IS NULL OR sr2.sub_service_id = sa.sub_service_id)
50
+ )
44
51
  LEFT JOIN fm_services svc
45
52
  ON svc.id = sa.service_id AND COALESCE(svc.is_deleted, false) = false
46
53
  LEFT JOIN fm_sub_services subsvc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-modules/foreign-ministry",
3
- "version": "1.3.342",
3
+ "version": "1.3.351",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -0,0 +1,2 @@
1
+ ALTER TABLE employee_evaluation_requests
2
+ ADD COLUMN IF NOT EXISTS is_draft BOOLEAN NOT NULL DEFAULT false;
@@ -0,0 +1,25 @@
1
+ -- Chatbot Q&A mapping master table (User Service)
2
+ -- PostgreSQL
3
+
4
+ CREATE TABLE IF NOT EXISTS chatbot_qns_ans_mapping (
5
+ id SERIAL PRIMARY KEY,
6
+ service_id INTEGER NOT NULL,
7
+ sub_service_id INTEGER NOT NULL,
8
+ question JSONB NOT NULL,
9
+ answer JSONB NOT NULL,
10
+ created_by VARCHAR NULL,
11
+ created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
12
+ updated_by VARCHAR NULL,
13
+ updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
14
+ is_deleted BOOLEAN DEFAULT FALSE
15
+ );
16
+
17
+ CREATE INDEX IF NOT EXISTS idx_chatbot_qns_ans_mapping_service_sub_service
18
+ ON chatbot_qns_ans_mapping (service_id, sub_service_id)
19
+ WHERE is_deleted = FALSE;
20
+
21
+ CREATE INDEX IF NOT EXISTS idx_chatbot_qns_ans_mapping_question_gin
22
+ ON chatbot_qns_ans_mapping USING GIN (question);
23
+
24
+ CREATE INDEX IF NOT EXISTS idx_chatbot_qns_ans_mapping_answer_gin
25
+ ON chatbot_qns_ans_mapping USING GIN (answer);
@@ -19,13 +19,15 @@ $$;
19
19
  -- Admin list views: served by Reports_Service TypeORM (sla-my-requests-view.query.ts, sla-approvals-view.query.ts)
20
20
 
21
21
  -- ---------------------------------------------------------------------------
22
- -- ADMIN — my requests Excel (optional p_target_user_id)
22
+ -- ADMIN — my requests Excel (optional p_target_user_ids INT[])
23
23
  -- Per sub-service Requests + Approvals sheets (same as user my-requests download)
24
24
  -- ---------------------------------------------------------------------------
25
25
  DROP FUNCTION IF EXISTS sp_sla_admin_my_requests_excel_export(INT, INT[], INT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT);
26
+ DROP FUNCTION IF EXISTS sp_sla_admin_my_requests_excel_export(INT, INT[], INT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT, TEXT);
27
+ DROP FUNCTION IF EXISTS sp_sla_admin_my_requests_excel_export(INT[], INT[], INT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT, TEXT);
26
28
 
27
29
  CREATE OR REPLACE FUNCTION sp_sla_admin_my_requests_excel_export(
28
- p_target_user_id INT DEFAULT NULL,
30
+ p_target_user_ids INT[] DEFAULT NULL,
29
31
  p_service_ids INT[] DEFAULT NULL,
30
32
  p_sub_service_ids INT[] DEFAULT NULL,
31
33
  p_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
@@ -65,10 +67,20 @@ DECLARE
65
67
  v_k TEXT;
66
68
  v_sheet_order INT := 0;
67
69
  v_user_filter TEXT;
70
+ v_user_scope TEXT;
68
71
  BEGIN
72
+ IF p_target_user_ids IS NULL OR cardinality(p_target_user_ids) = 0 THEN
73
+ v_user_scope := 'TRUE';
74
+ ELSE
75
+ v_user_scope := format(
76
+ 'v.user_id = ANY (ARRAY[%s]::int[])',
77
+ array_to_string(p_target_user_ids, ',')
78
+ );
79
+ END IF;
80
+
69
81
  v_user_filter := format(
70
82
  '(%s) AND (%s)',
71
- CASE WHEN p_target_user_id IS NULL THEN 'TRUE' ELSE format('v.user_id = %s', p_target_user_id) END,
83
+ v_user_scope,
72
84
  COALESCE(NULLIF(trim(p_extra_filter_sql), ''), 'TRUE')
73
85
  );
74
86
 
@@ -256,15 +268,21 @@ $$;
256
268
 
257
269
  -- ---------------------------------------------------------------------------
258
270
  -- ADMIN — approvals Excel
259
- -- Per sub-service Requests + Approvals; optional approver_user_id (NULL = all queues)
271
+ -- Per sub-service Requests + Approvals; optional approver user id(s) (NULL/empty = all queues)
260
272
  -- Does not exclude own requests (unlike user approvals download)
261
273
  -- ---------------------------------------------------------------------------
262
274
  DROP FUNCTION IF EXISTS sp_sla_admin_approvals_excel_export(
263
275
  INT, INT[], INT[], TEXT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT
264
276
  );
277
+ DROP FUNCTION IF EXISTS sp_sla_admin_approvals_excel_export(
278
+ INT, INT[], INT[], TEXT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT, TEXT
279
+ );
280
+ DROP FUNCTION IF EXISTS sp_sla_admin_approvals_excel_export(
281
+ INT[], INT[], INT[], TEXT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT, TEXT
282
+ );
265
283
 
266
284
  CREATE OR REPLACE FUNCTION sp_sla_admin_approvals_excel_export(
267
- p_target_approver_user_id INT DEFAULT NULL,
285
+ p_target_approver_user_ids INT[] DEFAULT NULL,
268
286
  p_service_ids INT[] DEFAULT NULL,
269
287
  p_sub_service_ids INT[] DEFAULT NULL,
270
288
  p_request_statuses TEXT[] DEFAULT ARRAY['Pending','In Progress','Approved','Rejected'],
@@ -283,7 +301,7 @@ LANGUAGE plpgsql
283
301
  STABLE
284
302
  AS $$
285
303
  DECLARE
286
- v_exists TEXT := sla_approval_queue_exists_sql_optional(p_target_approver_user_id);
304
+ v_exists TEXT := sla_approval_queue_exists_sql_any(p_target_approver_user_ids);
287
305
  v_own TEXT := COALESCE(NULLIF(trim(p_extra_filter_sql), ''), 'TRUE');
288
306
  BEGIN
289
307
  RETURN QUERY
@@ -96,6 +96,53 @@ AS $$
96
96
  END;
97
97
  $$;
98
98
 
99
+ -- Admin: any of multiple approver user ids (NULL/empty = all queues)
100
+ CREATE OR REPLACE FUNCTION sla_approval_queue_exists_sql_any(p_user_ids INT[])
101
+ RETURNS TEXT
102
+ LANGUAGE plpgsql
103
+ STABLE
104
+ AS $$
105
+ DECLARE
106
+ v_id INT;
107
+ v_parts TEXT[] := ARRAY[]::TEXT[];
108
+ v_match TEXT;
109
+ BEGIN
110
+ IF p_user_ids IS NULL OR cardinality(p_user_ids) = 0 THEN
111
+ RETURN sla_approval_queue_exists_sql_optional(NULL);
112
+ END IF;
113
+
114
+ IF cardinality(p_user_ids) = 1 THEN
115
+ RETURN sla_approval_queue_exists_sql(p_user_ids[1]);
116
+ END IF;
117
+
118
+ FOREACH v_id IN ARRAY p_user_ids LOOP
119
+ IF v_id IS NOT NULL AND v_id > 0 THEN
120
+ v_parts := array_append(v_parts, '(' || sla_approval_matches_user('appr', v_id) || ')');
121
+ END IF;
122
+ END LOOP;
123
+
124
+ IF cardinality(v_parts) = 0 THEN
125
+ RETURN sla_approval_queue_exists_sql_optional(NULL);
126
+ END IF;
127
+
128
+ v_match := array_to_string(v_parts, ' OR ');
129
+
130
+ RETURN format(
131
+ 'EXISTS (
132
+ SELECT 1
133
+ FROM sla_approval appr
134
+ WHERE appr.request_id = sr.request_id
135
+ AND COALESCE(appr.is_deleted, false) = false
136
+ AND (appr.service_id = sr.service_id OR appr.service_id IS NULL)
137
+ AND (appr.sub_service_id = sr.sub_service_id OR appr.sub_service_id IS NULL)
138
+ AND (%s)
139
+ AND (appr.approval_status = ANY($1) OR appr.approval_status IS NULL)
140
+ )',
141
+ v_match
142
+ );
143
+ END;
144
+ $$;
145
+
99
146
  DROP FUNCTION IF EXISTS sp_sla_approvals_workbook_excel_export(
100
147
  TEXT, TEXT, INT[], INT[], TEXT[], TEXT[], DATE, DATE, TEXT, TEXT, TEXT
101
148
  );
@@ -70,7 +70,14 @@ FROM sla_approval sa
70
70
 
71
71
  INNER JOIN sla_requests sr
72
72
 
73
- ON sr.request_id = sa.request_id AND COALESCE(sr.is_deleted, false) = false
73
+ ON sr.id = (
74
+ SELECT MIN(sr2.id)
75
+ FROM sla_requests sr2
76
+ WHERE sr2.request_id = sa.request_id
77
+ AND COALESCE(sr2.is_deleted, false) = false
78
+ AND (sa.service_id IS NULL OR sr2.service_id = sa.service_id)
79
+ AND (sa.sub_service_id IS NULL OR sr2.sub_service_id = sa.sub_service_id)
80
+ )
74
81
 
75
82
  LEFT JOIN fm_services svc
76
83
 
@@ -343,6 +343,7 @@ import { EmbassyEvaluationRequestAttachment } from './models/EmbassyEvaluationAt
343
343
  import { SlaRequest } from './models/SlaRequestModel';
344
344
  import { ServiceSlaApproval } from './models/ServiceSlaApprovalModel';
345
345
  import { SlaConfig } from './models/SlaConfigModel';
346
+ import { ChatbotQnsAnsMapping } from './models/ChatbotQnsAnsMappingModel';
346
347
 
347
348
  export const AppDataSource = new DataSource({
348
349
  type: 'postgres',
@@ -682,5 +683,6 @@ export const AppDataSource = new DataSource({
682
683
  SlaRequest,
683
684
  ServiceSlaApproval,
684
685
  SlaConfig,
686
+ ChatbotQnsAnsMapping,
685
687
  ],
686
688
  });
@@ -85,6 +85,81 @@ export function normalizeEmployeeSubmissions(raw: unknown): {
85
85
  return { employees };
86
86
  }
87
87
 
88
+ /** Relaxed validation for draft saves — employees/answers may be empty or partial. */
89
+ export function normalizeEmployeeDraftSubmissions(raw: unknown): {
90
+ employees: NormalizedEmployeeSubmission[];
91
+ error?: string;
92
+ } {
93
+ if (raw == null || raw === undefined) {
94
+ return { employees: [] };
95
+ }
96
+ if (!Array.isArray(raw)) {
97
+ return { employees: [], error: 'employees must be an array when provided' };
98
+ }
99
+ if (raw.length === 0) {
100
+ return { employees: [] };
101
+ }
102
+ const seen = new Set<number>();
103
+ const employees: NormalizedEmployeeSubmission[] = [];
104
+ for (const item of raw) {
105
+ if (item == null || typeof item !== 'object') {
106
+ return { employees: [], error: 'Each employee entry must be an object' };
107
+ }
108
+ const o = item as Record<string, unknown>;
109
+ const user_id = Number(o.user_id ?? o.employee_id);
110
+ if (!Number.isFinite(user_id) || user_id <= 0) {
111
+ return { employees: [], error: 'Each employee must have a valid user_id' };
112
+ }
113
+ if (seen.has(user_id)) {
114
+ return { employees: [], error: `Duplicate user_id ${user_id} in employees list` };
115
+ }
116
+ seen.add(user_id);
117
+ const answersRaw = o.answers;
118
+ const answers: EmployeeEvaluationSubmitAnswer[] = [];
119
+ if (answersRaw != null) {
120
+ if (!Array.isArray(answersRaw)) {
121
+ return { employees: [], error: `Employee ${user_id} answers must be an array when provided` };
122
+ }
123
+ const qSeen = new Set<number>();
124
+ for (const a of answersRaw) {
125
+ if (a == null || typeof a !== 'object') continue;
126
+ const ar = a as Record<string, unknown>;
127
+ const section_id = Number(ar.section_id);
128
+ const question_id = Number(ar.question_id);
129
+ const score = Number(ar.score);
130
+ if (!Number.isFinite(section_id) || !Number.isFinite(question_id)) {
131
+ return {
132
+ employees: [],
133
+ error: `section_id and question_id required for employee ${user_id} when answer is provided`,
134
+ };
135
+ }
136
+ if (!Number.isFinite(score) || score < 0) {
137
+ return {
138
+ employees: [],
139
+ error: `score must be a non-negative number for employee ${user_id}, question ${question_id}`,
140
+ };
141
+ }
142
+ if (qSeen.has(question_id)) {
143
+ return { employees: [], error: `Duplicate question_id ${question_id} for employee ${user_id}` };
144
+ }
145
+ qSeen.add(question_id);
146
+ answers.push({
147
+ section_id,
148
+ question_id,
149
+ score,
150
+ remarks: ar.remarks != null ? String(ar.remarks) : null,
151
+ });
152
+ }
153
+ }
154
+ employees.push({
155
+ user_id,
156
+ is_rca: o.is_rca !== undefined ? Boolean(o.is_rca) : false,
157
+ answers,
158
+ });
159
+ }
160
+ return { employees };
161
+ }
162
+
88
163
  /** Resolve max employees from active exception cycle for the evaluation month. */
89
164
  export async function resolveMaxEmployeesPerRequest(
90
165
  manager: EntityManager,
@@ -113,9 +188,10 @@ export async function persistEmployeeEvaluationScores(
113
188
  formId: number;
114
189
  createdBy: string;
115
190
  employees: NormalizedEmployeeSubmission[];
191
+ isDraft?: boolean;
116
192
  }
117
193
  ): Promise<{ totalScore: number; averageScore: number; employeeCount: number }> {
118
- const { requestId, formId, createdBy, employees } = opts;
194
+ const { requestId, formId, createdBy, employees, isDraft = false } = opts;
119
195
  let requestTotal = 0;
120
196
 
121
197
  for (const emp of employees) {
@@ -125,14 +201,18 @@ export async function persistEmployeeEvaluationScores(
125
201
  where: { id: ans.question_id, is_deleted: false },
126
202
  });
127
203
  const questionSectionId = qMeta?.form_section_id;
128
- if (!qMeta || questionSectionId == null || questionSectionId !== ans.section_id) {
204
+ if (!isDraft) {
205
+ if (!qMeta || questionSectionId == null || questionSectionId !== ans.section_id) {
206
+ throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
207
+ }
208
+ if (qMeta.max_score != null && ans.score > qMeta.max_score) {
209
+ throw new Error(
210
+ `score ${ans.score} exceeds max_score ${qMeta.max_score} for question ${ans.question_id}`
211
+ );
212
+ }
213
+ } else if (qMeta && qMeta.form_section_id != null && qMeta.form_section_id !== ans.section_id) {
129
214
  throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
130
215
  }
131
- if (qMeta.max_score != null && ans.score > qMeta.max_score) {
132
- throw new Error(
133
- `score ${ans.score} exceeds max_score ${qMeta.max_score} for question ${ans.question_id}`
134
- );
135
- }
136
216
  personTotal += ans.score;
137
217
  await manager.save(
138
218
  EmployeeEvaluationAnswers,
package/src/index.ts CHANGED
@@ -98,6 +98,7 @@ export * from './models/UpdateAttendanceWorkflowModel';
98
98
  export * from './models/SlaRequestModel';
99
99
  export * from './models/ServiceSlaApprovalModel';
100
100
  export * from './models/SlaConfigModel';
101
+ export * from './models/ChatbotQnsAnsMappingModel';
101
102
  export * from './models/GeneralServiceRequestsModel';
102
103
  export * from './models/GeneralServiceApprovalsModel';
103
104
  export * from './models/GeneralServiceWorkFlowModel';
@@ -459,6 +460,7 @@ export {
459
460
  export {
460
461
  DEFAULT_MAX_EMPLOYEES_PER_REQUEST,
461
462
  normalizeEmployeeSubmissions,
463
+ normalizeEmployeeDraftSubmissions,
462
464
  resolveMaxEmployeesPerRequest,
463
465
  persistEmployeeEvaluationScores,
464
466
  } from './helpers/employee-evaluation-request.utils';
@@ -0,0 +1,21 @@
1
+ import { Column, Entity } from "typeorm";
2
+ import { BaseModel } from "./BaseModel";
3
+
4
+ @Entity({ name: "chatbot_qns_ans_mapping" })
5
+ export class ChatbotQnsAnsMapping extends BaseModel {
6
+ @Column({ type: "int", nullable: false })
7
+ service_id: number;
8
+
9
+ @Column({ type: "int", nullable: false })
10
+ sub_service_id: number;
11
+
12
+ @Column({ type: "jsonb", nullable: false })
13
+ question: Record<string, unknown> | unknown[];
14
+
15
+ @Column({ type: "jsonb", nullable: false })
16
+ answer: Record<string, unknown> | unknown[];
17
+
18
+ constructor() {
19
+ super();
20
+ }
21
+ }
@@ -84,6 +84,9 @@ export class EmployeeEvaluationRequests extends BaseModel {
84
84
  @Column({ type: 'int', default: 0, nullable: false })
85
85
  employee_count: number;
86
86
 
87
+ @Column({ type: 'boolean', default: false, nullable: false })
88
+ is_draft: boolean;
89
+
87
90
  /** Form structure snapshot at request creation (sections/questions). */
88
91
  @Column({ type: 'jsonb', nullable: true })
89
92
  dynamic_evaluation_form: Record<string, unknown> | null;
@@ -29,7 +29,14 @@ SELECT
29
29
  sa.updated_at AS updated_at
30
30
  FROM sla_approval sa
31
31
  INNER JOIN sla_requests sr
32
- ON sr.request_id = sa.request_id AND COALESCE(sr.is_deleted, false) = false
32
+ ON sr.id = (
33
+ SELECT MIN(sr2.id)
34
+ FROM sla_requests sr2
35
+ WHERE sr2.request_id = sa.request_id
36
+ AND COALESCE(sr2.is_deleted, false) = false
37
+ AND (sa.service_id IS NULL OR sr2.service_id = sa.service_id)
38
+ AND (sa.sub_service_id IS NULL OR sr2.sub_service_id = sa.sub_service_id)
39
+ )
33
40
  LEFT JOIN fm_services svc
34
41
  ON svc.id = sa.service_id AND COALESCE(svc.is_deleted, false) = false
35
42
  LEFT JOIN fm_sub_services subsvc