@eeplatform/basic-edu 1.5.2 → 1.7.0

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/index.mjs CHANGED
@@ -3559,8 +3559,11 @@ function useLearnerRepo() {
3559
3559
  }
3560
3560
  }
3561
3561
  async function getByGradeLevel(value, session) {
3562
+ value.limit = value.limit && value.limit > 0 ? value.limit : 50;
3563
+ value.skip = value.skip && value.skip > 0 ? value.skip - 1 : 0;
3562
3564
  const validation = Joi7.object({
3563
3565
  school: Joi7.string().hex().required(),
3566
+ schoolYear: Joi7.string().optional(),
3564
3567
  gradeLevel: Joi7.string().required(),
3565
3568
  status: Joi7.string().optional().allow("", null),
3566
3569
  limit: Joi7.number().integer().required(),
@@ -3577,8 +3580,14 @@ function useLearnerRepo() {
3577
3580
  };
3578
3581
  const cacheKeyOptions = {
3579
3582
  ...query,
3580
- tag: "byGradeLevel"
3583
+ tag: "byGradeLevel",
3584
+ limit: value.limit,
3585
+ skip: value.skip
3581
3586
  };
3587
+ if (value.schoolYear) {
3588
+ query.schoolYear = value.schoolYear;
3589
+ cacheKeyOptions.schoolYear = value.schoolYear;
3590
+ }
3582
3591
  if (value.school && typeof value.school === "string") {
3583
3592
  try {
3584
3593
  query.school = new ObjectId7(value.school);
@@ -3598,11 +3607,7 @@ function useLearnerRepo() {
3598
3607
  }
3599
3608
  try {
3600
3609
  const data = await collection.aggregate(
3601
- [
3602
- { $match: query },
3603
- { $skip: value.skip ?? 0 },
3604
- { $limit: value.limit ?? 100 }
3605
- ],
3610
+ [{ $match: query }, { $skip: value.skip }, { $limit: value.limit }],
3606
3611
  { session }
3607
3612
  ).toArray();
3608
3613
  setCache(cacheKey, data, 600).then(() => {
@@ -37885,15 +37890,15 @@ function useSectionRepo() {
37885
37890
  }
37886
37891
 
37887
37892
  // src/resources/section/section.controller.ts
37888
- import { BadRequestError as BadRequestError47 } from "@eeplatform/nodejs-utils";
37889
- import Joi29 from "joi";
37893
+ import { BadRequestError as BadRequestError50 } from "@eeplatform/nodejs-utils";
37894
+ import Joi32 from "joi";
37890
37895
 
37891
37896
  // src/resources/section/section.service.ts
37892
37897
  import {
37893
- AppError as AppError16,
37894
- BadRequestError as BadRequestError46,
37895
- InternalServerError as InternalServerError14,
37896
- useAtlas as useAtlas22
37898
+ AppError as AppError18,
37899
+ BadRequestError as BadRequestError49,
37900
+ InternalServerError as InternalServerError16,
37901
+ useAtlas as useAtlas23
37897
37902
  } from "@eeplatform/nodejs-utils";
37898
37903
 
37899
37904
  // src/resources/section-student/section.student.repository.ts
@@ -37902,6 +37907,8 @@ import {
37902
37907
  BadRequestError as BadRequestError45,
37903
37908
  InternalServerError as InternalServerError13,
37904
37909
  logger as logger26,
37910
+ makeCacheKey as makeCacheKey15,
37911
+ paginate as paginate14,
37905
37912
  useAtlas as useAtlas21,
37906
37913
  useCache as useCache15
37907
37914
  } from "@eeplatform/nodejs-utils";
@@ -37918,9 +37925,15 @@ var allowedSectionStudentStatuses = [
37918
37925
  ];
37919
37926
  var schemaSectionStudent = Joi28.object({
37920
37927
  _id: Joi28.string().hex().optional().allow(null, ""),
37928
+ lrn: Joi28.string().required(),
37929
+ school: Joi28.string().hex().required(),
37930
+ schoolName: Joi28.string().optional().allow(null, ""),
37921
37931
  section: Joi28.string().hex().required(),
37922
37932
  student: Joi28.string().required(),
37923
37933
  studentName: Joi28.string().required(),
37934
+ gradeLevel: Joi28.string().optional().allow(null, ""),
37935
+ educationLevel: Joi28.string().optional().allow(null, ""),
37936
+ schoolYear: Joi28.string().optional().allow(null, ""),
37924
37937
  status: Joi28.string().valid(...allowedSectionStudentStatuses).optional().allow(null, ""),
37925
37938
  assignedAt: Joi28.string().isoDate().optional().allow(null, ""),
37926
37939
  updatedAt: Joi28.string().isoDate().optional().allow(null, "")
@@ -37944,11 +37957,24 @@ function modelSectionStudent(value) {
37944
37957
  throw new Error("Invalid section ID.");
37945
37958
  }
37946
37959
  }
37960
+ if (value.school && typeof value.school === "string") {
37961
+ try {
37962
+ value.school = new ObjectId27(value.school);
37963
+ } catch (error2) {
37964
+ throw new Error("Invalid school ID.");
37965
+ }
37966
+ }
37947
37967
  return {
37948
37968
  _id: value._id,
37969
+ lrn: value.lrn,
37970
+ school: value.school,
37971
+ schoolName: value.schoolName,
37949
37972
  section: value.section,
37950
37973
  student: value.student,
37951
37974
  studentName: value.studentName,
37975
+ gradeLevel: value.gradeLevel,
37976
+ educationLevel: value.educationLevel,
37977
+ schoolYear: value.schoolYear,
37952
37978
  status: value.status ?? "active",
37953
37979
  assignedAt: value.assignedAt ?? "",
37954
37980
  updatedAt: value.updatedAt ?? ""
@@ -37956,6 +37982,8 @@ function modelSectionStudent(value) {
37956
37982
  }
37957
37983
 
37958
37984
  // src/resources/section-student/section.student.repository.ts
37985
+ import { ObjectId as ObjectId28 } from "mongodb";
37986
+ import Joi29 from "joi";
37959
37987
  function useSectionStudentRepo() {
37960
37988
  const db = useAtlas21.getDb();
37961
37989
  if (!db) {
@@ -38019,377 +38047,909 @@ function useSectionStudentRepo() {
38019
38047
  }
38020
38048
  }
38021
38049
  }
38050
+ async function getAll({
38051
+ page = 1,
38052
+ limit = 10,
38053
+ search = "",
38054
+ school = "",
38055
+ status = "",
38056
+ gradeLevel = "",
38057
+ section = "",
38058
+ schoolYear = ""
38059
+ } = {}) {
38060
+ const { error } = Joi29.object({
38061
+ page: Joi29.number().min(1).default(1),
38062
+ limit: Joi29.number().min(1).max(100).default(10),
38063
+ search: Joi29.string().allow("").default(""),
38064
+ status: Joi29.string().allow("").default(""),
38065
+ school: Joi29.string().hex().allow("").default(""),
38066
+ gradeLevel: Joi29.string().allow("").default(""),
38067
+ section: Joi29.string().hex().allow("").default(""),
38068
+ schoolYear: Joi29.string().allow("").default("")
38069
+ }).validate({ page, limit, search, status, school, gradeLevel, section });
38070
+ if (error) {
38071
+ throw new BadRequestError45(error.details[0].message);
38072
+ }
38073
+ const query = { status: { $ne: "deleted" } };
38074
+ const cacheKeyOptions = { page, limit };
38075
+ if (status) {
38076
+ query.status = status;
38077
+ cacheKeyOptions.status = status;
38078
+ }
38079
+ if (school && typeof school === "string") {
38080
+ try {
38081
+ query.school = new ObjectId28(school);
38082
+ } catch (error2) {
38083
+ throw new BadRequestError45("Invalid school ID format.");
38084
+ }
38085
+ cacheKeyOptions.school = school;
38086
+ }
38087
+ if (gradeLevel) {
38088
+ query.gradeLevel = gradeLevel;
38089
+ cacheKeyOptions.gradeLevel = gradeLevel;
38090
+ }
38091
+ if (search) {
38092
+ query.$text = { $search: search };
38093
+ cacheKeyOptions.search = search;
38094
+ }
38095
+ if (section && typeof section === "string") {
38096
+ try {
38097
+ query.section = new ObjectId28(section);
38098
+ } catch (error2) {
38099
+ throw new BadRequestError45("Invalid section ID format.");
38100
+ }
38101
+ cacheKeyOptions.section = section;
38102
+ }
38103
+ if (schoolYear) {
38104
+ query.schoolYear = schoolYear;
38105
+ cacheKeyOptions.schoolYear = schoolYear;
38106
+ }
38107
+ const cacheKey = makeCacheKey15(namespace_collection, cacheKeyOptions);
38108
+ logger26.log({
38109
+ level: "info",
38110
+ message: `Cache key for getAll sections: ${cacheKey}`
38111
+ });
38112
+ const cachedData = await getCache(cacheKey);
38113
+ if (cachedData) {
38114
+ logger26.log({
38115
+ level: "info",
38116
+ message: `Cache hit for getAll sections: ${cacheKey}`
38117
+ });
38118
+ return cachedData;
38119
+ }
38120
+ page = page > 0 ? page - 1 : 0;
38121
+ const items = await collection.aggregate([
38122
+ { $match: query },
38123
+ { $skip: page * limit },
38124
+ { $limit: limit }
38125
+ ]).toArray();
38126
+ const length = await collection.countDocuments(query);
38127
+ const data = paginate14(items, page, limit, length);
38128
+ setCache(cacheKey, data, 600).then(() => {
38129
+ logger26.log({
38130
+ level: "info",
38131
+ message: `Cache set for getAll section students: ${cacheKey}`
38132
+ });
38133
+ }).catch((err) => {
38134
+ logger26.log({
38135
+ level: "error",
38136
+ message: `Failed to set cache for getAll section students: ${err.message}`
38137
+ });
38138
+ });
38139
+ return data;
38140
+ }
38022
38141
  return {
38023
38142
  createIndexes,
38024
38143
  delCachedData,
38025
- add
38144
+ add,
38145
+ getAll
38026
38146
  };
38027
38147
  }
38028
38148
 
38029
- // src/resources/section/section.service.ts
38030
- function useSectionService() {
38031
- const { getCountByGradeLevel, getByGradeLevel: getLeanerByGradeLevel } = useLearnerRepo();
38032
- const { getByGradeLevel } = useGradeLevelRepo();
38033
- const { add: createSection } = useSectionRepo();
38034
- const { add: assignStudent } = useSectionStudentRepo();
38035
- function distributeStudents(total, minPer, maxPer) {
38036
- if (total <= 0)
38037
- return [];
38038
- if (minPer <= 0 || maxPer <= 0)
38039
- return [];
38040
- if (minPer > maxPer) {
38041
- throw new BadRequestError46(
38042
- "Minimum students per section cannot be greater than maximum."
38043
- );
38044
- }
38045
- const minSections = Math.ceil(total / maxPer);
38046
- const maxSections = Math.floor(total / minPer);
38047
- let sectionCount;
38048
- if (minSections <= maxSections) {
38049
- sectionCount = minSections;
38050
- } else {
38051
- sectionCount = minSections;
38052
- }
38053
- const base = Math.floor(total / sectionCount);
38054
- const extra = total % sectionCount;
38055
- const sizes = new Array(sectionCount).fill(base);
38056
- for (let i = 0; i < extra; i++) {
38057
- sizes[i] += 1;
38149
+ // src/resources/section-subject/section.subject.model.ts
38150
+ import { BadRequestError as BadRequestError46 } from "@eeplatform/nodejs-utils";
38151
+ import Joi30 from "joi";
38152
+ import { ObjectId as ObjectId29 } from "mongodb";
38153
+ var schemaSectionSubject = Joi30.object({
38154
+ _id: Joi30.string().hex().optional().allow(null, ""),
38155
+ school: Joi30.string().hex().required(),
38156
+ schoolName: Joi30.string().optional().allow(null, ""),
38157
+ section: Joi30.string().hex().required(),
38158
+ sectionName: Joi30.string().required(),
38159
+ gradeLevel: Joi30.string().required(),
38160
+ educationLevel: Joi30.string().required(),
38161
+ schoolYear: Joi30.string().required(),
38162
+ subjectCode: Joi30.string().required(),
38163
+ subjectName: Joi30.string().required(),
38164
+ teacher: Joi30.string().hex().optional().allow(null, ""),
38165
+ teacherName: Joi30.string().optional().allow(null, ""),
38166
+ classroom: Joi30.string().optional().allow(null, ""),
38167
+ classroomName: Joi30.string().optional().allow(null, ""),
38168
+ daysOfWeek: Joi30.array().items(Joi30.string()).optional().allow(null),
38169
+ schedule: Joi30.string().optional().allow(null, ""),
38170
+ sessionDuration: Joi30.number().optional().allow(null, 0),
38171
+ sessionFrequency: Joi30.number().optional().allow(null, 0),
38172
+ status: Joi30.string().valid("active", "draft").optional(),
38173
+ createdAt: Joi30.string().isoDate().optional().allow(null, ""),
38174
+ updatedAt: Joi30.string().isoDate().optional().allow(null, ""),
38175
+ deletedAt: Joi30.string().isoDate().optional().allow(null, "")
38176
+ });
38177
+ var schemaSectionSubjectSetup = Joi30.object({
38178
+ teacher: Joi30.string().hex().optional().allow(null, ""),
38179
+ teacherName: Joi30.string().optional().allow(null, ""),
38180
+ classroom: Joi30.string().optional().allow(null, ""),
38181
+ classroomName: Joi30.string().optional().allow(null, ""),
38182
+ daysOfWeek: Joi30.array().items(Joi30.string()).optional().allow(null),
38183
+ schedule: Joi30.string().optional().allow(null, "")
38184
+ });
38185
+ function modelSectionSubject(value) {
38186
+ const { error } = schemaSectionSubject.validate(value);
38187
+ if (error) {
38188
+ throw new BadRequestError46(`Invalid section subject data: ${error.message}`);
38189
+ }
38190
+ if (value._id && typeof value._id === "string") {
38191
+ try {
38192
+ value._id = new ObjectId29(value._id);
38193
+ } catch (error2) {
38194
+ throw new Error("Invalid _id.");
38058
38195
  }
38059
- for (const size of sizes) {
38060
- if (size > maxPer) {
38061
- throw new BadRequestError46(
38062
- `Generated section exceeds max limit of ${maxPer}.`
38063
- );
38064
- }
38196
+ }
38197
+ if (value.school && typeof value.school === "string") {
38198
+ try {
38199
+ value.school = new ObjectId29(value.school);
38200
+ } catch (error2) {
38201
+ throw new Error("Invalid school ID.");
38065
38202
  }
38066
- return sizes;
38067
38203
  }
38068
- async function generateSections(value) {
38069
- const { error } = schemaGenerateSections.validate(value);
38070
- if (error) {
38071
- throw new BadRequestError46(
38072
- `Invalid section generation data: ${error.message}`
38073
- );
38204
+ if (value.section && typeof value.section === "string") {
38205
+ try {
38206
+ value.section = new ObjectId29(value.section);
38207
+ } catch (error2) {
38208
+ throw new Error("Invalid section ID.");
38074
38209
  }
38075
- const session = useAtlas22.getClient()?.startSession();
38076
- if (!session) {
38077
- throw new Error("Unable to start database session.");
38210
+ }
38211
+ if (value.teacher && typeof value.teacher === "string") {
38212
+ try {
38213
+ value.teacher = new ObjectId29(value.teacher);
38214
+ } catch (error2) {
38215
+ throw new Error("Invalid teacher ID.");
38078
38216
  }
38217
+ }
38218
+ return {
38219
+ _id: value._id,
38220
+ school: value.school,
38221
+ schoolName: value.schoolName,
38222
+ section: value.section,
38223
+ sectionName: value.sectionName,
38224
+ gradeLevel: value.gradeLevel,
38225
+ educationLevel: value.educationLevel,
38226
+ schoolYear: value.schoolYear,
38227
+ subjectCode: value.subjectCode,
38228
+ subjectName: value.subjectName,
38229
+ teacher: value.teacher,
38230
+ teacherName: value.teacherName,
38231
+ classroom: value.classroom ?? "",
38232
+ classroomName: value.classroomName ?? "",
38233
+ daysOfWeek: value.daysOfWeek ?? [],
38234
+ schedule: value.schedule ?? "",
38235
+ sessionDuration: value.sessionDuration ?? 0,
38236
+ sessionFrequency: value.sessionFrequency ?? 0,
38237
+ status: value.status ?? "draft",
38238
+ createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
38239
+ updatedAt: value.updatedAt ?? "",
38240
+ deletedAt: value.deletedAt ?? ""
38241
+ };
38242
+ }
38243
+
38244
+ // src/resources/section-subject/section.subject.repository.ts
38245
+ import {
38246
+ AppError as AppError16,
38247
+ BadRequestError as BadRequestError47,
38248
+ InternalServerError as InternalServerError14,
38249
+ logger as logger27,
38250
+ makeCacheKey as makeCacheKey16,
38251
+ paginate as paginate15,
38252
+ useAtlas as useAtlas22,
38253
+ useCache as useCache16
38254
+ } from "@eeplatform/nodejs-utils";
38255
+ import { ObjectId as ObjectId30 } from "mongodb";
38256
+ function useSectionSubjectRepo() {
38257
+ const db = useAtlas22.getDb();
38258
+ if (!db) {
38259
+ throw new Error("Unable to connect to server.");
38260
+ }
38261
+ const namespace_collection = "deped.section.subjects";
38262
+ const collection = db.collection(namespace_collection);
38263
+ const { getCache, setCache, delNamespace } = useCache16(namespace_collection);
38264
+ async function createIndexes() {
38079
38265
  try {
38080
- await session.startTransaction();
38081
- const studentCount = await getCountByGradeLevel(
38266
+ await collection.createIndexes([
38267
+ { key: { school: 1 } },
38268
+ { key: { section: 1 } },
38269
+ { key: { teacher: 1 } },
38270
+ { key: { subjectCode: 1 } },
38271
+ { key: { schoolYear: 1 } },
38272
+ { key: { gradeLevel: 1 } },
38273
+ { key: { createdAt: 1 } },
38082
38274
  {
38083
- school: value.school,
38084
- schoolYear: value.schoolYear,
38085
- gradeLevel: value.gradeLevel
38275
+ key: {
38276
+ subjectName: "text",
38277
+ subjectCode: "text",
38278
+ teacherName: "text"
38279
+ }
38086
38280
  },
38087
- session
38088
- );
38089
- if (studentCount === 0) {
38090
- throw new BadRequestError46("No learners found for this grade level.");
38091
- }
38092
- const gradeLevelData = await getByGradeLevel(
38093
38281
  {
38094
- school: value.school,
38095
- gradeLevel: value.gradeLevel
38096
- },
38097
- session
38098
- );
38099
- if (!gradeLevelData) {
38100
- throw new BadRequestError46("Grade level not found.");
38101
- }
38102
- const minPerSection = gradeLevelData.minNumberOfLearners;
38103
- const maxPerSection = gradeLevelData.maxNumberOfLearners;
38104
- const sectionsNeeded = Math.ceil(studentCount / minPerSection);
38105
- if (sectionsNeeded > value.set.length) {
38106
- throw new BadRequestError46(
38107
- "Insufficient number of section names in set[]."
38108
- );
38109
- }
38110
- const sectionSizes = distributeStudents(
38111
- studentCount,
38112
- minPerSection,
38113
- maxPerSection
38114
- );
38115
- if (sectionSizes.length === 0) {
38116
- throw new BadRequestError46("Unable to compute section sizes.");
38117
- }
38118
- let pointer = 0;
38119
- for (let i = 0; i < sectionSizes.length; i++) {
38120
- const size = sectionSizes[i];
38121
- const sectionName = value.set[i];
38122
- const section = await createSection(
38123
- {
38124
- school: value.school,
38125
- schoolYear: value.schoolYear,
38126
- gradeLevel: value.gradeLevel,
38127
- name: sectionName,
38128
- students: size
38129
- },
38130
- session
38131
- );
38132
- const learners = await getLeanerByGradeLevel(
38133
- {
38134
- school: value.school,
38135
- gradeLevel: value.gradeLevel,
38136
- skip: pointer,
38137
- limit: size
38282
+ key: {
38283
+ school: 1,
38284
+ section: 1,
38285
+ subjectCode: 1,
38286
+ schoolYear: 1,
38287
+ status: 1
38138
38288
  },
38139
- session
38140
- );
38141
- if (!learners.length) {
38142
- throw new BadRequestError46(`No learners found for section #${i + 1}.`);
38143
- }
38144
- pointer += size;
38145
- for (const student of learners) {
38146
- if (!student._id) {
38147
- throw new BadRequestError46("Learner ID is missing.");
38148
- }
38149
- await assignStudent(
38150
- {
38151
- section: section.toString(),
38152
- student: student._id?.toString(),
38153
- studentName: `${student.learnerInfo.firstName} ${student.learnerInfo.lastName}`,
38154
- status: "active"
38155
- },
38156
- session
38157
- );
38289
+ unique: true,
38290
+ name: "unique_section_subject"
38158
38291
  }
38159
- }
38160
- await session.commitTransaction();
38161
- return "Sections generated successfully.";
38162
- } catch (error2) {
38163
- await session.abortTransaction();
38164
- if (error2 instanceof AppError16) {
38165
- throw error2;
38166
- } else {
38167
- throw new InternalServerError14("Failed to generate sections.");
38168
- }
38169
- } finally {
38170
- await session?.endSession();
38292
+ ]);
38293
+ } catch (error) {
38294
+ throw new Error("Failed to create index on section subjects.");
38171
38295
  }
38172
38296
  }
38173
- return { generateSections };
38174
- }
38175
-
38176
- // src/resources/section/section.controller.ts
38177
- function useSectionController() {
38178
- const {
38179
- add: _add,
38180
- getAll: _getAll,
38181
- getById: _getById,
38182
- getByName: _getByName,
38183
- getBySchool: _getBySchool,
38184
- updateFieldById: _updateFieldById,
38185
- addStudentToSection: _addStudentToSection,
38186
- removeStudentFromSection: _removeStudentFromSection,
38187
- deleteById: _deleteById
38188
- } = useSectionRepo();
38189
- const { generateSections: _generateSections } = useSectionService();
38190
- async function add(req, res, next) {
38191
- const value = req.body;
38192
- const { error } = schemaSection.validate(value);
38193
- if (error) {
38194
- next(new BadRequestError47(error.message));
38195
- return;
38196
- }
38197
- try {
38198
- const data = await _add(value);
38199
- res.json({
38200
- message: "Successfully created section.",
38201
- data
38297
+ function delCachedData() {
38298
+ delNamespace().then(() => {
38299
+ logger27.log({
38300
+ level: "info",
38301
+ message: `Cache namespace cleared for ${namespace_collection}`
38202
38302
  });
38203
- return;
38204
- } catch (error2) {
38205
- next(error2);
38206
- }
38303
+ }).catch((err) => {
38304
+ logger27.log({
38305
+ level: "error",
38306
+ message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
38307
+ });
38308
+ });
38207
38309
  }
38208
- async function generateSections(req, res, next) {
38209
- const value = req.body;
38210
- const { error } = schemaGenerateSections.validate(value);
38211
- if (error) {
38212
- next(new BadRequestError47(error.message));
38213
- return;
38214
- }
38310
+ async function add(value, session) {
38215
38311
  try {
38216
- const data = await _generateSections(value);
38217
- res.json({
38218
- message: "Successfully created section.",
38219
- data
38312
+ value = modelSectionSubject(value);
38313
+ const res = await collection.insertOne(value, { session });
38314
+ delCachedData();
38315
+ return res.insertedId;
38316
+ } catch (error) {
38317
+ logger27.log({
38318
+ level: "error",
38319
+ message: error.message
38220
38320
  });
38221
- return;
38222
- } catch (error2) {
38223
- next(error2);
38321
+ if (error instanceof AppError16) {
38322
+ throw error;
38323
+ } else {
38324
+ const isDuplicated = error.message.includes("duplicate");
38325
+ if (isDuplicated) {
38326
+ throw new BadRequestError47("Section subject already exists.");
38327
+ }
38328
+ throw new Error("Failed to create section subject.");
38329
+ }
38224
38330
  }
38225
38331
  }
38226
- async function getAll(req, res, next) {
38227
- const query = req.query;
38228
- const validation = Joi29.object({
38229
- page: Joi29.number().min(1).optional().allow("", null),
38230
- limit: Joi29.number().min(1).optional().allow("", null),
38231
- search: Joi29.string().optional().allow("", null),
38232
- status: Joi29.string().optional().allow("", null),
38233
- school: Joi29.string().hex().optional().allow("", null),
38234
- schoolYear: Joi29.string().optional().allow("", null),
38235
- gradeLevel: Joi29.string().optional().allow("", null)
38236
- });
38237
- const { error } = validation.validate(query);
38238
- const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
38239
- const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
38240
- const search = req.query.search ?? "";
38241
- const status = req.query.status ?? "active";
38242
- const school = req.query.school ?? "";
38243
- const schoolYear = req.query.schoolYear ?? "";
38244
- const gradeLevel = req.query.gradeLevel ?? "";
38245
- const isPageNumber = isFinite(page);
38246
- if (!isPageNumber) {
38247
- next(new BadRequestError47("Invalid page number."));
38248
- return;
38332
+ async function getAll({
38333
+ search = "",
38334
+ page = 1,
38335
+ limit = 10,
38336
+ sort = {},
38337
+ status = "active",
38338
+ school = "",
38339
+ section = "",
38340
+ teacher = "",
38341
+ schoolYear = "",
38342
+ gradeLevel = "",
38343
+ subjectCode = ""
38344
+ } = {}) {
38345
+ page = page > 0 ? page - 1 : 0;
38346
+ const query = { status: { $ne: "deleted" } };
38347
+ const cacheKeyOptions = {
38348
+ page,
38349
+ limit,
38350
+ sort: JSON.stringify(sort)
38351
+ };
38352
+ if (status) {
38353
+ query.status = status;
38354
+ cacheKeyOptions.status = status;
38249
38355
  }
38250
- const isLimitNumber = isFinite(limit);
38251
- if (!isLimitNumber) {
38252
- next(new BadRequestError47("Invalid limit number."));
38253
- return;
38356
+ if (school) {
38357
+ try {
38358
+ query.school = new ObjectId30(school);
38359
+ } catch (error) {
38360
+ throw new BadRequestError47("Invalid school ID.");
38361
+ }
38362
+ cacheKeyOptions.school = school;
38254
38363
  }
38255
- if (error) {
38256
- next(new BadRequestError47(error.message));
38257
- return;
38364
+ if (section) {
38365
+ try {
38366
+ query.section = new ObjectId30(section);
38367
+ } catch (error) {
38368
+ throw new BadRequestError47("Invalid section ID.");
38369
+ }
38370
+ cacheKeyOptions.section = section;
38258
38371
  }
38259
- try {
38260
- const data = await _getAll({
38261
- page,
38262
- limit,
38263
- search,
38264
- status,
38265
- school,
38266
- schoolYear,
38267
- gradeLevel
38268
- });
38269
- res.json(data);
38270
- return;
38271
- } catch (error2) {
38272
- next(error2);
38372
+ if (teacher) {
38373
+ try {
38374
+ query.teacher = new ObjectId30(teacher);
38375
+ } catch (error) {
38376
+ throw new BadRequestError47("Invalid teacher ID.");
38377
+ }
38378
+ cacheKeyOptions.teacher = teacher;
38273
38379
  }
38274
- }
38275
- async function getById(req, res, next) {
38276
- const id = req.params.id;
38277
- const validation = Joi29.object({
38278
- id: Joi29.string().hex().required()
38279
- });
38280
- const { error } = validation.validate({ id });
38281
- if (error) {
38282
- next(new BadRequestError47(error.message));
38283
- return;
38380
+ if (schoolYear) {
38381
+ query.schoolYear = schoolYear;
38382
+ cacheKeyOptions.schoolYear = schoolYear;
38383
+ }
38384
+ if (gradeLevel) {
38385
+ query.gradeLevel = gradeLevel;
38386
+ cacheKeyOptions.gradeLevel = gradeLevel;
38387
+ }
38388
+ if (subjectCode) {
38389
+ query.subjectCode = subjectCode;
38390
+ cacheKeyOptions.subjectCode = subjectCode;
38391
+ }
38392
+ sort = Object.keys(sort).length > 0 ? sort : { _id: 1 };
38393
+ if (search) {
38394
+ query.$text = { $search: search };
38395
+ cacheKeyOptions.search = search;
38284
38396
  }
38397
+ const cacheKey = makeCacheKey16(namespace_collection, cacheKeyOptions);
38398
+ logger27.log({
38399
+ level: "info",
38400
+ message: `Cache key for getAll section subjects: ${cacheKey}`
38401
+ });
38285
38402
  try {
38286
- const data = await _getById(id);
38287
- res.json({
38288
- message: "Successfully retrieved section.",
38289
- data
38403
+ const cached = await getCache(cacheKey);
38404
+ if (cached) {
38405
+ logger27.log({
38406
+ level: "info",
38407
+ message: `Cache hit for getAll section subjects: ${cacheKey}`
38408
+ });
38409
+ return cached;
38410
+ }
38411
+ const items = await collection.aggregate([
38412
+ { $match: query },
38413
+ { $sort: sort },
38414
+ { $skip: page * limit },
38415
+ { $limit: limit }
38416
+ ]).toArray();
38417
+ const length = await collection.countDocuments(query);
38418
+ const data = paginate15(items, page, limit, length);
38419
+ setCache(cacheKey, data, 600).then(() => {
38420
+ logger27.log({
38421
+ level: "info",
38422
+ message: `Cache set for getAll section subjects: ${cacheKey}`
38423
+ });
38424
+ }).catch((err) => {
38425
+ logger27.log({
38426
+ level: "error",
38427
+ message: `Failed to set cache for getAll section subjects: ${err.message}`
38428
+ });
38290
38429
  });
38291
- return;
38292
- } catch (error2) {
38293
- next(error2);
38430
+ return data;
38431
+ } catch (error) {
38432
+ logger27.log({ level: "error", message: `${error}` });
38433
+ throw error;
38294
38434
  }
38295
38435
  }
38296
- async function getByName(req, res, next) {
38297
- const name = req.params.name;
38298
- const validation = Joi29.object({
38299
- name: Joi29.string().required()
38300
- });
38301
- const { error } = validation.validate({ name });
38302
- if (error) {
38303
- next(new BadRequestError47(error.message));
38304
- return;
38436
+ async function getById(_id) {
38437
+ try {
38438
+ _id = new ObjectId30(_id);
38439
+ } catch (error) {
38440
+ throw new BadRequestError47("Invalid ID.");
38305
38441
  }
38442
+ const cacheKey = makeCacheKey16(namespace_collection, { _id: String(_id) });
38306
38443
  try {
38307
- const data = await _getByName(name);
38308
- res.json({
38309
- message: "Successfully retrieved section.",
38310
- data
38444
+ const cached = await getCache(cacheKey);
38445
+ if (cached) {
38446
+ logger27.log({
38447
+ level: "info",
38448
+ message: `Cache hit for getById section subject: ${cacheKey}`
38449
+ });
38450
+ return cached;
38451
+ }
38452
+ const result = await collection.findOne({
38453
+ _id,
38454
+ deletedAt: { $in: ["", null] }
38311
38455
  });
38312
- return;
38313
- } catch (error2) {
38314
- next(error2);
38456
+ if (!result) {
38457
+ throw new BadRequestError47("Section subject not found.");
38458
+ }
38459
+ setCache(cacheKey, result, 300).then(() => {
38460
+ logger27.log({
38461
+ level: "info",
38462
+ message: `Cache set for section subject by id: ${cacheKey}`
38463
+ });
38464
+ }).catch((err) => {
38465
+ logger27.log({
38466
+ level: "error",
38467
+ message: `Failed to set cache for section subject by id: ${err.message}`
38468
+ });
38469
+ });
38470
+ return result;
38471
+ } catch (error) {
38472
+ if (error instanceof AppError16) {
38473
+ throw error;
38474
+ } else {
38475
+ throw new InternalServerError14("Failed to get section subject.");
38476
+ }
38315
38477
  }
38316
38478
  }
38317
- async function getBySchool(req, res, next) {
38318
- const school = req.params.school;
38319
- const validation = Joi29.object({
38320
- school: Joi29.string().hex().required()
38321
- });
38322
- const { error } = validation.validate({ school });
38323
- if (error) {
38324
- next(new BadRequestError47(error.message));
38325
- return;
38479
+ async function getBySection(section) {
38480
+ try {
38481
+ section = new ObjectId30(section);
38482
+ } catch (error) {
38483
+ throw new BadRequestError47("Invalid section ID.");
38326
38484
  }
38485
+ const cacheKey = makeCacheKey16(namespace_collection, {
38486
+ section: String(section)
38487
+ });
38327
38488
  try {
38328
- const data = await _getBySchool(school);
38329
- res.json({
38330
- message: "Successfully retrieved sections.",
38331
- data
38489
+ const cached = await getCache(cacheKey);
38490
+ if (cached) {
38491
+ logger27.log({
38492
+ level: "info",
38493
+ message: `Cache hit for getBySection section subjects: ${cacheKey}`
38494
+ });
38495
+ return cached;
38496
+ }
38497
+ const result = await collection.find({
38498
+ section,
38499
+ deletedAt: { $in: ["", null] }
38500
+ }).toArray();
38501
+ setCache(cacheKey, result, 300).then(() => {
38502
+ logger27.log({
38503
+ level: "info",
38504
+ message: `Cache set for section subjects by section: ${cacheKey}`
38505
+ });
38506
+ }).catch((err) => {
38507
+ logger27.log({
38508
+ level: "error",
38509
+ message: `Failed to set cache for section subjects by section: ${err.message}`
38510
+ });
38332
38511
  });
38333
- return;
38334
- } catch (error2) {
38335
- next(error2);
38512
+ return result;
38513
+ } catch (error) {
38514
+ if (error instanceof AppError16) {
38515
+ throw error;
38516
+ } else {
38517
+ throw new InternalServerError14(
38518
+ "Failed to get section subjects by section."
38519
+ );
38520
+ }
38336
38521
  }
38337
38522
  }
38338
- async function updateField(req, res, next) {
38339
- const _id = req.params.id;
38340
- const { field, value } = req.body;
38341
- const validation = Joi29.object({
38342
- _id: Joi29.string().hex().required(),
38343
- field: Joi29.string().valid("name", "schoolYear", "gradeLevel", "adviser", "adviserName").required(),
38344
- value: Joi29.string().required()
38345
- });
38346
- const { error } = validation.validate({ _id, field, value });
38347
- if (error) {
38348
- next(new BadRequestError47(error.message));
38349
- return;
38523
+ async function getByTeacher(teacher) {
38524
+ try {
38525
+ teacher = new ObjectId30(teacher);
38526
+ } catch (error) {
38527
+ throw new BadRequestError47("Invalid teacher ID.");
38350
38528
  }
38529
+ const cacheKey = makeCacheKey16(namespace_collection, {
38530
+ teacher: String(teacher)
38531
+ });
38351
38532
  try {
38352
- const message = await _updateFieldById({ _id, field, value });
38353
- res.json({ message });
38533
+ const cached = await getCache(cacheKey);
38534
+ if (cached) {
38535
+ logger27.log({
38536
+ level: "info",
38537
+ message: `Cache hit for getByTeacher section subjects: ${cacheKey}`
38538
+ });
38539
+ return cached;
38540
+ }
38541
+ const result = await collection.find({
38542
+ teacher,
38543
+ deletedAt: { $in: ["", null] }
38544
+ }).toArray();
38545
+ setCache(cacheKey, result, 300).then(() => {
38546
+ logger27.log({
38547
+ level: "info",
38548
+ message: `Cache set for section subjects by teacher: ${cacheKey}`
38549
+ });
38550
+ }).catch((err) => {
38551
+ logger27.log({
38552
+ level: "error",
38553
+ message: `Failed to set cache for section subjects by teacher: ${err.message}`
38554
+ });
38555
+ });
38556
+ return result;
38557
+ } catch (error) {
38558
+ if (error instanceof AppError16) {
38559
+ throw error;
38560
+ } else {
38561
+ throw new InternalServerError14(
38562
+ "Failed to get section subjects by teacher."
38563
+ );
38564
+ }
38565
+ }
38566
+ }
38567
+ async function getBySchool(school) {
38568
+ try {
38569
+ school = new ObjectId30(school);
38570
+ } catch (error) {
38571
+ throw new BadRequestError47("Invalid school ID.");
38572
+ }
38573
+ const cacheKey = makeCacheKey16(namespace_collection, {
38574
+ school: String(school)
38575
+ });
38576
+ try {
38577
+ const cached = await getCache(cacheKey);
38578
+ if (cached) {
38579
+ logger27.log({
38580
+ level: "info",
38581
+ message: `Cache hit for getBySchool section subjects: ${cacheKey}`
38582
+ });
38583
+ return cached;
38584
+ }
38585
+ const result = await collection.find({
38586
+ school,
38587
+ deletedAt: { $in: ["", null] }
38588
+ }).toArray();
38589
+ setCache(cacheKey, result, 300).then(() => {
38590
+ logger27.log({
38591
+ level: "info",
38592
+ message: `Cache set for section subjects by school: ${cacheKey}`
38593
+ });
38594
+ }).catch((err) => {
38595
+ logger27.log({
38596
+ level: "error",
38597
+ message: `Failed to set cache for section subjects by school: ${err.message}`
38598
+ });
38599
+ });
38600
+ return result;
38601
+ } catch (error) {
38602
+ if (error instanceof AppError16) {
38603
+ throw error;
38604
+ } else {
38605
+ throw new InternalServerError14(
38606
+ "Failed to get section subjects by school."
38607
+ );
38608
+ }
38609
+ }
38610
+ }
38611
+ async function updateFieldById({ _id, field, value } = {}, session) {
38612
+ const allowedFields = [
38613
+ "teacher",
38614
+ "teacherName",
38615
+ "classroom",
38616
+ "schedule",
38617
+ "daysOfWeek"
38618
+ ];
38619
+ if (!allowedFields.includes(field)) {
38620
+ throw new BadRequestError47(
38621
+ `Field "${field}" is not allowed to be updated.`
38622
+ );
38623
+ }
38624
+ try {
38625
+ _id = new ObjectId30(_id);
38626
+ } catch (error) {
38627
+ throw new BadRequestError47("Invalid ID.");
38628
+ }
38629
+ if (field === "teacher" && value) {
38630
+ try {
38631
+ value = new ObjectId30(value).toString();
38632
+ } catch (error) {
38633
+ throw new BadRequestError47("Invalid teacher ID.");
38634
+ }
38635
+ }
38636
+ try {
38637
+ const updateValue = field === "teacher" ? new ObjectId30(value) : value;
38638
+ await collection.updateOne(
38639
+ { _id, deletedAt: { $in: ["", null] } },
38640
+ { $set: { [field]: updateValue, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
38641
+ { session }
38642
+ );
38643
+ delCachedData();
38644
+ return `Successfully updated section subject ${field}.`;
38645
+ } catch (error) {
38646
+ throw new InternalServerError14(
38647
+ `Failed to update section subject ${field}.`
38648
+ );
38649
+ }
38650
+ }
38651
+ async function setupById(_id, value, session) {
38652
+ const { error } = schemaSectionSubjectSetup.validate(value);
38653
+ if (error) {
38654
+ throw new BadRequestError47(
38655
+ `Invalid section subject data: ${error.message}`
38656
+ );
38657
+ }
38658
+ try {
38659
+ _id = new ObjectId30(_id);
38660
+ } catch (error2) {
38661
+ throw new BadRequestError47("Invalid ID.");
38662
+ }
38663
+ try {
38664
+ await collection.updateOne(
38665
+ { _id, deletedAt: { $in: ["", null] } },
38666
+ { $set: { ...value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
38667
+ { session }
38668
+ );
38669
+ delCachedData();
38670
+ return `Successfully updated section subject.`;
38671
+ } catch (error2) {
38672
+ throw new InternalServerError14(`Failed to update section subject.`);
38673
+ }
38674
+ }
38675
+ async function deleteById(_id) {
38676
+ try {
38677
+ _id = new ObjectId30(_id);
38678
+ } catch (error) {
38679
+ throw new BadRequestError47("Invalid ID.");
38680
+ }
38681
+ try {
38682
+ await collection.updateOne(
38683
+ { _id },
38684
+ { $set: { status: "deleted", deletedAt: (/* @__PURE__ */ new Date()).toISOString() } }
38685
+ );
38686
+ delCachedData();
38687
+ return "Successfully deleted section subject.";
38688
+ } catch (error) {
38689
+ throw new InternalServerError14("Failed to delete section subject.");
38690
+ }
38691
+ }
38692
+ return {
38693
+ createIndexes,
38694
+ add,
38695
+ getAll,
38696
+ getById,
38697
+ getBySection,
38698
+ getByTeacher,
38699
+ getBySchool,
38700
+ updateFieldById,
38701
+ setupById,
38702
+ deleteById
38703
+ };
38704
+ }
38705
+
38706
+ // src/resources/section-subject/section.subject.service.ts
38707
+ import { AppError as AppError17, InternalServerError as InternalServerError15 } from "@eeplatform/nodejs-utils";
38708
+ function useSectionSubjectService() {
38709
+ const { getById: getSchoolById } = useSchoolRepo();
38710
+ const { add: _add } = useSectionSubjectRepo();
38711
+ async function add(value) {
38712
+ const { error } = schemaSectionSubject.validate(value);
38713
+ if (error) {
38714
+ throw new Error(`Invalid section subject data: ${error.message}`);
38715
+ }
38716
+ try {
38717
+ const school = await getSchoolById(value.school);
38718
+ if (!school) {
38719
+ throw new Error("School not found.");
38720
+ }
38721
+ if (!school.name) {
38722
+ throw new Error("School name is missing.");
38723
+ }
38724
+ value.schoolName = school.name;
38725
+ await _add(value);
38726
+ return "Successfully created section subject.";
38727
+ } catch (error2) {
38728
+ if (error2 instanceof AppError17) {
38729
+ throw error2;
38730
+ } else {
38731
+ throw new InternalServerError15("Failed to create section subject.");
38732
+ }
38733
+ }
38734
+ }
38735
+ return {
38736
+ add
38737
+ };
38738
+ }
38739
+
38740
+ // src/resources/section-subject/section.subject.controller.ts
38741
+ import { BadRequestError as BadRequestError48 } from "@eeplatform/nodejs-utils";
38742
+ import Joi31 from "joi";
38743
+ function useSectionSubjectController() {
38744
+ const {
38745
+ getAll: _getAll,
38746
+ getById: _getById,
38747
+ getBySection: _getBySection,
38748
+ getByTeacher: _getByTeacher,
38749
+ getBySchool: _getBySchool,
38750
+ updateFieldById: _updateFieldById,
38751
+ deleteById: _deleteById,
38752
+ setupById: _setupById
38753
+ } = useSectionSubjectRepo();
38754
+ const { add: _add } = useSectionSubjectService();
38755
+ async function add(req, res, next) {
38756
+ const value = req.body;
38757
+ const { error } = schemaSectionSubject.validate(value);
38758
+ if (error) {
38759
+ next(new BadRequestError48(error.message));
38760
+ return;
38761
+ }
38762
+ try {
38763
+ const data = await _add(value);
38764
+ res.json({
38765
+ message: "Successfully created section subject.",
38766
+ data
38767
+ });
38354
38768
  return;
38355
38769
  } catch (error2) {
38356
38770
  next(error2);
38357
38771
  }
38358
38772
  }
38359
- async function addStudent(req, res, next) {
38773
+ async function getAll(req, res, next) {
38774
+ const query = req.query;
38775
+ const validation = Joi31.object({
38776
+ page: Joi31.number().min(1).optional().allow("", null),
38777
+ limit: Joi31.number().min(1).optional().allow("", null),
38778
+ search: Joi31.string().optional().allow("", null),
38779
+ status: Joi31.string().optional().allow("", null),
38780
+ school: Joi31.string().hex().optional().allow("", null),
38781
+ section: Joi31.string().hex().optional().allow("", null),
38782
+ teacher: Joi31.string().hex().optional().allow("", null),
38783
+ schoolYear: Joi31.string().optional().allow("", null),
38784
+ gradeLevel: Joi31.string().optional().allow("", null),
38785
+ subjectCode: Joi31.string().optional().allow("", null)
38786
+ });
38787
+ const { error } = validation.validate(query);
38788
+ const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
38789
+ const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
38790
+ const search = req.query.search ?? "";
38791
+ const status = req.query.status ?? "";
38792
+ const school = req.query.school ?? "";
38793
+ const section = req.query.section ?? "";
38794
+ const teacher = req.query.teacher ?? "";
38795
+ const schoolYear = req.query.schoolYear ?? "";
38796
+ const gradeLevel = req.query.gradeLevel ?? "";
38797
+ const subjectCode = req.query.subjectCode ?? "";
38798
+ const isPageNumber = isFinite(page);
38799
+ if (!isPageNumber) {
38800
+ next(new BadRequestError48("Invalid page number."));
38801
+ return;
38802
+ }
38803
+ const isLimitNumber = isFinite(limit);
38804
+ if (!isLimitNumber) {
38805
+ next(new BadRequestError48("Invalid limit number."));
38806
+ return;
38807
+ }
38808
+ if (error) {
38809
+ next(new BadRequestError48(error.message));
38810
+ return;
38811
+ }
38812
+ try {
38813
+ const data = await _getAll({
38814
+ page,
38815
+ limit,
38816
+ search,
38817
+ status,
38818
+ school,
38819
+ section,
38820
+ teacher,
38821
+ schoolYear,
38822
+ gradeLevel,
38823
+ subjectCode
38824
+ });
38825
+ res.json(data);
38826
+ return;
38827
+ } catch (error2) {
38828
+ next(error2);
38829
+ }
38830
+ }
38831
+ async function getById(req, res, next) {
38832
+ const id = req.params.id;
38833
+ const validation = Joi31.object({
38834
+ id: Joi31.string().hex().required()
38835
+ });
38836
+ const { error } = validation.validate({ id });
38837
+ if (error) {
38838
+ next(new BadRequestError48(error.message));
38839
+ return;
38840
+ }
38841
+ try {
38842
+ const data = await _getById(id);
38843
+ res.json({
38844
+ message: "Successfully retrieved section subject.",
38845
+ data
38846
+ });
38847
+ return;
38848
+ } catch (error2) {
38849
+ next(error2);
38850
+ }
38851
+ }
38852
+ async function getBySection(req, res, next) {
38853
+ const section = req.params.section;
38854
+ const validation = Joi31.object({
38855
+ section: Joi31.string().hex().required()
38856
+ });
38857
+ const { error } = validation.validate({ section });
38858
+ if (error) {
38859
+ next(new BadRequestError48(error.message));
38860
+ return;
38861
+ }
38862
+ try {
38863
+ const data = await _getBySection(section);
38864
+ res.json({
38865
+ message: "Successfully retrieved section subjects.",
38866
+ data
38867
+ });
38868
+ return;
38869
+ } catch (error2) {
38870
+ next(error2);
38871
+ }
38872
+ }
38873
+ async function getByTeacher(req, res, next) {
38874
+ const teacher = req.params.teacher;
38875
+ const validation = Joi31.object({
38876
+ teacher: Joi31.string().hex().required()
38877
+ });
38878
+ const { error } = validation.validate({ teacher });
38879
+ if (error) {
38880
+ next(new BadRequestError48(error.message));
38881
+ return;
38882
+ }
38883
+ try {
38884
+ const data = await _getByTeacher(teacher);
38885
+ res.json({
38886
+ message: "Successfully retrieved section subjects.",
38887
+ data
38888
+ });
38889
+ return;
38890
+ } catch (error2) {
38891
+ next(error2);
38892
+ }
38893
+ }
38894
+ async function getBySchool(req, res, next) {
38895
+ const school = req.params.school;
38896
+ const validation = Joi31.object({
38897
+ school: Joi31.string().hex().required()
38898
+ });
38899
+ const { error } = validation.validate({ school });
38900
+ if (error) {
38901
+ next(new BadRequestError48(error.message));
38902
+ return;
38903
+ }
38904
+ try {
38905
+ const data = await _getBySchool(school);
38906
+ res.json({
38907
+ message: "Successfully retrieved section subjects.",
38908
+ data
38909
+ });
38910
+ return;
38911
+ } catch (error2) {
38912
+ next(error2);
38913
+ }
38914
+ }
38915
+ async function updateField(req, res, next) {
38360
38916
  const _id = req.params.id;
38361
- const { studentId } = req.body;
38362
- const validation = Joi29.object({
38363
- _id: Joi29.string().hex().required(),
38364
- studentId: Joi29.string().required()
38917
+ const { field, value } = req.body;
38918
+ const validation = Joi31.object({
38919
+ _id: Joi31.string().hex().required(),
38920
+ field: Joi31.string().valid(
38921
+ "subjectCode",
38922
+ "subjectName",
38923
+ "teacher",
38924
+ "teacherName",
38925
+ "classroom",
38926
+ "schedule"
38927
+ ).required(),
38928
+ value: Joi31.string().required()
38365
38929
  });
38366
- const { error } = validation.validate({ _id, studentId });
38930
+ const { error } = validation.validate({ _id, field, value });
38367
38931
  if (error) {
38368
- next(new BadRequestError47(error.message));
38932
+ next(new BadRequestError48(error.message));
38369
38933
  return;
38370
38934
  }
38371
38935
  try {
38372
- const message = await _addStudentToSection(_id, studentId);
38936
+ const message = await _updateFieldById({ _id, field, value });
38373
38937
  res.json({ message });
38374
38938
  return;
38375
38939
  } catch (error2) {
38376
38940
  next(error2);
38377
38941
  }
38378
38942
  }
38379
- async function removeStudent(req, res, next) {
38943
+ async function setupById(req, res, next) {
38380
38944
  const _id = req.params.id;
38381
- const { studentId } = req.body;
38382
- const validation = Joi29.object({
38383
- _id: Joi29.string().hex().required(),
38384
- studentId: Joi29.string().required()
38385
- });
38386
- const { error } = validation.validate({ _id, studentId });
38945
+ const data = req.body;
38946
+ const { error } = schemaSectionSubjectSetup.validate(data);
38387
38947
  if (error) {
38388
- next(new BadRequestError47(error.message));
38948
+ next(new BadRequestError48(error.message));
38389
38949
  return;
38390
38950
  }
38391
38951
  try {
38392
- const message = await _removeStudentFromSection(_id, studentId);
38952
+ const message = await _setupById(_id, data);
38393
38953
  res.json({ message });
38394
38954
  return;
38395
38955
  } catch (error2) {
@@ -38398,12 +38958,12 @@ function useSectionController() {
38398
38958
  }
38399
38959
  async function deleteById(req, res, next) {
38400
38960
  const _id = req.params.id;
38401
- const validation = Joi29.object({
38402
- _id: Joi29.string().hex().required()
38961
+ const validation = Joi31.object({
38962
+ _id: Joi31.string().hex().required()
38403
38963
  });
38404
38964
  const { error } = validation.validate({ _id });
38405
38965
  if (error) {
38406
- next(new BadRequestError47(error.message));
38966
+ next(new BadRequestError48(error.message));
38407
38967
  return;
38408
38968
  }
38409
38969
  try {
@@ -38416,27 +38976,2260 @@ function useSectionController() {
38416
38976
  }
38417
38977
  return {
38418
38978
  add,
38419
- generateSections,
38420
38979
  getAll,
38421
38980
  getById,
38422
- getByName,
38981
+ getBySection,
38982
+ getByTeacher,
38423
38983
  getBySchool,
38424
38984
  updateField,
38425
- addStudent,
38426
- removeStudent,
38985
+ setupById,
38427
38986
  deleteById
38428
38987
  };
38429
38988
  }
38430
38989
 
38990
+ // src/resources/section/section.service.ts
38991
+ function useSectionService() {
38992
+ const { getCountByGradeLevel, getByGradeLevel: getLeanerByGradeLevel } = useLearnerRepo();
38993
+ const { getByGradeLevel } = useGradeLevelRepo();
38994
+ const { add: createSection } = useSectionRepo();
38995
+ const { add: assignStudent } = useSectionStudentRepo();
38996
+ const { getAll: getAllCurriculumSubjects } = useCurriculumSubjectRepo();
38997
+ const { add: addSectionSubject } = useSectionSubjectRepo();
38998
+ const { getById: getSchoolById } = useSchoolRepo();
38999
+ function distributeStudents(total, minPer, maxPer) {
39000
+ if (total <= 0)
39001
+ return [];
39002
+ if (minPer <= 0 || maxPer <= 0)
39003
+ return [];
39004
+ if (minPer > maxPer) {
39005
+ throw new BadRequestError49(
39006
+ "Minimum students per section cannot be greater than maximum."
39007
+ );
39008
+ }
39009
+ const minSections = Math.ceil(total / maxPer);
39010
+ const maxSections = Math.floor(total / minPer);
39011
+ let sectionCount;
39012
+ if (minSections <= maxSections) {
39013
+ sectionCount = minSections;
39014
+ } else {
39015
+ sectionCount = minSections;
39016
+ }
39017
+ const base = Math.floor(total / sectionCount);
39018
+ const extra = total % sectionCount;
39019
+ const sizes = new Array(sectionCount).fill(base);
39020
+ for (let i = 0; i < extra; i++) {
39021
+ sizes[i] += 1;
39022
+ }
39023
+ for (const size of sizes) {
39024
+ if (size > maxPer) {
39025
+ throw new BadRequestError49(
39026
+ `Generated section exceeds max limit of ${maxPer}.`
39027
+ );
39028
+ }
39029
+ }
39030
+ return sizes;
39031
+ }
39032
+ async function generateSections(value) {
39033
+ const { error } = schemaGenerateSections.validate(value);
39034
+ if (error) {
39035
+ throw new BadRequestError49(
39036
+ `Invalid section generation data: ${error.message}`
39037
+ );
39038
+ }
39039
+ const session = useAtlas23.getClient()?.startSession();
39040
+ if (!session) {
39041
+ throw new Error("Unable to start database session.");
39042
+ }
39043
+ try {
39044
+ await session.startTransaction();
39045
+ const studentCount = await getCountByGradeLevel(
39046
+ {
39047
+ school: value.school,
39048
+ schoolYear: value.schoolYear,
39049
+ gradeLevel: value.gradeLevel
39050
+ },
39051
+ session
39052
+ );
39053
+ if (studentCount === 0) {
39054
+ throw new BadRequestError49("No learners found for this grade level.");
39055
+ }
39056
+ const gradeLevelData = await getByGradeLevel(
39057
+ {
39058
+ school: value.school,
39059
+ gradeLevel: value.gradeLevel
39060
+ },
39061
+ session
39062
+ );
39063
+ if (!gradeLevelData) {
39064
+ throw new BadRequestError49("Grade level not found.");
39065
+ }
39066
+ const minPerSection = gradeLevelData.minNumberOfLearners;
39067
+ const maxPerSection = gradeLevelData.maxNumberOfLearners;
39068
+ const sectionsNeeded = Math.ceil(studentCount / minPerSection);
39069
+ if (sectionsNeeded > value.set.length) {
39070
+ throw new BadRequestError49(
39071
+ "Insufficient number of section names in set[]."
39072
+ );
39073
+ }
39074
+ const sectionSizes = distributeStudents(
39075
+ studentCount,
39076
+ minPerSection,
39077
+ maxPerSection
39078
+ );
39079
+ if (sectionSizes.length === 0) {
39080
+ throw new BadRequestError49("Unable to compute section sizes.");
39081
+ }
39082
+ const schoolData = await getSchoolById(value.school);
39083
+ if (!schoolData) {
39084
+ throw new BadRequestError49("School not found.");
39085
+ }
39086
+ let totalStudentsProcessed = 0;
39087
+ for (let i = 0; i < sectionSizes.length; i++) {
39088
+ const size = sectionSizes[i];
39089
+ const sectionName = value.set[i];
39090
+ const section = await createSection(
39091
+ {
39092
+ school: value.school,
39093
+ schoolYear: value.schoolYear,
39094
+ gradeLevel: value.gradeLevel,
39095
+ name: sectionName,
39096
+ students: size
39097
+ },
39098
+ session
39099
+ );
39100
+ const skip = totalStudentsProcessed;
39101
+ const learners = await getLeanerByGradeLevel(
39102
+ {
39103
+ school: value.school,
39104
+ gradeLevel: value.gradeLevel,
39105
+ skip,
39106
+ limit: size
39107
+ },
39108
+ session
39109
+ );
39110
+ if (!learners.length) {
39111
+ throw new BadRequestError49(`No learners found for section #${i + 1}.`);
39112
+ }
39113
+ totalStudentsProcessed += learners.length;
39114
+ for (const student of learners) {
39115
+ if (!student._id) {
39116
+ throw new BadRequestError49("Learner ID is missing.");
39117
+ }
39118
+ if (!student.learnerInfo.lrn) {
39119
+ throw new BadRequestError49("Learner LRN is missing.");
39120
+ }
39121
+ await assignStudent(
39122
+ {
39123
+ section: section.toString(),
39124
+ lrn: student.learnerInfo.lrn,
39125
+ student: student._id?.toString(),
39126
+ studentName: `${student.learnerInfo.firstName} ${student.learnerInfo.lastName}`,
39127
+ school: value.school,
39128
+ schoolName: schoolData.name,
39129
+ gradeLevel: value.gradeLevel,
39130
+ educationLevel: gradeLevelData.educationLevel,
39131
+ schoolYear: value.schoolYear,
39132
+ status: "active"
39133
+ },
39134
+ session
39135
+ );
39136
+ }
39137
+ const curriculumSubjects = await getAllCurriculumSubjects({
39138
+ schoolYear: Number(value.schoolYear),
39139
+ gradeLevel: value.gradeLevel,
39140
+ limit: 20
39141
+ });
39142
+ for (const curriculumSubject of curriculumSubjects.items) {
39143
+ await addSectionSubject(
39144
+ {
39145
+ school: value.school,
39146
+ schoolName: schoolData.name,
39147
+ gradeLevel: value.gradeLevel,
39148
+ educationLevel: gradeLevelData.educationLevel,
39149
+ schoolYear: value.schoolYear,
39150
+ section: section.toString(),
39151
+ sectionName,
39152
+ subjectCode: curriculumSubject.subjectCode,
39153
+ subjectName: curriculumSubject.subjectName,
39154
+ teacher: "",
39155
+ teacherName: "",
39156
+ schedule: "",
39157
+ daysOfWeek: [],
39158
+ classroom: "",
39159
+ classroomName: "",
39160
+ sessionDuration: curriculumSubject.sessionDuration,
39161
+ sessionFrequency: curriculumSubject.sessionFrequency,
39162
+ status: "draft"
39163
+ },
39164
+ session
39165
+ );
39166
+ }
39167
+ }
39168
+ await session.commitTransaction();
39169
+ return "Sections generated successfully.";
39170
+ } catch (error2) {
39171
+ await session.abortTransaction();
39172
+ if (error2 instanceof AppError18) {
39173
+ throw error2;
39174
+ } else {
39175
+ throw new InternalServerError16("Failed to generate sections.");
39176
+ }
39177
+ } finally {
39178
+ await session?.endSession();
39179
+ }
39180
+ }
39181
+ return { generateSections };
39182
+ }
39183
+
39184
+ // src/resources/section/section.controller.ts
39185
+ function useSectionController() {
39186
+ const {
39187
+ add: _add,
39188
+ getAll: _getAll,
39189
+ getById: _getById,
39190
+ getByName: _getByName,
39191
+ getBySchool: _getBySchool,
39192
+ updateFieldById: _updateFieldById,
39193
+ addStudentToSection: _addStudentToSection,
39194
+ removeStudentFromSection: _removeStudentFromSection,
39195
+ deleteById: _deleteById
39196
+ } = useSectionRepo();
39197
+ const { generateSections: _generateSections } = useSectionService();
39198
+ async function add(req, res, next) {
39199
+ const value = req.body;
39200
+ const { error } = schemaSection.validate(value);
39201
+ if (error) {
39202
+ next(new BadRequestError50(error.message));
39203
+ return;
39204
+ }
39205
+ try {
39206
+ const data = await _add(value);
39207
+ res.json({
39208
+ message: "Successfully created section.",
39209
+ data
39210
+ });
39211
+ return;
39212
+ } catch (error2) {
39213
+ next(error2);
39214
+ }
39215
+ }
39216
+ async function generateSections(req, res, next) {
39217
+ const value = req.body;
39218
+ const { error } = schemaGenerateSections.validate(value);
39219
+ if (error) {
39220
+ next(new BadRequestError50(error.message));
39221
+ return;
39222
+ }
39223
+ try {
39224
+ const data = await _generateSections(value);
39225
+ res.json({
39226
+ message: "Successfully created section.",
39227
+ data
39228
+ });
39229
+ return;
39230
+ } catch (error2) {
39231
+ next(error2);
39232
+ }
39233
+ }
39234
+ async function getAll(req, res, next) {
39235
+ const query = req.query;
39236
+ const validation = Joi32.object({
39237
+ page: Joi32.number().min(1).optional().allow("", null),
39238
+ limit: Joi32.number().min(1).optional().allow("", null),
39239
+ search: Joi32.string().optional().allow("", null),
39240
+ status: Joi32.string().optional().allow("", null),
39241
+ school: Joi32.string().hex().optional().allow("", null),
39242
+ schoolYear: Joi32.string().optional().allow("", null),
39243
+ gradeLevel: Joi32.string().optional().allow("", null)
39244
+ });
39245
+ const { error } = validation.validate(query);
39246
+ const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
39247
+ const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
39248
+ const search = req.query.search ?? "";
39249
+ const status = req.query.status ?? "active";
39250
+ const school = req.query.school ?? "";
39251
+ const schoolYear = req.query.schoolYear ?? "";
39252
+ const gradeLevel = req.query.gradeLevel ?? "";
39253
+ const isPageNumber = isFinite(page);
39254
+ if (!isPageNumber) {
39255
+ next(new BadRequestError50("Invalid page number."));
39256
+ return;
39257
+ }
39258
+ const isLimitNumber = isFinite(limit);
39259
+ if (!isLimitNumber) {
39260
+ next(new BadRequestError50("Invalid limit number."));
39261
+ return;
39262
+ }
39263
+ if (error) {
39264
+ next(new BadRequestError50(error.message));
39265
+ return;
39266
+ }
39267
+ try {
39268
+ const data = await _getAll({
39269
+ page,
39270
+ limit,
39271
+ search,
39272
+ status,
39273
+ school,
39274
+ schoolYear,
39275
+ gradeLevel
39276
+ });
39277
+ res.json(data);
39278
+ return;
39279
+ } catch (error2) {
39280
+ next(error2);
39281
+ }
39282
+ }
39283
+ async function getById(req, res, next) {
39284
+ const id = req.params.id;
39285
+ const validation = Joi32.object({
39286
+ id: Joi32.string().hex().required()
39287
+ });
39288
+ const { error } = validation.validate({ id });
39289
+ if (error) {
39290
+ next(new BadRequestError50(error.message));
39291
+ return;
39292
+ }
39293
+ try {
39294
+ const data = await _getById(id);
39295
+ res.json(data);
39296
+ return;
39297
+ } catch (error2) {
39298
+ next(error2);
39299
+ }
39300
+ }
39301
+ async function getByName(req, res, next) {
39302
+ const name = req.params.name;
39303
+ const validation = Joi32.object({
39304
+ name: Joi32.string().required()
39305
+ });
39306
+ const { error } = validation.validate({ name });
39307
+ if (error) {
39308
+ next(new BadRequestError50(error.message));
39309
+ return;
39310
+ }
39311
+ try {
39312
+ const data = await _getByName(name);
39313
+ res.json({
39314
+ message: "Successfully retrieved section.",
39315
+ data
39316
+ });
39317
+ return;
39318
+ } catch (error2) {
39319
+ next(error2);
39320
+ }
39321
+ }
39322
+ async function getBySchool(req, res, next) {
39323
+ const school = req.params.school;
39324
+ const validation = Joi32.object({
39325
+ school: Joi32.string().hex().required()
39326
+ });
39327
+ const { error } = validation.validate({ school });
39328
+ if (error) {
39329
+ next(new BadRequestError50(error.message));
39330
+ return;
39331
+ }
39332
+ try {
39333
+ const data = await _getBySchool(school);
39334
+ res.json({
39335
+ message: "Successfully retrieved sections.",
39336
+ data
39337
+ });
39338
+ return;
39339
+ } catch (error2) {
39340
+ next(error2);
39341
+ }
39342
+ }
39343
+ async function updateField(req, res, next) {
39344
+ const _id = req.params.id;
39345
+ const { field, value } = req.body;
39346
+ const validation = Joi32.object({
39347
+ _id: Joi32.string().hex().required(),
39348
+ field: Joi32.string().valid("name", "schoolYear", "gradeLevel", "adviser", "adviserName").required(),
39349
+ value: Joi32.string().required()
39350
+ });
39351
+ const { error } = validation.validate({ _id, field, value });
39352
+ if (error) {
39353
+ next(new BadRequestError50(error.message));
39354
+ return;
39355
+ }
39356
+ try {
39357
+ const message = await _updateFieldById({ _id, field, value });
39358
+ res.json({ message });
39359
+ return;
39360
+ } catch (error2) {
39361
+ next(error2);
39362
+ }
39363
+ }
39364
+ async function addStudent(req, res, next) {
39365
+ const _id = req.params.id;
39366
+ const { studentId } = req.body;
39367
+ const validation = Joi32.object({
39368
+ _id: Joi32.string().hex().required(),
39369
+ studentId: Joi32.string().required()
39370
+ });
39371
+ const { error } = validation.validate({ _id, studentId });
39372
+ if (error) {
39373
+ next(new BadRequestError50(error.message));
39374
+ return;
39375
+ }
39376
+ try {
39377
+ const message = await _addStudentToSection(_id, studentId);
39378
+ res.json({ message });
39379
+ return;
39380
+ } catch (error2) {
39381
+ next(error2);
39382
+ }
39383
+ }
39384
+ async function removeStudent(req, res, next) {
39385
+ const _id = req.params.id;
39386
+ const { studentId } = req.body;
39387
+ const validation = Joi32.object({
39388
+ _id: Joi32.string().hex().required(),
39389
+ studentId: Joi32.string().required()
39390
+ });
39391
+ const { error } = validation.validate({ _id, studentId });
39392
+ if (error) {
39393
+ next(new BadRequestError50(error.message));
39394
+ return;
39395
+ }
39396
+ try {
39397
+ const message = await _removeStudentFromSection(_id, studentId);
39398
+ res.json({ message });
39399
+ return;
39400
+ } catch (error2) {
39401
+ next(error2);
39402
+ }
39403
+ }
39404
+ async function deleteById(req, res, next) {
39405
+ const _id = req.params.id;
39406
+ const validation = Joi32.object({
39407
+ _id: Joi32.string().hex().required()
39408
+ });
39409
+ const { error } = validation.validate({ _id });
39410
+ if (error) {
39411
+ next(new BadRequestError50(error.message));
39412
+ return;
39413
+ }
39414
+ try {
39415
+ const message = await _deleteById(_id);
39416
+ res.json({ message });
39417
+ return;
39418
+ } catch (error2) {
39419
+ next(error2);
39420
+ }
39421
+ }
39422
+ return {
39423
+ add,
39424
+ generateSections,
39425
+ getAll,
39426
+ getById,
39427
+ getByName,
39428
+ getBySchool,
39429
+ updateField,
39430
+ addStudent,
39431
+ removeStudent,
39432
+ deleteById
39433
+ };
39434
+ }
39435
+
39436
+ // src/resources/section-student/section.student.controller.ts
39437
+ import { BadRequestError as BadRequestError51 } from "@eeplatform/nodejs-utils";
39438
+ import Joi33 from "joi";
39439
+ function useSectionStudentController() {
39440
+ const { add: _add, getAll: _getAll } = useSectionStudentRepo();
39441
+ async function add(req, res, next) {
39442
+ const value = req.body;
39443
+ const { error } = schemaSectionStudent.validate(value);
39444
+ if (error) {
39445
+ next(new BadRequestError51(error.message));
39446
+ return;
39447
+ }
39448
+ try {
39449
+ const data = await _add(value);
39450
+ res.json({
39451
+ message: "Successfully created section student.",
39452
+ data
39453
+ });
39454
+ return;
39455
+ } catch (error2) {
39456
+ next(error2);
39457
+ }
39458
+ }
39459
+ async function getAll(req, res, next) {
39460
+ const query = req.query;
39461
+ const validation = Joi33.object({
39462
+ page: Joi33.number().min(1).optional().allow("", null),
39463
+ limit: Joi33.number().min(1).optional().allow("", null),
39464
+ search: Joi33.string().optional().allow("", null),
39465
+ status: Joi33.string().optional().allow("", null),
39466
+ school: Joi33.string().optional().allow("", null),
39467
+ gradeLevel: Joi33.string().optional().allow("", null),
39468
+ section: Joi33.string().optional().allow("", null),
39469
+ schoolYear: Joi33.string().optional().allow("", null)
39470
+ });
39471
+ const { error } = validation.validate(query);
39472
+ const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
39473
+ const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
39474
+ const search = req.query.search ?? "";
39475
+ const status = req.query.status ?? "active";
39476
+ const school = req.query.school ?? "";
39477
+ const gradeLevel = req.query.gradeLevel ?? "";
39478
+ const section = req.query.section ?? "";
39479
+ const schoolYear = req.query.schoolYear ?? "";
39480
+ const isPageNumber = isFinite(page);
39481
+ if (!isPageNumber) {
39482
+ next(new BadRequestError51("Invalid page number."));
39483
+ return;
39484
+ }
39485
+ const isLimitNumber = isFinite(limit);
39486
+ if (!isLimitNumber) {
39487
+ next(new BadRequestError51("Invalid limit number."));
39488
+ return;
39489
+ }
39490
+ if (error) {
39491
+ next(new BadRequestError51(error.message));
39492
+ return;
39493
+ }
39494
+ try {
39495
+ const data = await _getAll({
39496
+ page,
39497
+ limit,
39498
+ search,
39499
+ status,
39500
+ school,
39501
+ gradeLevel,
39502
+ section,
39503
+ schoolYear
39504
+ });
39505
+ res.json(data);
39506
+ return;
39507
+ } catch (error2) {
39508
+ next(error2);
39509
+ }
39510
+ }
39511
+ return {
39512
+ add,
39513
+ getAll
39514
+ };
39515
+ }
39516
+
39517
+ // src/resources/building/building.model.ts
39518
+ import { BadRequestError as BadRequestError52, logger as logger28 } from "@eeplatform/nodejs-utils";
39519
+ import Joi34 from "joi";
39520
+ import { ObjectId as ObjectId31 } from "mongodb";
39521
+ var schemaBuilding = Joi34.object({
39522
+ _id: Joi34.string().hex().optional(),
39523
+ school: Joi34.string().hex().required(),
39524
+ serial: Joi34.string().optional().allow("", null),
39525
+ name: Joi34.string().required(),
39526
+ levels: Joi34.number().integer().min(1).required(),
39527
+ createdAt: Joi34.date().optional().allow("", null),
39528
+ updatedAt: Joi34.date().optional().allow("", null),
39529
+ deletedAt: Joi34.date().optional().allow("", null),
39530
+ status: Joi34.string().optional().allow("", null)
39531
+ });
39532
+ var schemaBuildingUnit = Joi34.object({
39533
+ _id: Joi34.string().hex().optional(),
39534
+ school: Joi34.string().hex().required(),
39535
+ name: Joi34.string().optional().allow("", null),
39536
+ building: Joi34.string().hex().required(),
39537
+ buildingName: Joi34.string().optional().allow("", null),
39538
+ level: Joi34.number().integer().min(1).required(),
39539
+ category: Joi34.string().required(),
39540
+ type: Joi34.string().required(),
39541
+ seating_capacity: Joi34.number().integer().min(0).required(),
39542
+ standing_capacity: Joi34.number().integer().min(0).required(),
39543
+ description: Joi34.string().optional().allow("", null),
39544
+ unit_of_measurement: Joi34.string().valid("sqm").required(),
39545
+ area: Joi34.number().positive().required(),
39546
+ status: Joi34.string().optional().allow("", null)
39547
+ });
39548
+ var schemaUpdateOptions = Joi34.object({
39549
+ name: Joi34.string().optional().allow("", null),
39550
+ building: Joi34.string().hex().optional().allow("", null),
39551
+ buildingName: Joi34.string().optional().allow("", null),
39552
+ level: Joi34.number().integer().min(1).optional().allow("", null),
39553
+ category: Joi34.string().optional().allow("", null),
39554
+ type: Joi34.string().optional().allow("", null),
39555
+ seating_capacity: Joi34.number().integer().min(0).optional().allow("", null),
39556
+ standing_capacity: Joi34.number().integer().min(0).optional().allow("", null),
39557
+ area: Joi34.number().positive().optional().allow("", null)
39558
+ });
39559
+ function MBuilding(value) {
39560
+ const { error } = schemaBuilding.validate(value);
39561
+ if (error) {
39562
+ logger28.info(`Building Model: ${error.message}`);
39563
+ throw new BadRequestError52(error.message);
39564
+ }
39565
+ if (value._id && typeof value._id === "string") {
39566
+ try {
39567
+ value._id = new ObjectId31(value._id);
39568
+ } catch (error2) {
39569
+ throw new BadRequestError52("Invalid _id format");
39570
+ }
39571
+ }
39572
+ try {
39573
+ value.school = new ObjectId31(value.school);
39574
+ } catch (error2) {
39575
+ throw new BadRequestError52("Invalid school format");
39576
+ }
39577
+ return {
39578
+ _id: value._id ?? void 0,
39579
+ school: value.school,
39580
+ serial: value.serial ?? "",
39581
+ name: value.name ?? "",
39582
+ levels: value.levels ?? 0,
39583
+ status: value.status ?? "active",
39584
+ createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
39585
+ updatedAt: value.updatedAt ?? "",
39586
+ deletedAt: value.deletedAt ?? ""
39587
+ };
39588
+ }
39589
+ function MBuildingUnit(value) {
39590
+ const { error } = schemaBuildingUnit.validate(value);
39591
+ if (error) {
39592
+ logger28.info(`Building Unit Model: ${error.message}`);
39593
+ throw new BadRequestError52(error.message);
39594
+ }
39595
+ if (value._id && typeof value._id === "string") {
39596
+ try {
39597
+ value._id = new ObjectId31(value._id);
39598
+ } catch (error2) {
39599
+ throw new BadRequestError52("Invalid ID");
39600
+ }
39601
+ }
39602
+ try {
39603
+ value.school = new ObjectId31(value.school);
39604
+ } catch (error2) {
39605
+ throw new BadRequestError52("Invalid school ID");
39606
+ }
39607
+ try {
39608
+ value.building = new ObjectId31(value.building);
39609
+ } catch (error2) {
39610
+ throw new BadRequestError52("Invalid building ID");
39611
+ }
39612
+ return {
39613
+ _id: value._id ?? void 0,
39614
+ school: value.school,
39615
+ name: value.name ?? "",
39616
+ building: value.building,
39617
+ buildingName: value.buildingName ?? "",
39618
+ level: value.level ?? 0,
39619
+ category: value.category ?? "",
39620
+ type: value.type ?? "",
39621
+ seating_capacity: value.seating_capacity ?? 0,
39622
+ standing_capacity: value.standing_capacity ?? 0,
39623
+ description: value.description ?? "",
39624
+ unit_of_measurement: value.unit_of_measurement ?? "sqm",
39625
+ area: value.area ?? 0,
39626
+ status: value.status ?? "active",
39627
+ createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
39628
+ updatedAt: value.updatedAt ?? "",
39629
+ deletedAt: value.deletedAt ?? ""
39630
+ };
39631
+ }
39632
+
39633
+ // src/resources/building/building.repository.ts
39634
+ import {
39635
+ AppError as AppError19,
39636
+ BadRequestError as BadRequestError53,
39637
+ InternalServerError as InternalServerError17,
39638
+ logger as logger29,
39639
+ makeCacheKey as makeCacheKey17,
39640
+ paginate as paginate16,
39641
+ useAtlas as useAtlas24,
39642
+ useCache as useCache17
39643
+ } from "@eeplatform/nodejs-utils";
39644
+ import { ObjectId as ObjectId32 } from "mongodb";
39645
+ function useBuildingRepo() {
39646
+ const db = useAtlas24.getDb();
39647
+ if (!db) {
39648
+ throw new Error("Unable to connect to server.");
39649
+ }
39650
+ const namespace_collection = "school.buildings";
39651
+ const collection = db.collection(namespace_collection);
39652
+ const { getCache, setCache, delNamespace } = useCache17(namespace_collection);
39653
+ async function createIndexes() {
39654
+ try {
39655
+ await collection.createIndexes([
39656
+ { key: { name: 1 }, unique: true, name: "unique_name_index" },
39657
+ { key: { school: 1 } },
39658
+ { key: { status: 1 } }
39659
+ ]);
39660
+ } catch (error) {
39661
+ throw new Error("Failed to create index on buildings.");
39662
+ }
39663
+ }
39664
+ async function add(value, session) {
39665
+ try {
39666
+ value = MBuilding(value);
39667
+ const res = await collection.insertOne(value, { session });
39668
+ delCachedData();
39669
+ return res.insertedId;
39670
+ } catch (error) {
39671
+ logger29.log({
39672
+ level: "error",
39673
+ message: error.message
39674
+ });
39675
+ if (error instanceof AppError19) {
39676
+ throw error;
39677
+ } else {
39678
+ const isDuplicated = error.message.includes("duplicate");
39679
+ if (isDuplicated) {
39680
+ throw new BadRequestError53("Building already exists.");
39681
+ }
39682
+ throw new Error("Failed to create building.");
39683
+ }
39684
+ }
39685
+ }
39686
+ async function updateById(_id, value, session) {
39687
+ try {
39688
+ _id = new ObjectId32(_id);
39689
+ } catch (error) {
39690
+ throw new BadRequestError53("Invalid ID.");
39691
+ }
39692
+ try {
39693
+ const res = await collection.updateOne(
39694
+ { _id },
39695
+ { $set: value },
39696
+ { session }
39697
+ );
39698
+ delCachedData();
39699
+ return res;
39700
+ } catch (error) {
39701
+ logger29.log({
39702
+ level: "error",
39703
+ message: error.message
39704
+ });
39705
+ if (error instanceof AppError19) {
39706
+ throw error;
39707
+ } else {
39708
+ throw new Error("Failed to update building.");
39709
+ }
39710
+ }
39711
+ }
39712
+ async function getAll({
39713
+ search = "",
39714
+ page = 1,
39715
+ limit = 10,
39716
+ sort = {},
39717
+ school = "",
39718
+ status = "active"
39719
+ } = {}) {
39720
+ page = page > 0 ? page - 1 : 0;
39721
+ const query = {
39722
+ status
39723
+ };
39724
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
39725
+ if (search) {
39726
+ query.$text = { $search: search };
39727
+ }
39728
+ if (school) {
39729
+ try {
39730
+ query.school = new ObjectId32(school);
39731
+ } catch (error) {
39732
+ throw new BadRequestError53("Invalid school ID.");
39733
+ }
39734
+ }
39735
+ const cacheParams = {
39736
+ page,
39737
+ limit,
39738
+ sort: JSON.stringify(sort)
39739
+ };
39740
+ if (search)
39741
+ cacheParams.search = search;
39742
+ if (school)
39743
+ cacheParams.school = school;
39744
+ if (status !== "active")
39745
+ cacheParams.status = status;
39746
+ const cacheKey = makeCacheKey17(namespace_collection, cacheParams);
39747
+ logger29.log({
39748
+ level: "info",
39749
+ message: `Cache key for getAll buildings: ${cacheKey}`
39750
+ });
39751
+ try {
39752
+ const cached = await getCache(cacheKey);
39753
+ if (cached) {
39754
+ logger29.log({
39755
+ level: "info",
39756
+ message: `Cache hit for getAll buildings: ${cacheKey}`
39757
+ });
39758
+ return cached;
39759
+ }
39760
+ const items = await collection.aggregate([
39761
+ { $match: query },
39762
+ { $sort: sort },
39763
+ { $skip: page * limit },
39764
+ { $limit: limit }
39765
+ ]).toArray();
39766
+ const length = await collection.countDocuments(query);
39767
+ const data = paginate16(items, page, limit, length);
39768
+ setCache(cacheKey, data, 600).then(() => {
39769
+ logger29.log({
39770
+ level: "info",
39771
+ message: `Cache set for getAll buildings: ${cacheKey}`
39772
+ });
39773
+ }).catch((err) => {
39774
+ logger29.log({
39775
+ level: "error",
39776
+ message: `Failed to set cache for getAll buildings: ${err.message}`
39777
+ });
39778
+ });
39779
+ return data;
39780
+ } catch (error) {
39781
+ logger29.log({ level: "error", message: `${error}` });
39782
+ throw error;
39783
+ }
39784
+ }
39785
+ async function getById(_id) {
39786
+ try {
39787
+ _id = new ObjectId32(_id);
39788
+ } catch (error) {
39789
+ throw new BadRequestError53("Invalid ID.");
39790
+ }
39791
+ const cacheKey = makeCacheKey17(namespace_collection, { _id: String(_id) });
39792
+ try {
39793
+ const cached = await getCache(cacheKey);
39794
+ if (cached) {
39795
+ logger29.log({
39796
+ level: "info",
39797
+ message: `Cache hit for getById building: ${cacheKey}`
39798
+ });
39799
+ return cached;
39800
+ }
39801
+ const result = await collection.findOne({
39802
+ _id
39803
+ });
39804
+ setCache(cacheKey, result, 300).then(() => {
39805
+ logger29.log({
39806
+ level: "info",
39807
+ message: `Cache set for building by id: ${cacheKey}`
39808
+ });
39809
+ }).catch((err) => {
39810
+ logger29.log({
39811
+ level: "error",
39812
+ message: `Failed to set cache for building by id: ${err.message}`
39813
+ });
39814
+ });
39815
+ return result;
39816
+ } catch (error) {
39817
+ if (error instanceof AppError19) {
39818
+ throw error;
39819
+ } else {
39820
+ throw new InternalServerError17("Failed to get building.");
39821
+ }
39822
+ }
39823
+ }
39824
+ async function deleteById(_id, session) {
39825
+ try {
39826
+ _id = new ObjectId32(_id);
39827
+ } catch (error) {
39828
+ throw new BadRequestError53("Invalid ID.");
39829
+ }
39830
+ try {
39831
+ const res = await collection.updateOne(
39832
+ { _id },
39833
+ { $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } }
39834
+ );
39835
+ delCachedData();
39836
+ return res;
39837
+ } catch (error) {
39838
+ logger29.log({
39839
+ level: "error",
39840
+ message: error.message
39841
+ });
39842
+ if (error instanceof AppError19) {
39843
+ throw error;
39844
+ } else {
39845
+ throw new InternalServerError17("Failed to delete building.");
39846
+ }
39847
+ }
39848
+ }
39849
+ function delCachedData() {
39850
+ delNamespace().then(() => {
39851
+ logger29.log({
39852
+ level: "info",
39853
+ message: `Cache namespace cleared for ${namespace_collection}`
39854
+ });
39855
+ }).catch((err) => {
39856
+ logger29.log({
39857
+ level: "error",
39858
+ message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
39859
+ });
39860
+ });
39861
+ }
39862
+ return {
39863
+ createIndexes,
39864
+ add,
39865
+ getAll,
39866
+ getById,
39867
+ updateById,
39868
+ deleteById
39869
+ };
39870
+ }
39871
+
39872
+ // src/resources/building/building.service.ts
39873
+ import {
39874
+ BadRequestError as BadRequestError55,
39875
+ NotFoundError as NotFoundError3,
39876
+ useAtlas as useAtlas26
39877
+ } from "@eeplatform/nodejs-utils";
39878
+
39879
+ // src/resources/building/building-unit.repository.ts
39880
+ import {
39881
+ AppError as AppError20,
39882
+ BadRequestError as BadRequestError54,
39883
+ InternalServerError as InternalServerError18,
39884
+ logger as logger30,
39885
+ makeCacheKey as makeCacheKey18,
39886
+ paginate as paginate17,
39887
+ useAtlas as useAtlas25,
39888
+ useCache as useCache18
39889
+ } from "@eeplatform/nodejs-utils";
39890
+ import { ObjectId as ObjectId33 } from "mongodb";
39891
+ function useBuildingUnitRepo() {
39892
+ const db = useAtlas25.getDb();
39893
+ if (!db) {
39894
+ throw new Error("Unable to connect to server.");
39895
+ }
39896
+ const namespace_collection = "school.building-units";
39897
+ const collection = db.collection(namespace_collection);
39898
+ const { getCache, setCache, delNamespace } = useCache18(namespace_collection);
39899
+ async function createIndexes() {
39900
+ try {
39901
+ await collection.createIndexes([
39902
+ {
39903
+ key: { name: 1, building: 1, level: 1 },
39904
+ unique: true,
39905
+ name: "unique_name_index"
39906
+ },
39907
+ { key: { school: 1 } },
39908
+ { key: { building: 1 } },
39909
+ { key: { status: 1 } },
39910
+ { key: { createdAt: 1 } },
39911
+ {
39912
+ key: {
39913
+ name: "text",
39914
+ buildingName: "text",
39915
+ category: "text",
39916
+ type: "text"
39917
+ }
39918
+ }
39919
+ ]);
39920
+ } catch (error) {
39921
+ throw new Error("Failed to create index on building units.");
39922
+ }
39923
+ }
39924
+ function delCachedData() {
39925
+ delNamespace().then(() => {
39926
+ logger30.log({
39927
+ level: "info",
39928
+ message: `Cache namespace cleared for ${namespace_collection}`
39929
+ });
39930
+ }).catch((err) => {
39931
+ logger30.log({
39932
+ level: "error",
39933
+ message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
39934
+ });
39935
+ });
39936
+ }
39937
+ async function add(value, session) {
39938
+ try {
39939
+ value = MBuildingUnit(value);
39940
+ const res = await collection.insertOne(value, { session });
39941
+ delCachedData();
39942
+ return res.insertedId;
39943
+ } catch (error) {
39944
+ logger30.log({
39945
+ level: "error",
39946
+ message: error.message
39947
+ });
39948
+ if (error instanceof AppError20) {
39949
+ throw error;
39950
+ } else {
39951
+ throw new Error("Failed to create building unit.");
39952
+ }
39953
+ }
39954
+ }
39955
+ async function updateById(_id, value, session) {
39956
+ const { error } = schemaUpdateOptions.validate(value);
39957
+ if (error) {
39958
+ throw new BadRequestError54(error.message);
39959
+ }
39960
+ try {
39961
+ _id = new ObjectId33(_id);
39962
+ } catch (error2) {
39963
+ throw new BadRequestError54("Invalid ID.");
39964
+ }
39965
+ try {
39966
+ const res = await collection.updateOne(
39967
+ { _id },
39968
+ { $set: value },
39969
+ { session }
39970
+ );
39971
+ delCachedData();
39972
+ return res;
39973
+ } catch (error2) {
39974
+ logger30.log({
39975
+ level: "error",
39976
+ message: error2.message
39977
+ });
39978
+ if (error2 instanceof AppError20) {
39979
+ throw error2;
39980
+ } else {
39981
+ throw new Error("Failed to create building unit.");
39982
+ }
39983
+ }
39984
+ }
39985
+ async function updateByBuildingId(building, value, session) {
39986
+ const { error } = schemaUpdateOptions.validate(value);
39987
+ if (error) {
39988
+ throw new BadRequestError54(error.message);
39989
+ }
39990
+ try {
39991
+ building = new ObjectId33(building);
39992
+ } catch (error2) {
39993
+ throw new BadRequestError54("Invalid building ID.");
39994
+ }
39995
+ try {
39996
+ const res = await collection.updateMany(
39997
+ { building },
39998
+ { $set: value },
39999
+ { session }
40000
+ );
40001
+ delCachedData();
40002
+ return res;
40003
+ } catch (error2) {
40004
+ logger30.log({
40005
+ level: "error",
40006
+ message: error2.message
40007
+ });
40008
+ if (error2 instanceof AppError20) {
40009
+ throw error2;
40010
+ } else {
40011
+ throw new Error("Failed to update building unit.");
40012
+ }
40013
+ }
40014
+ }
40015
+ async function getAll({
40016
+ search = "",
40017
+ page = 1,
40018
+ limit = 10,
40019
+ sort = {},
40020
+ school = "",
40021
+ building = "",
40022
+ status = "active"
40023
+ } = {}) {
40024
+ page = page > 0 ? page - 1 : 0;
40025
+ const query = {
40026
+ deletedAt: { $in: ["", null] },
40027
+ status: { $in: [status, "", null] }
40028
+ };
40029
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
40030
+ if (search) {
40031
+ query.$text = { $search: search };
40032
+ }
40033
+ if (school) {
40034
+ try {
40035
+ query.school = new ObjectId33(school);
40036
+ } catch (error) {
40037
+ throw new BadRequestError54("Invalid school ID.");
40038
+ }
40039
+ }
40040
+ if (building) {
40041
+ try {
40042
+ query.building = new ObjectId33(building);
40043
+ } catch (error) {
40044
+ throw new BadRequestError54("Invalid building ID.");
40045
+ }
40046
+ }
40047
+ const cacheParams = {
40048
+ page,
40049
+ limit,
40050
+ sort: JSON.stringify(sort)
40051
+ };
40052
+ if (search)
40053
+ cacheParams.search = search;
40054
+ if (school)
40055
+ cacheParams.school = school;
40056
+ if (building)
40057
+ cacheParams.building = building;
40058
+ if (status !== "active")
40059
+ cacheParams.status = status;
40060
+ const cacheKey = makeCacheKey18(namespace_collection, cacheParams);
40061
+ logger30.log({
40062
+ level: "info",
40063
+ message: `Cache key for getAll building units: ${cacheKey}`
40064
+ });
40065
+ try {
40066
+ const cached = await getCache(cacheKey);
40067
+ if (cached) {
40068
+ logger30.log({
40069
+ level: "info",
40070
+ message: `Cache hit for getAll building units: ${cacheKey}`
40071
+ });
40072
+ return cached;
40073
+ }
40074
+ const items = await collection.aggregate([
40075
+ { $match: query },
40076
+ { $sort: sort },
40077
+ { $skip: page * limit },
40078
+ { $limit: limit }
40079
+ ]).toArray();
40080
+ const length = await collection.countDocuments(query);
40081
+ const data = paginate17(items, page, limit, length);
40082
+ setCache(cacheKey, data, 600).then(() => {
40083
+ logger30.log({
40084
+ level: "info",
40085
+ message: `Cache set for getAll building units: ${cacheKey}`
40086
+ });
40087
+ }).catch((err) => {
40088
+ logger30.log({
40089
+ level: "error",
40090
+ message: `Failed to set cache for getAll building units: ${err.message}`
40091
+ });
40092
+ });
40093
+ return data;
40094
+ } catch (error) {
40095
+ logger30.log({ level: "error", message: `${error}` });
40096
+ throw error;
40097
+ }
40098
+ }
40099
+ async function getById(_id) {
40100
+ try {
40101
+ _id = new ObjectId33(_id);
40102
+ } catch (error) {
40103
+ throw new BadRequestError54("Invalid ID.");
40104
+ }
40105
+ const cacheKey = makeCacheKey18(namespace_collection, { _id: String(_id) });
40106
+ try {
40107
+ const cached = await getCache(cacheKey);
40108
+ if (cached) {
40109
+ logger30.log({
40110
+ level: "info",
40111
+ message: `Cache hit for getById building unit: ${cacheKey}`
40112
+ });
40113
+ return cached;
40114
+ }
40115
+ const result = await collection.findOne({
40116
+ _id,
40117
+ deletedAt: { $in: ["", null] }
40118
+ });
40119
+ if (!result) {
40120
+ throw new BadRequestError54("Building unit not found.");
40121
+ }
40122
+ setCache(cacheKey, result, 300).then(() => {
40123
+ logger30.log({
40124
+ level: "info",
40125
+ message: `Cache set for building unit by id: ${cacheKey}`
40126
+ });
40127
+ }).catch((err) => {
40128
+ logger30.log({
40129
+ level: "error",
40130
+ message: `Failed to set cache for building unit by id: ${err.message}`
40131
+ });
40132
+ });
40133
+ return result;
40134
+ } catch (error) {
40135
+ if (error instanceof AppError20) {
40136
+ throw error;
40137
+ } else {
40138
+ throw new InternalServerError18("Failed to get building unit.");
40139
+ }
40140
+ }
40141
+ }
40142
+ async function getByBuildingLevel(building, level) {
40143
+ try {
40144
+ building = new ObjectId33(building);
40145
+ } catch (error) {
40146
+ throw new BadRequestError54("Invalid building ID.");
40147
+ }
40148
+ const cacheKey = makeCacheKey18(namespace_collection, {
40149
+ building: String(building),
40150
+ level
40151
+ });
40152
+ try {
40153
+ const cached = await getCache(cacheKey);
40154
+ if (cached) {
40155
+ logger30.log({
40156
+ level: "info",
40157
+ message: `Cache hit for getById building unit: ${cacheKey}`
40158
+ });
40159
+ return cached;
40160
+ }
40161
+ const result = await collection.findOne({
40162
+ building,
40163
+ level,
40164
+ status: "active"
40165
+ });
40166
+ setCache(cacheKey, result, 300).then(() => {
40167
+ logger30.log({
40168
+ level: "info",
40169
+ message: `Cache set for building unit by id: ${cacheKey}`
40170
+ });
40171
+ }).catch((err) => {
40172
+ logger30.log({
40173
+ level: "error",
40174
+ message: `Failed to set cache for building unit by id: ${err.message}`
40175
+ });
40176
+ });
40177
+ return result;
40178
+ } catch (error) {
40179
+ if (error instanceof AppError20) {
40180
+ throw error;
40181
+ } else {
40182
+ throw new InternalServerError18("Failed to get building unit.");
40183
+ }
40184
+ }
40185
+ }
40186
+ async function getByBuilding(building) {
40187
+ try {
40188
+ building = new ObjectId33(building);
40189
+ } catch (error) {
40190
+ throw new BadRequestError54("Invalid building ID.");
40191
+ }
40192
+ const cacheKey = makeCacheKey18(namespace_collection, {
40193
+ building: String(building)
40194
+ });
40195
+ try {
40196
+ const cached = await getCache(cacheKey);
40197
+ if (cached) {
40198
+ logger30.log({
40199
+ level: "info",
40200
+ message: `Cache hit for getById building unit: ${cacheKey}`
40201
+ });
40202
+ return cached;
40203
+ }
40204
+ const result = await collection.findOne({
40205
+ building,
40206
+ status: "active"
40207
+ });
40208
+ setCache(cacheKey, result, 300).then(() => {
40209
+ logger30.log({
40210
+ level: "info",
40211
+ message: `Cache set for building unit by id: ${cacheKey}`
40212
+ });
40213
+ }).catch((err) => {
40214
+ logger30.log({
40215
+ level: "error",
40216
+ message: `Failed to set cache for building unit by id: ${err.message}`
40217
+ });
40218
+ });
40219
+ return result;
40220
+ } catch (error) {
40221
+ if (error instanceof AppError20) {
40222
+ throw error;
40223
+ } else {
40224
+ throw new InternalServerError18("Failed to get building unit.");
40225
+ }
40226
+ }
40227
+ }
40228
+ async function deleteById(_id, session) {
40229
+ try {
40230
+ _id = new ObjectId33(_id);
40231
+ } catch (error) {
40232
+ throw new BadRequestError54("Invalid ID.");
40233
+ }
40234
+ try {
40235
+ const res = await collection.updateOne(
40236
+ { _id },
40237
+ { $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } },
40238
+ { session }
40239
+ );
40240
+ delCachedData();
40241
+ return "Room/Facility deleted successfully.";
40242
+ } catch (error) {
40243
+ logger30.log({
40244
+ level: "error",
40245
+ message: error.message
40246
+ });
40247
+ if (error instanceof AppError20) {
40248
+ throw error;
40249
+ } else {
40250
+ throw new Error("Failed to deleted room/facility.");
40251
+ }
40252
+ }
40253
+ }
40254
+ return {
40255
+ createIndexes,
40256
+ add,
40257
+ getAll,
40258
+ getById,
40259
+ getByBuildingLevel,
40260
+ updateById,
40261
+ getByBuilding,
40262
+ deleteById,
40263
+ updateByBuildingId
40264
+ };
40265
+ }
40266
+
40267
+ // src/resources/building/building.service.ts
40268
+ function useBuildingService() {
40269
+ const {
40270
+ updateById: _updateById,
40271
+ getById: _getById,
40272
+ deleteById: _deleteById
40273
+ } = useBuildingRepo();
40274
+ const { getByBuildingLevel, getByBuilding, updateByBuildingId } = useBuildingUnitRepo();
40275
+ async function updateById(id, data) {
40276
+ data.levels = Number(data.levels);
40277
+ const session = useAtlas26.getClient()?.startSession();
40278
+ try {
40279
+ const building = await _getById(id);
40280
+ if (!building) {
40281
+ throw new NotFoundError3("Building not found.");
40282
+ }
40283
+ if (data.levels < building.levels) {
40284
+ const unit = await getByBuildingLevel(id, building.levels);
40285
+ if (unit) {
40286
+ throw new BadRequestError55(
40287
+ "Cannot reduce floors, there are existing building units at higher floors."
40288
+ );
40289
+ }
40290
+ }
40291
+ session?.startTransaction();
40292
+ if (building.name !== data.name) {
40293
+ await updateByBuildingId(id, { buildingName: data.name }, session);
40294
+ }
40295
+ const result = await _updateById(id, data, session);
40296
+ await session?.commitTransaction();
40297
+ return result;
40298
+ } catch (error) {
40299
+ await session?.abortTransaction();
40300
+ throw error;
40301
+ } finally {
40302
+ session?.endSession();
40303
+ }
40304
+ }
40305
+ async function deleteById(id) {
40306
+ const building = await getByBuilding(id);
40307
+ if (building) {
40308
+ throw new BadRequestError55(
40309
+ "Cannot delete building with existing room/facility. Please delete room/facility first."
40310
+ );
40311
+ }
40312
+ try {
40313
+ await _deleteById(id);
40314
+ return "Building deleted successfully.";
40315
+ } catch (error) {
40316
+ throw error;
40317
+ }
40318
+ }
40319
+ return {
40320
+ updateById,
40321
+ deleteById
40322
+ };
40323
+ }
40324
+
40325
+ // src/resources/building/building.controller.ts
40326
+ import { BadRequestError as BadRequestError56, logger as logger31 } from "@eeplatform/nodejs-utils";
40327
+ import Joi35 from "joi";
40328
+ function useBuildingController() {
40329
+ const { getAll: _getAll, getById: _getById, add: _add } = useBuildingRepo();
40330
+ const { updateById: _updateById, deleteById: _deleteById } = useBuildingService();
40331
+ async function createBuilding(req, res, next) {
40332
+ const value = req.body;
40333
+ const validation = Joi35.object({
40334
+ name: Joi35.string().required(),
40335
+ school: Joi35.string().hex().required(),
40336
+ levels: Joi35.number().integer().min(1).required(),
40337
+ serial: Joi35.string().optional().allow("", null),
40338
+ status: Joi35.string().optional().allow("", null)
40339
+ });
40340
+ const { error } = validation.validate(value);
40341
+ if (error) {
40342
+ next(new BadRequestError56(error.message));
40343
+ logger31.info(`Controller: ${error.message}`);
40344
+ return;
40345
+ }
40346
+ try {
40347
+ const result = await _add(value);
40348
+ res.json(result);
40349
+ return;
40350
+ } catch (error2) {
40351
+ next(error2);
40352
+ }
40353
+ }
40354
+ async function updateById(req, res, next) {
40355
+ const value = req.body;
40356
+ const id = req.params.id ?? "";
40357
+ const validation = Joi35.object({
40358
+ id: Joi35.string().hex().required(),
40359
+ value: Joi35.object({
40360
+ name: Joi35.string().required(),
40361
+ serial: Joi35.string().optional().allow("", null),
40362
+ levels: Joi35.number().integer().min(1).required()
40363
+ })
40364
+ });
40365
+ const { error } = validation.validate({ id, value });
40366
+ if (error) {
40367
+ next(new BadRequestError56(error.message));
40368
+ logger31.info(`Controller: ${error.message}`);
40369
+ return;
40370
+ }
40371
+ try {
40372
+ const result = await _updateById(id, value);
40373
+ res.json(result);
40374
+ return;
40375
+ } catch (error2) {
40376
+ next(error2);
40377
+ }
40378
+ }
40379
+ async function getAll(req, res, next) {
40380
+ const query = req.query;
40381
+ const validation = Joi35.object({
40382
+ page: Joi35.number().min(1).optional().allow("", null),
40383
+ limit: Joi35.number().min(1).optional().allow("", null),
40384
+ search: Joi35.string().optional().allow("", null),
40385
+ school: Joi35.string().hex().optional().allow("", null),
40386
+ status: Joi35.string().optional().allow("", null)
40387
+ });
40388
+ const { error } = validation.validate(query);
40389
+ if (error) {
40390
+ next(new BadRequestError56(error.message));
40391
+ return;
40392
+ }
40393
+ const page = parseInt(req.query.page) ?? 1;
40394
+ let limit = parseInt(req.query.limit) ?? 20;
40395
+ limit = isNaN(limit) ? 20 : limit;
40396
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
40397
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
40398
+ const sortObj = {};
40399
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
40400
+ sort.forEach((field, index) => {
40401
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
40402
+ });
40403
+ }
40404
+ const status = req.query.status ?? "active";
40405
+ const school = req.query.school ?? "";
40406
+ const search = req.query.search ?? "";
40407
+ try {
40408
+ const buildings = await _getAll({
40409
+ page,
40410
+ limit,
40411
+ sort: sortObj,
40412
+ status,
40413
+ school,
40414
+ search
40415
+ });
40416
+ res.json(buildings);
40417
+ return;
40418
+ } catch (error2) {
40419
+ next(error2);
40420
+ }
40421
+ }
40422
+ async function getById(req, res, next) {
40423
+ const id = req.params.id;
40424
+ const validation = Joi35.object({
40425
+ id: Joi35.string().hex().required()
40426
+ });
40427
+ const { error } = validation.validate({ id });
40428
+ if (error) {
40429
+ next(new BadRequestError56(error.message));
40430
+ return;
40431
+ }
40432
+ try {
40433
+ const building = await _getById(id);
40434
+ res.json({
40435
+ message: "Successfully retrieved building.",
40436
+ data: { building }
40437
+ });
40438
+ return;
40439
+ } catch (error2) {
40440
+ next(error2);
40441
+ }
40442
+ }
40443
+ async function deleteById(req, res, next) {
40444
+ const id = req.params.id;
40445
+ const validation = Joi35.object({
40446
+ id: Joi35.string().hex().required()
40447
+ });
40448
+ const { error } = validation.validate({ id });
40449
+ if (error) {
40450
+ next(new BadRequestError56(error.message));
40451
+ return;
40452
+ }
40453
+ try {
40454
+ const message = await _deleteById(id);
40455
+ res.json(message);
40456
+ return;
40457
+ } catch (error2) {
40458
+ next(error2);
40459
+ }
40460
+ }
40461
+ return {
40462
+ createBuilding,
40463
+ getAll,
40464
+ getById,
40465
+ updateById,
40466
+ deleteById
40467
+ };
40468
+ }
40469
+
40470
+ // src/resources/building/building-unit.service.ts
40471
+ import { useAtlas as useAtlas27 } from "@eeplatform/nodejs-utils";
40472
+ function useBuildingUnitService() {
40473
+ const { add: _add } = useBuildingUnitRepo();
40474
+ async function add(value) {
40475
+ const session = useAtlas27.getClient()?.startSession();
40476
+ if (!session) {
40477
+ throw new Error("Unable to start session for building unit service.");
40478
+ }
40479
+ try {
40480
+ await session.startTransaction();
40481
+ for (let index = 0; index < value.qty; index++) {
40482
+ await _add(
40483
+ { ...value.building, name: `${value.building.name} ${index + 1}` },
40484
+ session
40485
+ );
40486
+ }
40487
+ await session.commitTransaction();
40488
+ return "Building unit added successfully.";
40489
+ } catch (error) {
40490
+ await session.abortTransaction();
40491
+ throw error;
40492
+ } finally {
40493
+ session.endSession();
40494
+ }
40495
+ }
40496
+ return {
40497
+ add
40498
+ };
40499
+ }
40500
+
40501
+ // src/resources/building/building-unit.controller.ts
40502
+ import { BadRequestError as BadRequestError57 } from "@eeplatform/nodejs-utils";
40503
+ import Joi36 from "joi";
40504
+ function useBuildingUnitController() {
40505
+ const {
40506
+ getAll: _getAll,
40507
+ getById: _getById,
40508
+ updateById: _updateById,
40509
+ deleteById: _deleteById
40510
+ } = useBuildingUnitRepo();
40511
+ const { add: _add } = useBuildingUnitService();
40512
+ async function add(req, res, next) {
40513
+ const data = req.body;
40514
+ const validation = Joi36.object({
40515
+ building: Joi36.object({
40516
+ school: Joi36.string().hex().required(),
40517
+ name: Joi36.string().optional().allow("", null),
40518
+ building: Joi36.string().hex().required(),
40519
+ buildingName: Joi36.string().optional().allow("", null),
40520
+ level: Joi36.number().integer().min(1).required(),
40521
+ category: Joi36.string().required(),
40522
+ type: Joi36.string().required(),
40523
+ seating_capacity: Joi36.number().integer().min(0).required(),
40524
+ standing_capacity: Joi36.number().integer().min(0).required(),
40525
+ description: Joi36.string().optional().allow("", null),
40526
+ unit_of_measurement: Joi36.string().valid("sqm").required(),
40527
+ area: Joi36.number().positive().required(),
40528
+ status: Joi36.string().optional().allow("", null)
40529
+ }),
40530
+ qty: Joi36.number().integer().min(1).max(20).optional().default(1)
40531
+ });
40532
+ const { error } = validation.validate(data);
40533
+ if (error) {
40534
+ next(new BadRequestError57(error.message));
40535
+ return;
40536
+ }
40537
+ try {
40538
+ const buildingUnit = await _add(data);
40539
+ res.json({
40540
+ message: "Building unit added successfully.",
40541
+ data: { buildingUnit }
40542
+ });
40543
+ } catch (error2) {
40544
+ next(error2);
40545
+ }
40546
+ }
40547
+ async function updateById(req, res, next) {
40548
+ const data = req.body;
40549
+ const id = req.params.id ?? "";
40550
+ const validation = Joi36.object({
40551
+ id: Joi36.string().hex().required(),
40552
+ value: schemaUpdateOptions
40553
+ });
40554
+ const { error } = validation.validate({ id, value: data });
40555
+ if (error) {
40556
+ next(new BadRequestError57(error.message));
40557
+ return;
40558
+ }
40559
+ try {
40560
+ const buildingUnit = await _updateById(id, data);
40561
+ res.json({
40562
+ message: "Building unit updated successfully.",
40563
+ data: { buildingUnit }
40564
+ });
40565
+ } catch (error2) {
40566
+ next(error2);
40567
+ }
40568
+ }
40569
+ async function getAll(req, res, next) {
40570
+ const query = req.query;
40571
+ const validation = Joi36.object({
40572
+ page: Joi36.number().min(1).optional().allow("", null),
40573
+ limit: Joi36.number().min(1).optional().allow("", null),
40574
+ search: Joi36.string().optional().allow("", null),
40575
+ school: Joi36.string().hex().optional().allow("", null),
40576
+ building: Joi36.string().hex().optional().allow("", null),
40577
+ status: Joi36.string().optional().allow("", null)
40578
+ });
40579
+ const { error } = validation.validate(query);
40580
+ if (error) {
40581
+ next(new BadRequestError57(error.message));
40582
+ return;
40583
+ }
40584
+ const page = parseInt(req.query.page) ?? 1;
40585
+ let limit = parseInt(req.query.limit) ?? 20;
40586
+ limit = isNaN(limit) ? 20 : limit;
40587
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
40588
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
40589
+ const sortObj = {};
40590
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
40591
+ sort.forEach((field, index) => {
40592
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
40593
+ });
40594
+ }
40595
+ const status = req.query.status ?? "active";
40596
+ const school = req.query.school ?? "";
40597
+ const building = req.query.building ?? "";
40598
+ const search = req.query.search ?? "";
40599
+ try {
40600
+ const buildings = await _getAll({
40601
+ page,
40602
+ limit,
40603
+ sort: sortObj,
40604
+ status,
40605
+ school,
40606
+ search,
40607
+ building
40608
+ });
40609
+ res.json(buildings);
40610
+ return;
40611
+ } catch (error2) {
40612
+ next(error2);
40613
+ }
40614
+ }
40615
+ async function getById(req, res, next) {
40616
+ const id = req.params.id;
40617
+ const validation = Joi36.object({
40618
+ id: Joi36.string().hex().required()
40619
+ });
40620
+ const { error } = validation.validate({ id });
40621
+ if (error) {
40622
+ next(new BadRequestError57(error.message));
40623
+ return;
40624
+ }
40625
+ try {
40626
+ const buildingUnit = await _getById(id);
40627
+ res.json({
40628
+ message: "Successfully retrieved building unit.",
40629
+ data: { buildingUnit }
40630
+ });
40631
+ return;
40632
+ } catch (error2) {
40633
+ next(error2);
40634
+ }
40635
+ }
40636
+ async function deleteById(req, res, next) {
40637
+ const id = req.params.id;
40638
+ const validation = Joi36.object({
40639
+ id: Joi36.string().hex().required()
40640
+ });
40641
+ const { error } = validation.validate({ id });
40642
+ if (error) {
40643
+ next(new BadRequestError57(error.message));
40644
+ return;
40645
+ }
40646
+ try {
40647
+ const message = await _deleteById(id);
40648
+ res.json({ message });
40649
+ return;
40650
+ } catch (error2) {
40651
+ next(error2);
40652
+ }
40653
+ }
40654
+ return {
40655
+ add,
40656
+ getAll,
40657
+ getById,
40658
+ updateById,
40659
+ deleteById
40660
+ };
40661
+ }
40662
+
40663
+ // src/resources/personnel/personnel.model.ts
40664
+ import { BadRequestError as BadRequestError58, logger as logger32 } from "@eeplatform/nodejs-utils";
40665
+ import Joi37 from "joi";
40666
+ import { ObjectId as ObjectId34 } from "mongodb";
40667
+ var schemaPersonnel = Joi37.object({
40668
+ _id: Joi37.string().hex().optional().allow("", null),
40669
+ school: Joi37.string().hex().required(),
40670
+ schoolName: Joi37.string().optional().allow("", null),
40671
+ firstName: Joi37.string().required(),
40672
+ lastName: Joi37.string().required(),
40673
+ middleName: Joi37.string().optional().allow("", null),
40674
+ suffix: Joi37.string().optional().allow("", null),
40675
+ title: Joi37.string().optional().allow("", null),
40676
+ classification: Joi37.string().required(),
40677
+ status: Joi37.string().optional().allow("", null),
40678
+ createdAt: Joi37.date().optional().allow("", null),
40679
+ updatedAt: Joi37.date().optional().allow("", null),
40680
+ deletedAt: Joi37.date().optional().allow("", null)
40681
+ });
40682
+ function MPersonnel(value) {
40683
+ const { error } = schemaPersonnel.validate(value);
40684
+ if (error) {
40685
+ logger32.info(`Personnel Model: ${error.message}`);
40686
+ throw new BadRequestError58(error.message);
40687
+ }
40688
+ if (value._id && typeof value._id === "string") {
40689
+ try {
40690
+ value._id = new ObjectId34(value._id);
40691
+ } catch (error2) {
40692
+ throw new BadRequestError58("Invalid _id format");
40693
+ }
40694
+ }
40695
+ if (value.school && typeof value.school === "string") {
40696
+ try {
40697
+ value.school = new ObjectId34(value.school);
40698
+ } catch (error2) {
40699
+ throw new BadRequestError58("Invalid school format");
40700
+ }
40701
+ }
40702
+ if (!value.status) {
40703
+ value.status = "active";
40704
+ }
40705
+ return {
40706
+ _id: value._id,
40707
+ school: value.school,
40708
+ schoolName: value.schoolName ?? "",
40709
+ firstName: value.firstName ?? "",
40710
+ lastName: value.lastName ?? "",
40711
+ middleName: value.middleName ?? "",
40712
+ suffix: value.suffix ?? "",
40713
+ title: value.title ?? "",
40714
+ classification: value.classification ?? "",
40715
+ status: value.status,
40716
+ createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
40717
+ updatedAt: value.updatedAt ?? "",
40718
+ deletedAt: value.deletedAt ?? ""
40719
+ };
40720
+ }
40721
+
40722
+ // src/resources/personnel/personnel.repository.ts
40723
+ import {
40724
+ AppError as AppError21,
40725
+ BadRequestError as BadRequestError59,
40726
+ InternalServerError as InternalServerError19,
40727
+ logger as logger33,
40728
+ makeCacheKey as makeCacheKey19,
40729
+ paginate as paginate18,
40730
+ useAtlas as useAtlas28,
40731
+ useCache as useCache19
40732
+ } from "@eeplatform/nodejs-utils";
40733
+ import { ObjectId as ObjectId35 } from "mongodb";
40734
+ function usePersonnelRepo() {
40735
+ const db = useAtlas28.getDb();
40736
+ if (!db) {
40737
+ throw new Error("Unable to connect to server.");
40738
+ }
40739
+ const namespace_collection = "deped.school.personnel";
40740
+ const collection = db.collection(namespace_collection);
40741
+ const { getCache, setCache, delNamespace } = useCache19(namespace_collection);
40742
+ async function createIndexes() {
40743
+ try {
40744
+ await collection.createIndexes([
40745
+ {
40746
+ key: {
40747
+ firstName: 1,
40748
+ lastName: 1,
40749
+ classification: 1,
40750
+ status: 1
40751
+ },
40752
+ name: "personnel_search_index"
40753
+ },
40754
+ { key: { status: 1 } },
40755
+ { key: { classification: 1 } },
40756
+ { key: { firstName: "text", lastName: "text", middleName: "text" } }
40757
+ ]);
40758
+ } catch (error) {
40759
+ throw new Error("Failed to create index on personnel.");
40760
+ }
40761
+ }
40762
+ async function add(value, session) {
40763
+ try {
40764
+ value = MPersonnel(value);
40765
+ await collection.insertOne(value, { session });
40766
+ delCachedData();
40767
+ return "Successfully added personnel.";
40768
+ } catch (error) {
40769
+ logger33.log({
40770
+ level: "error",
40771
+ message: error.message
40772
+ });
40773
+ if (error instanceof AppError21) {
40774
+ throw error;
40775
+ } else {
40776
+ throw new Error("Failed to create personnel.");
40777
+ }
40778
+ }
40779
+ }
40780
+ async function updateById(_id, value, session) {
40781
+ try {
40782
+ _id = new ObjectId35(_id);
40783
+ } catch (error) {
40784
+ throw new BadRequestError59("Invalid ID.");
40785
+ }
40786
+ try {
40787
+ const res = await collection.updateOne(
40788
+ { _id },
40789
+ { $set: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
40790
+ { session }
40791
+ );
40792
+ delCachedData();
40793
+ return res;
40794
+ } catch (error) {
40795
+ logger33.log({
40796
+ level: "error",
40797
+ message: error.message
40798
+ });
40799
+ if (error instanceof AppError21) {
40800
+ throw error;
40801
+ } else {
40802
+ throw new Error("Failed to update personnel.");
40803
+ }
40804
+ }
40805
+ }
40806
+ async function getAll({
40807
+ school = "",
40808
+ search = "",
40809
+ page = 1,
40810
+ limit = 10,
40811
+ sort = {},
40812
+ status = "active"
40813
+ } = {}) {
40814
+ page = page > 0 ? page - 1 : 0;
40815
+ const query = {
40816
+ status
40817
+ };
40818
+ const cacheParams = {
40819
+ page,
40820
+ limit,
40821
+ sort: JSON.stringify(sort)
40822
+ };
40823
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
40824
+ if (search) {
40825
+ query.$text = { $search: search };
40826
+ cacheParams.search = search;
40827
+ }
40828
+ if (school) {
40829
+ try {
40830
+ query.school = new ObjectId35(school);
40831
+ } catch (error) {
40832
+ throw new BadRequestError59("Invalid school ID.");
40833
+ }
40834
+ cacheParams.school = school;
40835
+ }
40836
+ const cacheKey = makeCacheKey19(namespace_collection, cacheParams);
40837
+ logger33.log({
40838
+ level: "info",
40839
+ message: `Cache key for getAll personnel: ${cacheKey}`
40840
+ });
40841
+ try {
40842
+ const cached = await getCache(cacheKey);
40843
+ if (cached) {
40844
+ logger33.log({
40845
+ level: "info",
40846
+ message: `Cache hit for getAll personnel: ${cacheKey}`
40847
+ });
40848
+ return cached;
40849
+ }
40850
+ const items = await collection.aggregate([
40851
+ { $match: query },
40852
+ { $sort: sort },
40853
+ { $skip: page * limit },
40854
+ { $limit: limit }
40855
+ ]).toArray();
40856
+ const length = await collection.countDocuments(query);
40857
+ const data = paginate18(items, page, limit, length);
40858
+ setCache(cacheKey, data, 600).then(() => {
40859
+ logger33.log({
40860
+ level: "info",
40861
+ message: `Cache set for getAll personnel: ${cacheKey}`
40862
+ });
40863
+ }).catch((err) => {
40864
+ logger33.log({
40865
+ level: "error",
40866
+ message: `Failed to set cache for getAll personnel: ${err.message}`
40867
+ });
40868
+ });
40869
+ return data;
40870
+ } catch (error) {
40871
+ logger33.log({ level: "error", message: `${error}` });
40872
+ throw error;
40873
+ }
40874
+ }
40875
+ async function getById(_id) {
40876
+ try {
40877
+ _id = new ObjectId35(_id);
40878
+ } catch (error) {
40879
+ throw new BadRequestError59("Invalid ID.");
40880
+ }
40881
+ const cacheKey = makeCacheKey19(namespace_collection, { _id: String(_id) });
40882
+ try {
40883
+ const cached = await getCache(cacheKey);
40884
+ if (cached) {
40885
+ logger33.log({
40886
+ level: "info",
40887
+ message: `Cache hit for getById personnel: ${cacheKey}`
40888
+ });
40889
+ return cached;
40890
+ }
40891
+ const result = await collection.findOne({
40892
+ _id
40893
+ });
40894
+ setCache(cacheKey, result, 300).then(() => {
40895
+ logger33.log({
40896
+ level: "info",
40897
+ message: `Cache set for personnel by id: ${cacheKey}`
40898
+ });
40899
+ }).catch((err) => {
40900
+ logger33.log({
40901
+ level: "error",
40902
+ message: `Failed to set cache for personnel by id: ${err.message}`
40903
+ });
40904
+ });
40905
+ return result;
40906
+ } catch (error) {
40907
+ if (error instanceof AppError21) {
40908
+ throw error;
40909
+ } else {
40910
+ throw new InternalServerError19("Failed to get personnel.");
40911
+ }
40912
+ }
40913
+ }
40914
+ async function deleteById(_id, session) {
40915
+ try {
40916
+ _id = new ObjectId35(_id);
40917
+ } catch (error) {
40918
+ throw new BadRequestError59("Invalid ID.");
40919
+ }
40920
+ try {
40921
+ const res = await collection.updateOne(
40922
+ { _id },
40923
+ { $set: { status: "deleted", deletedAt: /* @__PURE__ */ new Date() } }
40924
+ );
40925
+ delCachedData();
40926
+ return res;
40927
+ } catch (error) {
40928
+ logger33.log({
40929
+ level: "error",
40930
+ message: error.message
40931
+ });
40932
+ if (error instanceof AppError21) {
40933
+ throw error;
40934
+ } else {
40935
+ throw new InternalServerError19("Failed to delete personnel.");
40936
+ }
40937
+ }
40938
+ }
40939
+ async function getByClassification(classification) {
40940
+ const query = {
40941
+ classification,
40942
+ status: "active"
40943
+ };
40944
+ const cacheKey = makeCacheKey19(namespace_collection, { classification });
40945
+ try {
40946
+ const cached = await getCache(cacheKey);
40947
+ if (cached) {
40948
+ logger33.log({
40949
+ level: "info",
40950
+ message: `Cache hit for getByClassification personnel: ${cacheKey}`
40951
+ });
40952
+ return cached;
40953
+ }
40954
+ const result = await collection.find(query).toArray();
40955
+ setCache(cacheKey, result, 300).then(() => {
40956
+ logger33.log({
40957
+ level: "info",
40958
+ message: `Cache set for personnel by classification: ${cacheKey}`
40959
+ });
40960
+ }).catch((err) => {
40961
+ logger33.log({
40962
+ level: "error",
40963
+ message: `Failed to set cache for personnel by classification: ${err.message}`
40964
+ });
40965
+ });
40966
+ return result;
40967
+ } catch (error) {
40968
+ if (error instanceof AppError21) {
40969
+ throw error;
40970
+ } else {
40971
+ throw new InternalServerError19(
40972
+ "Failed to get personnel by classification."
40973
+ );
40974
+ }
40975
+ }
40976
+ }
40977
+ function delCachedData() {
40978
+ delNamespace().then(() => {
40979
+ logger33.log({
40980
+ level: "info",
40981
+ message: `Cache cleared for namespace: ${namespace_collection}`
40982
+ });
40983
+ }).catch((err) => {
40984
+ logger33.log({
40985
+ level: "error",
40986
+ message: `Failed to clear cache for namespace: ${namespace_collection}: ${err.message}`
40987
+ });
40988
+ });
40989
+ }
40990
+ return {
40991
+ createIndexes,
40992
+ add,
40993
+ updateById,
40994
+ getAll,
40995
+ getById,
40996
+ deleteById,
40997
+ getByClassification,
40998
+ delCachedData
40999
+ };
41000
+ }
41001
+
41002
+ // src/resources/personnel/personnel.controller.ts
41003
+ import { BadRequestError as BadRequestError60, logger as logger34 } from "@eeplatform/nodejs-utils";
41004
+ import Joi38 from "joi";
41005
+ function usePersonnelController() {
41006
+ const {
41007
+ getAll: _getAll,
41008
+ getById: _getById,
41009
+ add: _add,
41010
+ updateById: _updateById,
41011
+ deleteById: _deleteById,
41012
+ getByClassification: _getByClassification
41013
+ } = usePersonnelRepo();
41014
+ async function add(req, res, next) {
41015
+ const value = req.body;
41016
+ const { error } = schemaPersonnel.validate(value);
41017
+ if (error) {
41018
+ next(new BadRequestError60(error.message));
41019
+ logger34.info(`Controller: ${error.message}`);
41020
+ return;
41021
+ }
41022
+ try {
41023
+ const result = await _add(value);
41024
+ res.json(result);
41025
+ return;
41026
+ } catch (error2) {
41027
+ next(error2);
41028
+ }
41029
+ }
41030
+ async function updateById(req, res, next) {
41031
+ const value = req.body;
41032
+ const id = req.params.id ?? "";
41033
+ const validation = Joi38.object({
41034
+ id: Joi38.string().hex().required(),
41035
+ value: Joi38.object({
41036
+ firstName: Joi38.string().optional(),
41037
+ lastName: Joi38.string().optional(),
41038
+ middleName: Joi38.string().optional().allow("", null),
41039
+ classification: Joi38.string().optional(),
41040
+ status: Joi38.string().optional()
41041
+ }).min(1)
41042
+ });
41043
+ const { error } = validation.validate({ id, value });
41044
+ if (error) {
41045
+ next(new BadRequestError60(error.message));
41046
+ logger34.info(`Controller: ${error.message}`);
41047
+ return;
41048
+ }
41049
+ try {
41050
+ const result = await _updateById(id, value);
41051
+ res.json(result);
41052
+ return;
41053
+ } catch (error2) {
41054
+ next(error2);
41055
+ }
41056
+ }
41057
+ async function getAll(req, res, next) {
41058
+ const query = req.query;
41059
+ const validation = Joi38.object({
41060
+ page: Joi38.number().min(1).optional().allow("", null),
41061
+ limit: Joi38.number().min(1).optional().allow("", null),
41062
+ search: Joi38.string().optional().allow("", null),
41063
+ status: Joi38.string().optional().allow("", null),
41064
+ classification: Joi38.string().optional().allow("", null)
41065
+ });
41066
+ const { error } = validation.validate(query);
41067
+ if (error) {
41068
+ next(new BadRequestError60(error.message));
41069
+ return;
41070
+ }
41071
+ const page = parseInt(req.query.page) ?? 1;
41072
+ let limit = parseInt(req.query.limit) ?? 20;
41073
+ limit = isNaN(limit) ? 20 : limit;
41074
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
41075
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
41076
+ const sortObj = {};
41077
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
41078
+ sort.forEach((field, index) => {
41079
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
41080
+ });
41081
+ }
41082
+ const status = req.query.status ?? "active";
41083
+ const search = req.query.search ?? "";
41084
+ try {
41085
+ const personnel = await _getAll({
41086
+ page,
41087
+ limit,
41088
+ sort: sortObj,
41089
+ status,
41090
+ search
41091
+ });
41092
+ res.json(personnel);
41093
+ return;
41094
+ } catch (error2) {
41095
+ next(error2);
41096
+ }
41097
+ }
41098
+ async function getAllBySchool(req, res, next) {
41099
+ const school = req.params.school;
41100
+ const schoolValidation = Joi38.string().hex().required();
41101
+ const { error: schoolError } = schoolValidation.validate(school);
41102
+ if (schoolError) {
41103
+ next(new BadRequestError60(schoolError.message));
41104
+ return;
41105
+ }
41106
+ const query = req.query;
41107
+ const validation = Joi38.object({
41108
+ page: Joi38.number().min(1).optional().allow("", null),
41109
+ limit: Joi38.number().min(1).optional().allow("", null),
41110
+ search: Joi38.string().optional().allow("", null),
41111
+ status: Joi38.string().optional().allow("", null),
41112
+ classification: Joi38.string().optional().allow("", null)
41113
+ });
41114
+ const { error } = validation.validate(query);
41115
+ if (error) {
41116
+ next(new BadRequestError60(error.message));
41117
+ return;
41118
+ }
41119
+ const page = parseInt(req.query.page) ?? 1;
41120
+ let limit = parseInt(req.query.limit) ?? 20;
41121
+ limit = isNaN(limit) ? 20 : limit;
41122
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
41123
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
41124
+ const sortObj = {};
41125
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
41126
+ sort.forEach((field, index) => {
41127
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
41128
+ });
41129
+ }
41130
+ const status = req.query.status ?? "active";
41131
+ const search = req.query.search ?? "";
41132
+ try {
41133
+ const personnel = await _getAll({
41134
+ school,
41135
+ page,
41136
+ limit,
41137
+ sort: sortObj,
41138
+ status,
41139
+ search
41140
+ });
41141
+ res.json(personnel);
41142
+ return;
41143
+ } catch (error2) {
41144
+ next(error2);
41145
+ }
41146
+ }
41147
+ async function getById(req, res, next) {
41148
+ const id = req.params.id;
41149
+ const validation = Joi38.object({
41150
+ id: Joi38.string().hex().required()
41151
+ });
41152
+ const { error } = validation.validate({ id });
41153
+ if (error) {
41154
+ next(new BadRequestError60(error.message));
41155
+ return;
41156
+ }
41157
+ try {
41158
+ const personnel = await _getById(id);
41159
+ res.json({
41160
+ message: "Successfully retrieved personnel.",
41161
+ data: personnel
41162
+ });
41163
+ return;
41164
+ } catch (error2) {
41165
+ next(error2);
41166
+ }
41167
+ }
41168
+ async function deleteById(req, res, next) {
41169
+ const id = req.params.id;
41170
+ const validation = Joi38.object({
41171
+ id: Joi38.string().hex().required()
41172
+ });
41173
+ const { error } = validation.validate({ id });
41174
+ if (error) {
41175
+ next(new BadRequestError60(error.message));
41176
+ return;
41177
+ }
41178
+ try {
41179
+ const result = await _deleteById(id);
41180
+ res.json({
41181
+ message: "Successfully deleted personnel.",
41182
+ data: result
41183
+ });
41184
+ return;
41185
+ } catch (error2) {
41186
+ next(error2);
41187
+ }
41188
+ }
41189
+ async function getByClassification(req, res, next) {
41190
+ const classification = req.params.classification;
41191
+ const validation = Joi38.object({
41192
+ classification: Joi38.string().required()
41193
+ });
41194
+ const { error } = validation.validate({ classification });
41195
+ if (error) {
41196
+ next(new BadRequestError60(error.message));
41197
+ return;
41198
+ }
41199
+ try {
41200
+ const personnel = await _getByClassification(classification);
41201
+ res.json({
41202
+ message: "Successfully retrieved personnel by classification.",
41203
+ data: personnel
41204
+ });
41205
+ return;
41206
+ } catch (error2) {
41207
+ next(error2);
41208
+ }
41209
+ }
41210
+ return {
41211
+ add,
41212
+ updateById,
41213
+ getAll,
41214
+ getAllBySchool,
41215
+ getById,
41216
+ deleteById,
41217
+ getByClassification
41218
+ };
41219
+ }
41220
+
38431
41221
  // src/config.ts
38432
41222
  import * as dotenv from "dotenv";
38433
41223
  dotenv.config();
38434
41224
  export {
38435
41225
  MAsset,
41226
+ MBuilding,
41227
+ MBuildingUnit,
38436
41228
  MCurriculum,
38437
41229
  MCurriculumSubject,
38438
41230
  MGradeLevel,
38439
41231
  MLearner,
41232
+ MPersonnel,
38440
41233
  MPlantilla,
38441
41234
  MStockCard,
38442
41235
  allowedSectionStudentStatuses,
@@ -38447,9 +41240,12 @@ export {
38447
41240
  modelSection,
38448
41241
  modelSectionPreset,
38449
41242
  modelSectionStudent,
41243
+ modelSectionSubject,
38450
41244
  schemaAsset,
38451
41245
  schemaAssetUpdateOption,
38452
41246
  schemaBasicEduCount,
41247
+ schemaBuilding,
41248
+ schemaBuildingUnit,
38453
41249
  schemaCurriculum,
38454
41250
  schemaCurriculumSubject,
38455
41251
  schemaCurriculumSubjectAdd,
@@ -38458,6 +41254,7 @@ export {
38458
41254
  schemaEnrollment,
38459
41255
  schemaGenerateSections,
38460
41256
  schemaGradeLevel,
41257
+ schemaPersonnel,
38461
41258
  schemaPlantilla,
38462
41259
  schemaRegion,
38463
41260
  schemaSchool,
@@ -38465,11 +41262,20 @@ export {
38465
41262
  schemaSection,
38466
41263
  schemaSectionPreset,
38467
41264
  schemaSectionStudent,
41265
+ schemaSectionSubject,
41266
+ schemaSectionSubjectSetup,
38468
41267
  schemaStockCard,
41268
+ schemaUpdateOptions,
38469
41269
  schemaUpdateStatus,
38470
41270
  useAssetController,
38471
41271
  useAssetRepo,
38472
41272
  useBasicEduCountRepo,
41273
+ useBuildingController,
41274
+ useBuildingRepo,
41275
+ useBuildingService,
41276
+ useBuildingUnitController,
41277
+ useBuildingUnitRepo,
41278
+ useBuildingUnitService,
38473
41279
  useCurriculumController,
38474
41280
  useCurriculumRepo,
38475
41281
  useCurriculumSubjectController,
@@ -38485,6 +41291,8 @@ export {
38485
41291
  useGradeLevelRepo,
38486
41292
  useLearnerController,
38487
41293
  useLearnerRepo,
41294
+ usePersonnelController,
41295
+ usePersonnelRepo,
38488
41296
  usePlantillaController,
38489
41297
  usePlantillaRepo,
38490
41298
  usePlantillaService,
@@ -38497,7 +41305,11 @@ export {
38497
41305
  useSectionPresetController,
38498
41306
  useSectionPresetRepo,
38499
41307
  useSectionRepo,
41308
+ useSectionStudentController,
38500
41309
  useSectionStudentRepo,
41310
+ useSectionSubjectController,
41311
+ useSectionSubjectRepo,
41312
+ useSectionSubjectService,
38501
41313
  useStockCardController,
38502
41314
  useStockCardRepository,
38503
41315
  useStockCardService