@platform-modules/foreign-ministry 1.3.297 → 1.3.309
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-source.js +16 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +23 -1
- package/dist/models/EmbassyEvaluationApprovalModel.d.ts +22 -0
- package/dist/models/EmbassyEvaluationApprovalModel.js +85 -0
- package/dist/models/EmbassyEvaluationAssignmentModel.d.ts +24 -0
- package/dist/models/EmbassyEvaluationAssignmentModel.js +89 -0
- package/dist/models/EmbassyEvaluationAttachmentModel.d.ts +10 -0
- package/dist/models/EmbassyEvaluationAttachmentModel.js +48 -0
- package/dist/models/EmbassyEvaluationChatModel.d.ts +18 -0
- package/dist/models/EmbassyEvaluationChatModel.js +66 -0
- package/dist/models/EmbassyEvaluationCycleModel.d.ts +17 -0
- package/dist/models/EmbassyEvaluationCycleModel.js +57 -0
- package/dist/models/EmbassyEvaluationDepartmentSettingModel.d.ts +14 -0
- package/dist/models/EmbassyEvaluationDepartmentSettingModel.js +56 -0
- package/dist/models/EmbassyEvaluationRequestModel.d.ts +24 -0
- package/dist/models/EmbassyEvaluationRequestModel.js +86 -0
- package/dist/models/EmbassyEvaluationResponseModel.d.ts +8 -0
- package/dist/models/EmbassyEvaluationResponseModel.js +44 -0
- package/dist/models/EmbassyEvaluationWorkflowModel.d.ts +19 -0
- package/dist/models/EmbassyEvaluationWorkflowModel.js +73 -0
- package/dist/models/EmployeeOfMonthSupportNominationApprovalModel.d.ts +9 -7
- package/dist/models/EmployeeOfMonthSupportNominationApprovalModel.js +10 -11
- package/dist/models/EmployeeOfMonthSupportNominationChatModel.d.ts +9 -8
- package/dist/models/EmployeeOfMonthSupportNominationChatModel.js +10 -12
- package/dist/models/EmployeeOfMonthSupportNominationRequestModel.d.ts +8 -7
- package/dist/models/EmployeeOfMonthSupportNominationRequestModel.js +9 -11
- package/dist/models/EmployeeOfMonthSupportNominationWorkflowModel.d.ts +8 -7
- package/dist/models/EmployeeOfMonthSupportNominationWorkflowModel.js +9 -11
- package/dist/models/EvaluationFormModel.js +5 -1
- package/package.json +1 -1
- package/src/data-source.ts +16 -0
- package/src/helpers/employee-evaluation-request.utils.ts +181 -181
- package/src/helpers/evaluation-eligibility.utils.ts +36 -36
- package/src/index.ts +26 -0
- package/src/models/EmbassyEvaluationApprovalModel.ts +57 -0
- package/src/models/EmbassyEvaluationAssignmentModel.ts +63 -0
- package/src/models/EmbassyEvaluationAttachmentModel.ts +26 -0
- package/src/models/EmbassyEvaluationChatModel.ts +43 -0
- package/src/models/EmbassyEvaluationCycleModel.ts +38 -0
- package/src/models/EmbassyEvaluationRequestModel.ts +59 -0
- package/src/models/EmbassyEvaluationResponseModel.ts +24 -0
- package/src/models/EmbassyEvaluationWorkflowModel.ts +48 -0
- package/src/models/EmployeeEvaluationPersonScoreModel.ts +25 -25
- package/src/models/EmployeeEvaluationRequestModel.ts +90 -90
- package/src/models/EmployeeOfMonthSupportNominationApprovalModel.ts +60 -57
- package/src/models/EmployeeOfMonthSupportNominationChatModel.ts +13 -11
- package/src/models/EmployeeOfMonthSupportNominationRequestModel.ts +12 -10
- package/src/models/EmployeeOfMonthSupportNominationWorkflowModel.ts +50 -48
- package/src/models/EvaluationFormModel.ts +4 -0
- package/src/models/EvaluationFormQuestionModel.ts +52 -52
- package/src/models/EvaluationFormSectionModel.ts +33 -33
|
@@ -1,181 +1,181 @@
|
|
|
1
|
-
import type { EntityManager } from 'typeorm';
|
|
2
|
-
import { EmployeeEvaluationAnswers } from '../models/EmployeeEvaluationAnswerModel';
|
|
3
|
-
import { EmployeeEvaluationPersonScore } from '../models/EmployeeEvaluationPersonScoreModel';
|
|
4
|
-
import { EmployeeEvaluationRequests } from '../models/EmployeeEvaluationRequestModel';
|
|
5
|
-
import { EvaluationEligibilitySetting } from '../models/EvaluationEligibilitySettingModel';
|
|
6
|
-
import { EvaluationFormQuestion } from '../models/EvaluationFormQuestionModel';
|
|
7
|
-
|
|
8
|
-
export const DEFAULT_MAX_EMPLOYEES_PER_REQUEST = 50;
|
|
9
|
-
|
|
10
|
-
export type EmployeeEvaluationSubmitAnswer = {
|
|
11
|
-
section_id: number;
|
|
12
|
-
question_id: number;
|
|
13
|
-
score: number;
|
|
14
|
-
remarks?: string | null;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type EmployeeEvaluationSubmitEmployee = {
|
|
18
|
-
user_id: number;
|
|
19
|
-
is_rca?: boolean;
|
|
20
|
-
answers: EmployeeEvaluationSubmitAnswer[];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type NormalizedEmployeeSubmission = EmployeeEvaluationSubmitEmployee;
|
|
24
|
-
|
|
25
|
-
export function normalizeEmployeeSubmissions(raw: unknown): {
|
|
26
|
-
employees: NormalizedEmployeeSubmission[];
|
|
27
|
-
error?: string;
|
|
28
|
-
} {
|
|
29
|
-
if (!Array.isArray(raw) || raw.length === 0) {
|
|
30
|
-
return { employees: [], error: 'employees must be a non-empty array' };
|
|
31
|
-
}
|
|
32
|
-
const seen = new Set<number>();
|
|
33
|
-
const employees: NormalizedEmployeeSubmission[] = [];
|
|
34
|
-
for (const item of raw) {
|
|
35
|
-
if (item == null || typeof item !== 'object') {
|
|
36
|
-
return { employees: [], error: 'Each employee entry must be an object' };
|
|
37
|
-
}
|
|
38
|
-
const o = item as Record<string, unknown>;
|
|
39
|
-
const user_id = Number(o.user_id ?? o.employee_id);
|
|
40
|
-
if (!Number.isFinite(user_id) || user_id <= 0) {
|
|
41
|
-
return { employees: [], error: 'Each employee must have a valid user_id' };
|
|
42
|
-
}
|
|
43
|
-
if (seen.has(user_id)) {
|
|
44
|
-
return { employees: [], error: `Duplicate user_id ${user_id} in employees list` };
|
|
45
|
-
}
|
|
46
|
-
seen.add(user_id);
|
|
47
|
-
const answersRaw = o.answers;
|
|
48
|
-
if (!Array.isArray(answersRaw) || answersRaw.length === 0) {
|
|
49
|
-
return { employees: [], error: `Employee ${user_id} must include a non-empty answers array` };
|
|
50
|
-
}
|
|
51
|
-
const answers: EmployeeEvaluationSubmitAnswer[] = [];
|
|
52
|
-
const qSeen = new Set<number>();
|
|
53
|
-
for (const a of answersRaw) {
|
|
54
|
-
if (a == null || typeof a !== 'object') {
|
|
55
|
-
return { employees: [], error: `Invalid answer for employee ${user_id}` };
|
|
56
|
-
}
|
|
57
|
-
const ar = a as Record<string, unknown>;
|
|
58
|
-
const section_id = Number(ar.section_id);
|
|
59
|
-
const question_id = Number(ar.question_id);
|
|
60
|
-
const score = Number(ar.score);
|
|
61
|
-
if (!Number.isFinite(section_id) || !Number.isFinite(question_id)) {
|
|
62
|
-
return { employees: [], error: `section_id and question_id required for employee ${user_id}` };
|
|
63
|
-
}
|
|
64
|
-
if (!Number.isFinite(score) || score < 0) {
|
|
65
|
-
return { employees: [], error: `score must be a non-negative number for employee ${user_id}, question ${question_id}` };
|
|
66
|
-
}
|
|
67
|
-
if (qSeen.has(question_id)) {
|
|
68
|
-
return { employees: [], error: `Duplicate question_id ${question_id} for employee ${user_id}` };
|
|
69
|
-
}
|
|
70
|
-
qSeen.add(question_id);
|
|
71
|
-
answers.push({
|
|
72
|
-
section_id,
|
|
73
|
-
question_id,
|
|
74
|
-
score,
|
|
75
|
-
remarks: ar.remarks != null ? String(ar.remarks) : null,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
employees.push({
|
|
79
|
-
user_id,
|
|
80
|
-
is_rca: o.is_rca !== undefined ? Boolean(o.is_rca) : false,
|
|
81
|
-
answers,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return { employees };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Resolve max employees from active eligibility setting for dept/section/month. */
|
|
88
|
-
export async function resolveMaxEmployeesPerRequest(
|
|
89
|
-
manager: EntityManager,
|
|
90
|
-
department_id: number,
|
|
91
|
-
section_id: number,
|
|
92
|
-
month: number
|
|
93
|
-
): Promise<number> {
|
|
94
|
-
const setting = await manager
|
|
95
|
-
.getRepository(EvaluationEligibilitySetting)
|
|
96
|
-
.createQueryBuilder('s')
|
|
97
|
-
.where('s.is_deleted = false')
|
|
98
|
-
.andWhere('s.is_active = true')
|
|
99
|
-
.andWhere('s.department_id = :department_id', { department_id })
|
|
100
|
-
.andWhere('s.section_id = :section_id', { section_id })
|
|
101
|
-
.andWhere('s.from_month <= :month', { month })
|
|
102
|
-
.andWhere('s.to_month >= :month', { month })
|
|
103
|
-
.orderBy('s.id', 'DESC')
|
|
104
|
-
.getOne();
|
|
105
|
-
const max = setting?.max_employees_per_request;
|
|
106
|
-
if (max != null && Number.isFinite(Number(max)) && Number(max) > 0) {
|
|
107
|
-
return Math.floor(Number(max));
|
|
108
|
-
}
|
|
109
|
-
return DEFAULT_MAX_EMPLOYEES_PER_REQUEST;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export async function persistEmployeeEvaluationScores(
|
|
113
|
-
manager: EntityManager,
|
|
114
|
-
opts: {
|
|
115
|
-
requestId: number;
|
|
116
|
-
formId: number;
|
|
117
|
-
createdBy: string;
|
|
118
|
-
employees: NormalizedEmployeeSubmission[];
|
|
119
|
-
}
|
|
120
|
-
): Promise<{ totalScore: number; averageScore: number; employeeCount: number }> {
|
|
121
|
-
const { requestId, formId, createdBy, employees } = opts;
|
|
122
|
-
let requestTotal = 0;
|
|
123
|
-
|
|
124
|
-
for (const emp of employees) {
|
|
125
|
-
let personTotal = 0;
|
|
126
|
-
for (const ans of emp.answers) {
|
|
127
|
-
const qMeta = await manager.findOne(EvaluationFormQuestion, {
|
|
128
|
-
where: { id: ans.question_id, is_deleted: false },
|
|
129
|
-
});
|
|
130
|
-
const questionSectionId = qMeta?.form_section_id;
|
|
131
|
-
if (!qMeta || questionSectionId == null || questionSectionId !== ans.section_id) {
|
|
132
|
-
throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
|
|
133
|
-
}
|
|
134
|
-
if (qMeta.max_score != null && ans.score > qMeta.max_score) {
|
|
135
|
-
throw new Error(
|
|
136
|
-
`score ${ans.score} exceeds max_score ${qMeta.max_score} for question ${ans.question_id}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
personTotal += ans.score;
|
|
140
|
-
await manager.save(
|
|
141
|
-
EmployeeEvaluationAnswers,
|
|
142
|
-
manager.create(EmployeeEvaluationAnswers, {
|
|
143
|
-
request_id: requestId,
|
|
144
|
-
user_id: emp.user_id,
|
|
145
|
-
form_id: formId,
|
|
146
|
-
section_id: ans.section_id,
|
|
147
|
-
question_id: ans.question_id,
|
|
148
|
-
score: ans.score,
|
|
149
|
-
remarks: ans.remarks ?? null,
|
|
150
|
-
created_by: createdBy,
|
|
151
|
-
is_deleted: false,
|
|
152
|
-
})
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await manager.save(
|
|
157
|
-
EmployeeEvaluationPersonScore,
|
|
158
|
-
manager.create(EmployeeEvaluationPersonScore, {
|
|
159
|
-
request_id: requestId,
|
|
160
|
-
user_id: emp.user_id,
|
|
161
|
-
is_rca: Boolean(emp.is_rca),
|
|
162
|
-
us_feedback: null,
|
|
163
|
-
total_score: personTotal,
|
|
164
|
-
created_by: createdBy,
|
|
165
|
-
is_deleted: false,
|
|
166
|
-
})
|
|
167
|
-
);
|
|
168
|
-
requestTotal += personTotal;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const employeeCount = employees.length;
|
|
172
|
-
const averageScore = employeeCount ? requestTotal / employeeCount : 0;
|
|
173
|
-
await manager.update(EmployeeEvaluationRequests, { id: requestId }, {
|
|
174
|
-
total_score: requestTotal,
|
|
175
|
-
average_score: averageScore,
|
|
176
|
-
employee_count: employeeCount,
|
|
177
|
-
updated_by: createdBy,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
return { totalScore: requestTotal, averageScore, employeeCount };
|
|
181
|
-
}
|
|
1
|
+
import type { EntityManager } from 'typeorm';
|
|
2
|
+
import { EmployeeEvaluationAnswers } from '../models/EmployeeEvaluationAnswerModel';
|
|
3
|
+
import { EmployeeEvaluationPersonScore } from '../models/EmployeeEvaluationPersonScoreModel';
|
|
4
|
+
import { EmployeeEvaluationRequests } from '../models/EmployeeEvaluationRequestModel';
|
|
5
|
+
import { EvaluationEligibilitySetting } from '../models/EvaluationEligibilitySettingModel';
|
|
6
|
+
import { EvaluationFormQuestion } from '../models/EvaluationFormQuestionModel';
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_MAX_EMPLOYEES_PER_REQUEST = 50;
|
|
9
|
+
|
|
10
|
+
export type EmployeeEvaluationSubmitAnswer = {
|
|
11
|
+
section_id: number;
|
|
12
|
+
question_id: number;
|
|
13
|
+
score: number;
|
|
14
|
+
remarks?: string | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type EmployeeEvaluationSubmitEmployee = {
|
|
18
|
+
user_id: number;
|
|
19
|
+
is_rca?: boolean;
|
|
20
|
+
answers: EmployeeEvaluationSubmitAnswer[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type NormalizedEmployeeSubmission = EmployeeEvaluationSubmitEmployee;
|
|
24
|
+
|
|
25
|
+
export function normalizeEmployeeSubmissions(raw: unknown): {
|
|
26
|
+
employees: NormalizedEmployeeSubmission[];
|
|
27
|
+
error?: string;
|
|
28
|
+
} {
|
|
29
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
30
|
+
return { employees: [], error: 'employees must be a non-empty array' };
|
|
31
|
+
}
|
|
32
|
+
const seen = new Set<number>();
|
|
33
|
+
const employees: NormalizedEmployeeSubmission[] = [];
|
|
34
|
+
for (const item of raw) {
|
|
35
|
+
if (item == null || typeof item !== 'object') {
|
|
36
|
+
return { employees: [], error: 'Each employee entry must be an object' };
|
|
37
|
+
}
|
|
38
|
+
const o = item as Record<string, unknown>;
|
|
39
|
+
const user_id = Number(o.user_id ?? o.employee_id);
|
|
40
|
+
if (!Number.isFinite(user_id) || user_id <= 0) {
|
|
41
|
+
return { employees: [], error: 'Each employee must have a valid user_id' };
|
|
42
|
+
}
|
|
43
|
+
if (seen.has(user_id)) {
|
|
44
|
+
return { employees: [], error: `Duplicate user_id ${user_id} in employees list` };
|
|
45
|
+
}
|
|
46
|
+
seen.add(user_id);
|
|
47
|
+
const answersRaw = o.answers;
|
|
48
|
+
if (!Array.isArray(answersRaw) || answersRaw.length === 0) {
|
|
49
|
+
return { employees: [], error: `Employee ${user_id} must include a non-empty answers array` };
|
|
50
|
+
}
|
|
51
|
+
const answers: EmployeeEvaluationSubmitAnswer[] = [];
|
|
52
|
+
const qSeen = new Set<number>();
|
|
53
|
+
for (const a of answersRaw) {
|
|
54
|
+
if (a == null || typeof a !== 'object') {
|
|
55
|
+
return { employees: [], error: `Invalid answer for employee ${user_id}` };
|
|
56
|
+
}
|
|
57
|
+
const ar = a as Record<string, unknown>;
|
|
58
|
+
const section_id = Number(ar.section_id);
|
|
59
|
+
const question_id = Number(ar.question_id);
|
|
60
|
+
const score = Number(ar.score);
|
|
61
|
+
if (!Number.isFinite(section_id) || !Number.isFinite(question_id)) {
|
|
62
|
+
return { employees: [], error: `section_id and question_id required for employee ${user_id}` };
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isFinite(score) || score < 0) {
|
|
65
|
+
return { employees: [], error: `score must be a non-negative number for employee ${user_id}, question ${question_id}` };
|
|
66
|
+
}
|
|
67
|
+
if (qSeen.has(question_id)) {
|
|
68
|
+
return { employees: [], error: `Duplicate question_id ${question_id} for employee ${user_id}` };
|
|
69
|
+
}
|
|
70
|
+
qSeen.add(question_id);
|
|
71
|
+
answers.push({
|
|
72
|
+
section_id,
|
|
73
|
+
question_id,
|
|
74
|
+
score,
|
|
75
|
+
remarks: ar.remarks != null ? String(ar.remarks) : null,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
employees.push({
|
|
79
|
+
user_id,
|
|
80
|
+
is_rca: o.is_rca !== undefined ? Boolean(o.is_rca) : false,
|
|
81
|
+
answers,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return { employees };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Resolve max employees from active eligibility setting for dept/section/month. */
|
|
88
|
+
export async function resolveMaxEmployeesPerRequest(
|
|
89
|
+
manager: EntityManager,
|
|
90
|
+
department_id: number,
|
|
91
|
+
section_id: number,
|
|
92
|
+
month: number
|
|
93
|
+
): Promise<number> {
|
|
94
|
+
const setting = await manager
|
|
95
|
+
.getRepository(EvaluationEligibilitySetting)
|
|
96
|
+
.createQueryBuilder('s')
|
|
97
|
+
.where('s.is_deleted = false')
|
|
98
|
+
.andWhere('s.is_active = true')
|
|
99
|
+
.andWhere('s.department_id = :department_id', { department_id })
|
|
100
|
+
.andWhere('s.section_id = :section_id', { section_id })
|
|
101
|
+
.andWhere('s.from_month <= :month', { month })
|
|
102
|
+
.andWhere('s.to_month >= :month', { month })
|
|
103
|
+
.orderBy('s.id', 'DESC')
|
|
104
|
+
.getOne();
|
|
105
|
+
const max = setting?.max_employees_per_request;
|
|
106
|
+
if (max != null && Number.isFinite(Number(max)) && Number(max) > 0) {
|
|
107
|
+
return Math.floor(Number(max));
|
|
108
|
+
}
|
|
109
|
+
return DEFAULT_MAX_EMPLOYEES_PER_REQUEST;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function persistEmployeeEvaluationScores(
|
|
113
|
+
manager: EntityManager,
|
|
114
|
+
opts: {
|
|
115
|
+
requestId: number;
|
|
116
|
+
formId: number;
|
|
117
|
+
createdBy: string;
|
|
118
|
+
employees: NormalizedEmployeeSubmission[];
|
|
119
|
+
}
|
|
120
|
+
): Promise<{ totalScore: number; averageScore: number; employeeCount: number }> {
|
|
121
|
+
const { requestId, formId, createdBy, employees } = opts;
|
|
122
|
+
let requestTotal = 0;
|
|
123
|
+
|
|
124
|
+
for (const emp of employees) {
|
|
125
|
+
let personTotal = 0;
|
|
126
|
+
for (const ans of emp.answers) {
|
|
127
|
+
const qMeta = await manager.findOne(EvaluationFormQuestion, {
|
|
128
|
+
where: { id: ans.question_id, is_deleted: false },
|
|
129
|
+
});
|
|
130
|
+
const questionSectionId = qMeta?.form_section_id;
|
|
131
|
+
if (!qMeta || questionSectionId == null || questionSectionId !== ans.section_id) {
|
|
132
|
+
throw new Error(`Invalid question ${ans.question_id} for section ${ans.section_id}`);
|
|
133
|
+
}
|
|
134
|
+
if (qMeta.max_score != null && ans.score > qMeta.max_score) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`score ${ans.score} exceeds max_score ${qMeta.max_score} for question ${ans.question_id}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
personTotal += ans.score;
|
|
140
|
+
await manager.save(
|
|
141
|
+
EmployeeEvaluationAnswers,
|
|
142
|
+
manager.create(EmployeeEvaluationAnswers, {
|
|
143
|
+
request_id: requestId,
|
|
144
|
+
user_id: emp.user_id,
|
|
145
|
+
form_id: formId,
|
|
146
|
+
section_id: ans.section_id,
|
|
147
|
+
question_id: ans.question_id,
|
|
148
|
+
score: ans.score,
|
|
149
|
+
remarks: ans.remarks ?? null,
|
|
150
|
+
created_by: createdBy,
|
|
151
|
+
is_deleted: false,
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await manager.save(
|
|
157
|
+
EmployeeEvaluationPersonScore,
|
|
158
|
+
manager.create(EmployeeEvaluationPersonScore, {
|
|
159
|
+
request_id: requestId,
|
|
160
|
+
user_id: emp.user_id,
|
|
161
|
+
is_rca: Boolean(emp.is_rca),
|
|
162
|
+
us_feedback: null,
|
|
163
|
+
total_score: personTotal,
|
|
164
|
+
created_by: createdBy,
|
|
165
|
+
is_deleted: false,
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
requestTotal += personTotal;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const employeeCount = employees.length;
|
|
172
|
+
const averageScore = employeeCount ? requestTotal / employeeCount : 0;
|
|
173
|
+
await manager.update(EmployeeEvaluationRequests, { id: requestId }, {
|
|
174
|
+
total_score: requestTotal,
|
|
175
|
+
average_score: averageScore,
|
|
176
|
+
employee_count: employeeCount,
|
|
177
|
+
updated_by: createdBy,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return { totalScore: requestTotal, averageScore, employeeCount };
|
|
181
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Whether evaluations are still open for a setting's month range.
|
|
3
|
-
* `evaluation_end_date` is the last calendar day (1–31) of each month in the range.
|
|
4
|
-
*/
|
|
5
|
-
export function isEvaluationEligibilityWindowOpen(
|
|
6
|
-
fromMonth: number,
|
|
7
|
-
toMonth: number,
|
|
8
|
-
evaluationEndDay: number,
|
|
9
|
-
referenceDate: Date = new Date()
|
|
10
|
-
): boolean {
|
|
11
|
-
const month = referenceDate.getUTCMonth() + 1;
|
|
12
|
-
const day = referenceDate.getUTCDate();
|
|
13
|
-
if (month < fromMonth || month > toMonth) return false;
|
|
14
|
-
if (!Number.isFinite(evaluationEndDay) || evaluationEndDay < 1 || evaluationEndDay > 31) return false;
|
|
15
|
-
return day <= evaluationEndDay;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function parseEvaluationEndDay(input: unknown): number | null {
|
|
19
|
-
if (input == null || input === '') return null;
|
|
20
|
-
const n = typeof input === 'number' ? input : parseInt(String(input).trim(), 10);
|
|
21
|
-
if (!Number.isFinite(n) || n < 1 || n > 31) return null;
|
|
22
|
-
return Math.floor(n);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function parseMonthRange(
|
|
26
|
-
fromInput: unknown,
|
|
27
|
-
toInput: unknown
|
|
28
|
-
): { from_month: number; to_month: number } | null {
|
|
29
|
-
const from_month = typeof fromInput === 'number' ? fromInput : parseInt(String(fromInput).trim(), 10);
|
|
30
|
-
const to_month = typeof toInput === 'number' ? toInput : parseInt(String(toInput).trim(), 10);
|
|
31
|
-
if (!Number.isFinite(from_month) || !Number.isFinite(to_month)) return null;
|
|
32
|
-
if (!Number.isInteger(from_month) || !Number.isInteger(to_month)) return null;
|
|
33
|
-
if (from_month < 1 || from_month > 12 || to_month < 1 || to_month > 12) return null;
|
|
34
|
-
if (from_month > to_month) return null;
|
|
35
|
-
return { from_month, to_month };
|
|
36
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Whether evaluations are still open for a setting's month range.
|
|
3
|
+
* `evaluation_end_date` is the last calendar day (1–31) of each month in the range.
|
|
4
|
+
*/
|
|
5
|
+
export function isEvaluationEligibilityWindowOpen(
|
|
6
|
+
fromMonth: number,
|
|
7
|
+
toMonth: number,
|
|
8
|
+
evaluationEndDay: number,
|
|
9
|
+
referenceDate: Date = new Date()
|
|
10
|
+
): boolean {
|
|
11
|
+
const month = referenceDate.getUTCMonth() + 1;
|
|
12
|
+
const day = referenceDate.getUTCDate();
|
|
13
|
+
if (month < fromMonth || month > toMonth) return false;
|
|
14
|
+
if (!Number.isFinite(evaluationEndDay) || evaluationEndDay < 1 || evaluationEndDay > 31) return false;
|
|
15
|
+
return day <= evaluationEndDay;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function parseEvaluationEndDay(input: unknown): number | null {
|
|
19
|
+
if (input == null || input === '') return null;
|
|
20
|
+
const n = typeof input === 'number' ? input : parseInt(String(input).trim(), 10);
|
|
21
|
+
if (!Number.isFinite(n) || n < 1 || n > 31) return null;
|
|
22
|
+
return Math.floor(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function parseMonthRange(
|
|
26
|
+
fromInput: unknown,
|
|
27
|
+
toInput: unknown
|
|
28
|
+
): { from_month: number; to_month: number } | null {
|
|
29
|
+
const from_month = typeof fromInput === 'number' ? fromInput : parseInt(String(fromInput).trim(), 10);
|
|
30
|
+
const to_month = typeof toInput === 'number' ? toInput : parseInt(String(toInput).trim(), 10);
|
|
31
|
+
if (!Number.isFinite(from_month) || !Number.isFinite(to_month)) return null;
|
|
32
|
+
if (!Number.isInteger(from_month) || !Number.isInteger(to_month)) return null;
|
|
33
|
+
if (from_month < 1 || from_month > 12 || to_month < 1 || to_month > 12) return null;
|
|
34
|
+
if (from_month > to_month) return null;
|
|
35
|
+
return { from_month, to_month };
|
|
36
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -562,6 +562,32 @@ export {
|
|
|
562
562
|
EmployeeOfMonthSupportNominationMessageType,
|
|
563
563
|
} from './models/EmployeeOfMonthSupportNominationChatModel';
|
|
564
564
|
export { EmployeeOfMonthSupportNominationRequestAttachment } from './models/EmployeeOfMonthSupportNominationAttachmentModel';
|
|
565
|
+
export {
|
|
566
|
+
EmbassyEvaluationCycle,
|
|
567
|
+
EmbassyEvaluationCycleStatus,
|
|
568
|
+
} from './models/EmbassyEvaluationCycleModel';
|
|
569
|
+
export {
|
|
570
|
+
EmbassyEvaluationAssignment,
|
|
571
|
+
EmbassyEvaluationAssignmentStatus,
|
|
572
|
+
} from './models/EmbassyEvaluationAssignmentModel';
|
|
573
|
+
export { EmbassyEvaluationResponse } from './models/EmbassyEvaluationResponseModel';
|
|
574
|
+
export {
|
|
575
|
+
EmbassyEvaluationRequests,
|
|
576
|
+
EmbassyEvaluationRequestStatus,
|
|
577
|
+
} from './models/EmbassyEvaluationRequestModel';
|
|
578
|
+
export {
|
|
579
|
+
EmbassyEvaluationApprovalDetails,
|
|
580
|
+
EmbassyEvaluationApprovalStatus,
|
|
581
|
+
} from './models/EmbassyEvaluationApprovalModel';
|
|
582
|
+
export {
|
|
583
|
+
EmbassyEvaluationWorkFlow,
|
|
584
|
+
EmbassyEvaluationWorkFlowStatus,
|
|
585
|
+
} from './models/EmbassyEvaluationWorkflowModel';
|
|
586
|
+
export {
|
|
587
|
+
EmbassyEvaluationRequestChat,
|
|
588
|
+
EmbassyEvaluationMessageType,
|
|
589
|
+
} from './models/EmbassyEvaluationChatModel';
|
|
590
|
+
export { EmbassyEvaluationRequestAttachment } from './models/EmbassyEvaluationAttachmentModel';
|
|
565
591
|
export {
|
|
566
592
|
EvaluationForm,
|
|
567
593
|
EvaluationFormType,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Column, Entity } from 'typeorm';
|
|
2
|
+
import { BaseModel } from './BaseModel';
|
|
3
|
+
|
|
4
|
+
export enum EmbassyEvaluationApprovalStatus {
|
|
5
|
+
PENDING = 'Pending',
|
|
6
|
+
IN_PROGRESS = 'In Progress',
|
|
7
|
+
APPROVED = 'Approved',
|
|
8
|
+
REJECTED = 'Rejected',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@Entity({ name: 'embassy_evaluation_approvals' })
|
|
12
|
+
export class EmbassyEvaluationApprovalDetails extends BaseModel {
|
|
13
|
+
@Column({ type: 'integer', nullable: false })
|
|
14
|
+
request_id: number;
|
|
15
|
+
|
|
16
|
+
@Column({ type: 'integer', nullable: true })
|
|
17
|
+
service_id: number | null;
|
|
18
|
+
|
|
19
|
+
@Column({ type: 'integer', nullable: true })
|
|
20
|
+
sub_service_id: number | null;
|
|
21
|
+
|
|
22
|
+
@Column({ type: 'integer', nullable: false })
|
|
23
|
+
level: number;
|
|
24
|
+
|
|
25
|
+
@Column({ type: 'integer', nullable: true })
|
|
26
|
+
approver_role_id: number | null;
|
|
27
|
+
|
|
28
|
+
@Column({ type: 'integer', nullable: true })
|
|
29
|
+
department_id: number | null;
|
|
30
|
+
|
|
31
|
+
@Column({ type: 'integer', nullable: true })
|
|
32
|
+
section_id: number | null;
|
|
33
|
+
|
|
34
|
+
@Column({ type: 'integer', nullable: true })
|
|
35
|
+
approver_user_id: number | null;
|
|
36
|
+
|
|
37
|
+
@Column({ type: 'integer', nullable: true })
|
|
38
|
+
delegate_user_id: number | null;
|
|
39
|
+
|
|
40
|
+
@Column({ type: 'integer', nullable: true })
|
|
41
|
+
approved_by: number | null;
|
|
42
|
+
|
|
43
|
+
@Column({ type: 'varchar', length: 500, nullable: true, default: '' })
|
|
44
|
+
comment: string;
|
|
45
|
+
|
|
46
|
+
@Column({
|
|
47
|
+
type: 'enum',
|
|
48
|
+
enum: EmbassyEvaluationApprovalStatus,
|
|
49
|
+
enumName: 'embassy_evaluation_approval_status_enum',
|
|
50
|
+
default: EmbassyEvaluationApprovalStatus.PENDING,
|
|
51
|
+
nullable: false,
|
|
52
|
+
})
|
|
53
|
+
approval_status: EmbassyEvaluationApprovalStatus;
|
|
54
|
+
|
|
55
|
+
@Column({ type: 'boolean', default: true, nullable: false })
|
|
56
|
+
is_allowed: boolean;
|
|
57
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Column, Entity, Index } from 'typeorm';
|
|
2
|
+
import { BaseModel } from './BaseModel';
|
|
3
|
+
|
|
4
|
+
export enum EmbassyEvaluationAssignmentStatus {
|
|
5
|
+
PENDING = 'Pending',
|
|
6
|
+
IN_PROGRESS = 'In Progress',
|
|
7
|
+
SUBMITTED = 'Submitted',
|
|
8
|
+
LOCKED = 'Locked',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@Entity({ name: 'embassy_evaluation_assignments' })
|
|
12
|
+
@Index('uq_embassy_eval_assignment', ['cycle_id', 'evaluation_year', 'mc_id', 'department_id', 'section_id'], {
|
|
13
|
+
unique: true,
|
|
14
|
+
where: '"is_deleted" = false',
|
|
15
|
+
})
|
|
16
|
+
export class EmbassyEvaluationAssignment extends BaseModel {
|
|
17
|
+
@Column({ type: 'int', nullable: false })
|
|
18
|
+
cycle_id: number;
|
|
19
|
+
|
|
20
|
+
/** Set at activation from cycle window + reference date; separates runs per year (not on cycle row). */
|
|
21
|
+
@Column({ type: 'int', nullable: false })
|
|
22
|
+
evaluation_year: number;
|
|
23
|
+
|
|
24
|
+
@Column({ type: 'int', nullable: false })
|
|
25
|
+
mc_id: number;
|
|
26
|
+
|
|
27
|
+
@Column({ type: 'int', nullable: false })
|
|
28
|
+
department_id: number;
|
|
29
|
+
|
|
30
|
+
@Column({ type: 'int', nullable: false })
|
|
31
|
+
section_id: number;
|
|
32
|
+
|
|
33
|
+
@Column({ type: 'int', nullable: false })
|
|
34
|
+
form_id: number;
|
|
35
|
+
|
|
36
|
+
@Column({ type: 'int', nullable: true })
|
|
37
|
+
assigned_to_user_id: number | null;
|
|
38
|
+
|
|
39
|
+
@Column({
|
|
40
|
+
type: 'enum',
|
|
41
|
+
enum: EmbassyEvaluationAssignmentStatus,
|
|
42
|
+
enumName: 'embassy_eval_assignment_status_enum',
|
|
43
|
+
default: EmbassyEvaluationAssignmentStatus.PENDING,
|
|
44
|
+
nullable: false,
|
|
45
|
+
})
|
|
46
|
+
status: EmbassyEvaluationAssignmentStatus;
|
|
47
|
+
|
|
48
|
+
@Column({ type: 'timestamp', nullable: true })
|
|
49
|
+
due_date: Date | null;
|
|
50
|
+
|
|
51
|
+
@Column({ type: 'timestamp', nullable: true })
|
|
52
|
+
submitted_at: Date | null;
|
|
53
|
+
|
|
54
|
+
@Column({ type: 'decimal', precision: 10, scale: 2, nullable: true })
|
|
55
|
+
score: number | null;
|
|
56
|
+
|
|
57
|
+
@Column({ type: 'boolean', default: false, nullable: false })
|
|
58
|
+
workflow_triggered: boolean;
|
|
59
|
+
|
|
60
|
+
/** Set when cycle batch request is created after all mandatory evaluations complete. */
|
|
61
|
+
@Column({ type: 'int', nullable: true })
|
|
62
|
+
request_id: number | null;
|
|
63
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Column, Entity } from 'typeorm';
|
|
2
|
+
import { BaseModel } from './BaseModel';
|
|
3
|
+
|
|
4
|
+
@Entity({ name: 'embassy_evaluation_attachments' })
|
|
5
|
+
export class EmbassyEvaluationRequestAttachment 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
|
+
file_url: string;
|
|
17
|
+
|
|
18
|
+
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
19
|
+
file_name: string | null;
|
|
20
|
+
|
|
21
|
+
@Column({ type: 'varchar', length: 100, nullable: true })
|
|
22
|
+
file_type: string | null;
|
|
23
|
+
|
|
24
|
+
@Column({ type: 'bigint', nullable: true })
|
|
25
|
+
file_size: number | null;
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Column, Entity } from 'typeorm';
|
|
2
|
+
import { BaseModel } from './BaseModel';
|
|
3
|
+
|
|
4
|
+
export enum EmbassyEvaluationMessageType {
|
|
5
|
+
TEXT = 'text',
|
|
6
|
+
IMAGE = 'image',
|
|
7
|
+
VIDEO = 'video',
|
|
8
|
+
FILE = 'file',
|
|
9
|
+
LINK = 'link',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Entity({ name: 'embassy_evaluation_chats' })
|
|
13
|
+
export class EmbassyEvaluationRequestChat extends BaseModel {
|
|
14
|
+
@Column({ type: 'integer', nullable: false })
|
|
15
|
+
request_id: number;
|
|
16
|
+
|
|
17
|
+
@Column({ type: 'integer', nullable: true })
|
|
18
|
+
service_id: number | null;
|
|
19
|
+
|
|
20
|
+
@Column({ type: 'integer', nullable: true })
|
|
21
|
+
sub_service_id: number | null;
|
|
22
|
+
|
|
23
|
+
@Column({ type: 'integer', nullable: false })
|
|
24
|
+
user_id: number;
|
|
25
|
+
|
|
26
|
+
@Column({ type: 'integer', nullable: true })
|
|
27
|
+
role_id: number | null;
|
|
28
|
+
|
|
29
|
+
@Column({ type: 'text', nullable: false })
|
|
30
|
+
message: string;
|
|
31
|
+
|
|
32
|
+
@Column({
|
|
33
|
+
type: 'enum',
|
|
34
|
+
enum: EmbassyEvaluationMessageType,
|
|
35
|
+
enumName: 'embassy_evaluation_message_type_enum',
|
|
36
|
+
default: EmbassyEvaluationMessageType.TEXT,
|
|
37
|
+
nullable: false,
|
|
38
|
+
})
|
|
39
|
+
message_type: EmbassyEvaluationMessageType;
|
|
40
|
+
|
|
41
|
+
@Column({ type: 'text', nullable: true })
|
|
42
|
+
status: string | null;
|
|
43
|
+
}
|