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