@7365admin1/module-hygiene 4.23.1 → 4.24.1

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
@@ -2473,7 +2473,8 @@ import { AppServiceType as AppServiceType6 } from "@7365admin1/core";
2473
2473
  var parentChecklistSchema = Joi6.object({
2474
2474
  createdAt: Joi6.alternatives().try(Joi6.date(), Joi6.string()).optional().allow("", null),
2475
2475
  site: Joi6.string().hex().required(),
2476
- serviceType: Joi6.string().valid(...Object.values(AppServiceType6)).required()
2476
+ serviceType: Joi6.string().valid(...Object.values(AppServiceType6)).required(),
2477
+ dateStr: Joi6.string().optional().allow("", null)
2477
2478
  });
2478
2479
  function MParentChecklist(value) {
2479
2480
  const { error } = parentChecklistSchema.validate(value);
@@ -2488,18 +2489,22 @@ function MParentChecklist(value) {
2488
2489
  throw new BadRequestError11(`Invalid site ID format: ${value.site}`);
2489
2490
  }
2490
2491
  }
2492
+ const currentDate = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
2493
+ const sgOffset = 8 * 60 * 60 * 1e3;
2494
+ const sgTime = new Date(currentDate.getTime() + sgOffset);
2495
+ const dateStr = `${sgTime.getUTCFullYear()}-${String(sgTime.getUTCMonth() + 1).padStart(2, "0")}-${String(sgTime.getUTCDate()).padStart(2, "0")}`;
2491
2496
  return {
2492
2497
  site: value.site,
2493
2498
  serviceType: value.serviceType,
2494
2499
  status: "open",
2495
- createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
2496
- updatedAt: value.updatedAt ?? ""
2500
+ createdAt: currentDate,
2501
+ updatedAt: value.updatedAt ?? "",
2502
+ dateStr
2497
2503
  };
2498
2504
  }
2499
2505
 
2500
2506
  // src/repositories/hygiene-parent-checklist.repository.ts
2501
2507
  import { ObjectId as ObjectId7 } from "mongodb";
2502
- import moment2 from "moment-timezone";
2503
2508
  import {
2504
2509
  useAtlas as useAtlas5,
2505
2510
  InternalServerError as InternalServerError4,
@@ -2522,13 +2527,55 @@ function useParentChecklistRepo() {
2522
2527
  const { delNamespace, setCache, getCache } = useCache4(namespace_collection);
2523
2528
  async function createIndex() {
2524
2529
  try {
2530
+ await collection.updateMany(
2531
+ { dateStr: { $exists: false } },
2532
+ [
2533
+ {
2534
+ $set: {
2535
+ dateStr: {
2536
+ $let: {
2537
+ vars: {
2538
+ sgTime: { $add: ["$createdAt", 8 * 60 * 60 * 1e3] }
2539
+ },
2540
+ in: {
2541
+ $dateToString: {
2542
+ format: "%Y-%m-%d",
2543
+ date: "$$sgTime",
2544
+ timezone: "UTC"
2545
+ }
2546
+ }
2547
+ }
2548
+ }
2549
+ }
2550
+ }
2551
+ ]
2552
+ );
2553
+ const duplicates = await collection.aggregate([
2554
+ {
2555
+ $group: {
2556
+ _id: { site: "$site", serviceType: "$serviceType", dateStr: "$dateStr" },
2557
+ docs: { $push: "$_id" },
2558
+ count: { $sum: 1 }
2559
+ }
2560
+ },
2561
+ { $match: { count: { $gt: 1 } } }
2562
+ ]).toArray();
2563
+ for (const group of duplicates) {
2564
+ const toDelete = group.docs.slice(1);
2565
+ await collection.deleteMany({ _id: { $in: toDelete } });
2566
+ logger14.info(
2567
+ `Deleted ${toDelete.length} duplicate parent checklists for site ${group._id.site} / ${group._id.serviceType} on date ${group._id.dateStr}`
2568
+ );
2569
+ }
2525
2570
  await collection.createIndexes([
2526
2571
  { key: { serviceType: 1 } },
2527
2572
  { key: { createdAt: 1 } },
2528
2573
  { key: { site: 1 } },
2529
- { key: { status: 1 } }
2574
+ { key: { status: 1 } },
2575
+ { key: { site: 1, serviceType: 1, dateStr: 1 }, unique: true }
2530
2576
  ]);
2531
2577
  } catch (error) {
2578
+ logger14.error("Failed to create index/backfill on hygiene parent checklist:", error);
2532
2579
  throw new InternalServerError4(
2533
2580
  "Failed to create index on hygiene parent checklist."
2534
2581
  );
@@ -2537,10 +2584,13 @@ function useParentChecklistRepo() {
2537
2584
  async function createParentChecklist(value, session) {
2538
2585
  try {
2539
2586
  const currentDate = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
2540
- const startOfDay = moment2(currentDate).tz("Asia/Singapore").startOf("day").toDate();
2541
- const endOfDay = moment2(currentDate).tz("Asia/Singapore").endOf("day").toDate();
2587
+ const sgOffset = 8 * 60 * 60 * 1e3;
2588
+ const sgTime = new Date(currentDate.getTime() + sgOffset);
2589
+ const startOfDay = new Date(Date.UTC(sgTime.getUTCFullYear(), sgTime.getUTCMonth(), sgTime.getUTCDate(), 0, 0, 0, 0) - sgOffset);
2590
+ const endOfDay = new Date(Date.UTC(sgTime.getUTCFullYear(), sgTime.getUTCMonth(), sgTime.getUTCDate(), 23, 59, 59, 999) - sgOffset);
2591
+ const targetDateStr = `${sgTime.getUTCFullYear()}-${String(sgTime.getUTCMonth() + 1).padStart(2, "0")}-${String(sgTime.getUTCDate()).padStart(2, "0")}`;
2592
+ const dateStr = targetDateStr;
2542
2593
  const allServiceTypes = Object.values(AppServiceType7);
2543
- const dateStr = currentDate.toISOString().split("T")[0];
2544
2594
  if (value.site) {
2545
2595
  let siteObjectId;
2546
2596
  try {
@@ -2550,9 +2600,12 @@ function useParentChecklistRepo() {
2550
2600
  }
2551
2601
  if (value.serviceType) {
2552
2602
  const existingChecklist = await collection.findOne({
2553
- createdAt: { $gte: startOfDay, $lte: endOfDay },
2554
2603
  site: siteObjectId,
2555
- serviceType: value.serviceType
2604
+ serviceType: value.serviceType,
2605
+ $or: [
2606
+ { dateStr: targetDateStr },
2607
+ { createdAt: { $gte: startOfDay, $lte: endOfDay } }
2608
+ ]
2556
2609
  });
2557
2610
  if (existingChecklist) {
2558
2611
  logger14.info(
@@ -2565,22 +2618,41 @@ function useParentChecklistRepo() {
2565
2618
  createdAt: currentDate,
2566
2619
  serviceType: value.serviceType
2567
2620
  });
2568
- const result3 = await collection.insertOne(doc, { session });
2569
- delNamespace().catch((err) => {
2570
- logger14.error(
2571
- `Failed to clear cache for namespace: ${namespace_collection}`,
2572
- err
2621
+ let insertedId;
2622
+ try {
2623
+ const result = await collection.insertOne(doc, { session });
2624
+ insertedId = result.insertedId;
2625
+ delNamespace().catch((err) => {
2626
+ logger14.error(
2627
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2628
+ err
2629
+ );
2630
+ });
2631
+ logger14.info(
2632
+ `Created parent checklist for site ${value.site} / ${value.serviceType} for today: ${dateStr}`
2573
2633
  );
2574
- });
2575
- logger14.info(
2576
- `Created parent checklist for site ${value.site} / ${value.serviceType} for today: ${dateStr}`
2577
- );
2578
- return result3.insertedId;
2634
+ } catch (error) {
2635
+ if (error.code === 11e3) {
2636
+ const existingChecklist2 = await collection.findOne({
2637
+ site: siteObjectId,
2638
+ serviceType: value.serviceType,
2639
+ dateStr: targetDateStr
2640
+ });
2641
+ if (existingChecklist2) {
2642
+ return existingChecklist2._id;
2643
+ }
2644
+ }
2645
+ throw error;
2646
+ }
2647
+ return insertedId;
2579
2648
  }
2580
2649
  const existingServiceTypes = await collection.distinct("serviceType", {
2581
- createdAt: { $gte: startOfDay, $lte: endOfDay },
2582
2650
  site: siteObjectId,
2583
- serviceType: { $exists: true, $ne: null }
2651
+ serviceType: { $exists: true, $ne: null },
2652
+ $or: [
2653
+ { dateStr: targetDateStr },
2654
+ { createdAt: { $gte: startOfDay, $lte: endOfDay } }
2655
+ ]
2584
2656
  });
2585
2657
  const missingServiceTypes = allServiceTypes.filter(
2586
2658
  (st) => !existingServiceTypes.includes(st)
@@ -2590,8 +2662,11 @@ function useParentChecklistRepo() {
2590
2662
  `All serviceType checklists already exist for site ${value.site} on ${dateStr}`
2591
2663
  );
2592
2664
  const first = await collection.findOne({
2593
- createdAt: { $gte: startOfDay, $lte: endOfDay },
2594
- site: siteObjectId
2665
+ site: siteObjectId,
2666
+ $or: [
2667
+ { dateStr: targetDateStr },
2668
+ { createdAt: { $gte: startOfDay, $lte: endOfDay } }
2669
+ ]
2595
2670
  });
2596
2671
  return first._id;
2597
2672
  }
@@ -2602,17 +2677,30 @@ function useParentChecklistRepo() {
2602
2677
  serviceType
2603
2678
  })
2604
2679
  );
2605
- const result2 = await collection.insertMany(checklistDocs2, { session });
2606
- delNamespace().catch((err) => {
2607
- logger14.error(
2608
- `Failed to clear cache for namespace: ${namespace_collection}`,
2609
- err
2680
+ let insertedIds2 = [];
2681
+ try {
2682
+ const result = await collection.insertMany(checklistDocs2, { session, ordered: false });
2683
+ insertedIds2 = Object.values(result.insertedIds);
2684
+ delNamespace().catch((err) => {
2685
+ logger14.error(
2686
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2687
+ err
2688
+ );
2689
+ });
2690
+ logger14.info(
2691
+ `Created ${checklistDocs2.length} parent checklists for site ${value.site} for today: ${dateStr}`
2610
2692
  );
2611
- });
2612
- logger14.info(
2613
- `Created ${checklistDocs2.length} parent checklists for site ${value.site} for today: ${dateStr}`
2614
- );
2615
- return Object.values(result2.insertedIds);
2693
+ } catch (error) {
2694
+ if (error.code === 11e3 || error.writeErrors) {
2695
+ const existing = await collection.find({
2696
+ site: siteObjectId,
2697
+ dateStr: targetDateStr
2698
+ }).toArray();
2699
+ return existing.map((e) => e._id);
2700
+ }
2701
+ throw error;
2702
+ }
2703
+ return insertedIds2;
2616
2704
  }
2617
2705
  const siteIds = await getHygieneSiteIds();
2618
2706
  if (!Array.isArray(siteIds)) {
@@ -2625,8 +2713,11 @@ function useParentChecklistRepo() {
2625
2713
  }
2626
2714
  const existingPairs = await collection.find(
2627
2715
  {
2628
- createdAt: { $gte: startOfDay, $lte: endOfDay },
2629
- serviceType: { $exists: true, $ne: null }
2716
+ serviceType: { $exists: true, $ne: null },
2717
+ $or: [
2718
+ { dateStr: targetDateStr },
2719
+ { createdAt: { $gte: startOfDay, $lte: endOfDay } }
2720
+ ]
2630
2721
  },
2631
2722
  { projection: { site: 1, serviceType: 1 } }
2632
2723
  ).toArray();
@@ -2658,19 +2749,31 @@ function useParentChecklistRepo() {
2658
2749
  logger14.info(
2659
2750
  `createParentChecklist: Creating ${checklistDocs.length} missing site+serviceType checklist(s) for today: ${dateStr}`
2660
2751
  );
2661
- const result = await collection.insertMany(checklistDocs, { session });
2662
- delNamespace().then(() => {
2663
- logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
2664
- }).catch((err) => {
2665
- logger14.error(
2666
- `Failed to clear cache for namespace: ${namespace_collection}`,
2667
- err
2752
+ let insertedIds = [];
2753
+ try {
2754
+ const result = await collection.insertMany(checklistDocs, { session, ordered: false });
2755
+ insertedIds = Object.values(result.insertedIds);
2756
+ delNamespace().then(() => {
2757
+ logger14.info(`Cache cleared for namespace: ${namespace_collection}`);
2758
+ }).catch((err) => {
2759
+ logger14.error(
2760
+ `Failed to clear cache for namespace: ${namespace_collection}`,
2761
+ err
2762
+ );
2763
+ });
2764
+ logger14.info(
2765
+ `Created ${Object.keys(result.insertedIds).length} parent checklists for today: ${dateStr}`
2668
2766
  );
2669
- });
2670
- logger14.info(
2671
- `Created ${Object.keys(result.insertedIds).length} parent checklists for today: ${dateStr}`
2672
- );
2673
- return Object.values(result.insertedIds);
2767
+ } catch (error) {
2768
+ if (error.code === 11e3 || error.writeErrors) {
2769
+ const existing = await collection.find({
2770
+ dateStr: targetDateStr
2771
+ }).toArray();
2772
+ return existing.map((e) => e._id);
2773
+ }
2774
+ throw error;
2775
+ }
2776
+ return insertedIds;
2674
2777
  } catch (error) {
2675
2778
  logger14.error("Failed to create daily parent checklist", error);
2676
2779
  throw error;
@@ -2889,8 +2992,11 @@ function useParentChecklistRepo() {
2889
2992
  }
2890
2993
  }
2891
2994
  async function getTodayParentChecklists() {
2892
- const start = moment2().tz("Asia/Singapore").startOf("day").toDate();
2893
- const end = moment2().tz("Asia/Singapore").endOf("day").toDate();
2995
+ const sgOffset = 8 * 60 * 60 * 1e3;
2996
+ const now = /* @__PURE__ */ new Date();
2997
+ const sgTime = new Date(now.getTime() + sgOffset);
2998
+ const start = new Date(Date.UTC(sgTime.getUTCFullYear(), sgTime.getUTCMonth(), sgTime.getUTCDate(), 0, 0, 0, 0) - sgOffset);
2999
+ const end = new Date(Date.UTC(sgTime.getUTCFullYear(), sgTime.getUTCMonth(), sgTime.getUTCDate(), 23, 59, 59, 999) - sgOffset);
2894
3000
  try {
2895
3001
  const items = await collection.find(
2896
3002
  { createdAt: { $gte: start, $lte: end } },
@@ -2903,8 +3009,11 @@ function useParentChecklistRepo() {
2903
3009
  }
2904
3010
  }
2905
3011
  async function getTodayParentChecklistsForAreaGen() {
2906
- const start = moment2().tz("Asia/Singapore").startOf("day").toDate();
2907
- const end = moment2().tz("Asia/Singapore").endOf("day").toDate();
3012
+ const sgOffset = 8 * 60 * 60 * 1e3;
3013
+ const now = /* @__PURE__ */ new Date();
3014
+ const sgTime = new Date(now.getTime() + sgOffset);
3015
+ const start = new Date(Date.UTC(sgTime.getUTCFullYear(), sgTime.getUTCMonth(), sgTime.getUTCDate(), 0, 0, 0, 0) - sgOffset);
3016
+ const end = new Date(Date.UTC(sgTime.getUTCFullYear(), sgTime.getUTCMonth(), sgTime.getUTCDate(), 23, 59, 59, 999) - sgOffset);
2908
3017
  try {
2909
3018
  const items = await collection.find(
2910
3019
  {
@@ -3077,6 +3186,7 @@ function MAreaChecklist(value) {
3077
3186
  reject: false,
3078
3187
  status: "open",
3079
3188
  remarks: "",
3189
+ attachment: [],
3080
3190
  completedBy: "",
3081
3191
  timestamp: ""
3082
3192
  }))
@@ -3514,7 +3624,12 @@ function useAreaChecklistRepo() {
3514
3624
  },
3515
3625
  unit: "$checklist.units.unit",
3516
3626
  name: "$checklist.units.name",
3627
+ approve: "$checklist.units.approve",
3628
+ reject: "$checklist.units.reject",
3517
3629
  remarks: "$checklist.units.remarks",
3630
+ attachment: {
3631
+ $ifNull: ["$checklist.units.attachment", []]
3632
+ },
3518
3633
  status: {
3519
3634
  $switch: {
3520
3635
  branches: [
@@ -3547,8 +3662,11 @@ function useAreaChecklistRepo() {
3547
3662
  $push: {
3548
3663
  unit: "$unit",
3549
3664
  name: "$name",
3665
+ approve: "$approve",
3666
+ reject: "$reject",
3550
3667
  status: "$status",
3551
3668
  remarks: "$remarks",
3669
+ attachment: "$attachment",
3552
3670
  completedByName: "$completedByName",
3553
3671
  timestamp: "$timestamp"
3554
3672
  }
@@ -3700,6 +3818,10 @@ function useAreaChecklistRepo() {
3700
3818
  name: "$checklist.units.name",
3701
3819
  approve: "$checklist.units.approve",
3702
3820
  reject: "$checklist.units.reject",
3821
+ unitRemarks: "$checklist.units.remarks",
3822
+ unitAttachment: {
3823
+ $ifNull: ["$checklist.units.attachment", []]
3824
+ },
3703
3825
  timestamp: "$checklist.units.timestamp",
3704
3826
  status: {
3705
3827
  $switch: {
@@ -3739,7 +3861,8 @@ function useAreaChecklistRepo() {
3739
3861
  reject: "$reject",
3740
3862
  timestamp: "$timestamp",
3741
3863
  status: "$status",
3742
- remarks: "$remarks",
3864
+ remarks: "$unitRemarks",
3865
+ attachment: "$unitAttachment",
3743
3866
  completedByName: "$completedByName"
3744
3867
  }
3745
3868
  }
@@ -3887,16 +4010,13 @@ function useAreaChecklistRepo() {
3887
4010
  } else if (value.reject === true) {
3888
4011
  updateValue["checklist.$[checklist].units.$[unit].approve"] = false;
3889
4012
  updateValue["checklist.$[checklist].units.$[unit].reject"] = true;
3890
- updateValue["checklist.$[checklist].units.$[unit].status"] = "open";
4013
+ updateValue["checklist.$[checklist].units.$[unit].status"] = "completed";
3891
4014
  }
3892
- if (value.remarks) {
3893
- updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks;
4015
+ if (value.remarks !== void 0) {
4016
+ updateValue["checklist.$[checklist].units.$[unit].remarks"] = value.remarks ?? "";
3894
4017
  }
3895
- if (value.attachment) {
3896
- updateValue["checklist.$[checklist].attachment"] = value.attachment;
3897
- if (value.remarks) {
3898
- updateValue["checklist.$[checklist].remarks"] = value.remarks;
3899
- }
4018
+ if (value.attachment !== void 0) {
4019
+ updateValue["checklist.$[checklist].units.$[unit].attachment"] = value.attachment ?? [];
3900
4020
  }
3901
4021
  if (value.completedBy) {
3902
4022
  updateValue["checklist.$[checklist].units.$[unit].completedBy"] = new ObjectId9(value.completedBy);
@@ -4324,6 +4444,7 @@ function useAreaChecklistService() {
4324
4444
  reject: false,
4325
4445
  status: "open",
4326
4446
  remarks: "",
4447
+ attachment: [],
4327
4448
  completedBy: "",
4328
4449
  timestamp: ""
4329
4450
  }))
@@ -6797,6 +6918,7 @@ function useScheduleTaskService() {
6797
6918
  reject: false,
6798
6919
  status: "open",
6799
6920
  remarks: "",
6921
+ attachment: [],
6800
6922
  completedBy: "",
6801
6923
  timestamp: ""
6802
6924
  }));