|  |  |  | @ -2135,88 +2135,313 @@ exports.getSuppliersForPlanSearch = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // controllers/validationHandler.js (add below the previous handler)
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | exports.createRequestedPlanBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const { | 
			
		
	
		
			
				
					|  |  |  |  |     customerId, | 
			
		
	
		
			
				
					|  |  |  |  |     type_of_water, | 
			
		
	
		
			
				
					|  |  |  |  |     capacity, | 
			
		
	
		
			
				
					|  |  |  |  |     quantity, | 
			
		
	
		
			
				
					|  |  |  |  |     start_date, | 
			
		
	
		
			
				
					|  |  |  |  |     end_date, | 
			
		
	
		
			
				
					|  |  |  |  |     time, | 
			
		
	
		
			
				
					|  |  |  |  |     frequency,          // "daily" | "weekly_once" | "weekly_twice" | "weekly_thrice" | "weekly"
 | 
			
		
	
		
			
				
					|  |  |  |  |     weekly_count,       // used only if frequency === "weekly"
 | 
			
		
	
		
			
				
					|  |  |  |  |     requested_suppliers | 
			
		
	
		
			
				
					|  |  |  |  |   } = req.body; | 
			
		
	
		
			
				
					|  |  |  |  | // exports.createRequestedPlanBooking = async (req, reply) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     customerId,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     type_of_water,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     capacity,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     quantity,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     start_date,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     end_date,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     time,
 | 
			
		
	
		
			
				
					|  |  |  |  | //     frequency,          // "daily" | "weekly_once" | "weekly_twice" | "weekly_thrice" | "weekly"
 | 
			
		
	
		
			
				
					|  |  |  |  | //     weekly_count,       // used only if frequency === "weekly"
 | 
			
		
	
		
			
				
					|  |  |  |  | //     requested_suppliers
 | 
			
		
	
		
			
				
					|  |  |  |  | //   } = req.body;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //   // helpers inside function (as you prefer)
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const parseCapacity = (v) => parseFloat((v || "0").toString().replace(/,/g, "")) || 0;
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const parseIntSafe = (v) => parseInt((v || "0").toString().replace(/,/g, ""), 10) || 0;
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const toISODate = (d) => d.toISOString().slice(0, 10);
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const mkUTCDate = (yyyy_mm_dd) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const [y, m, d] = (yyyy_mm_dd || "").split("-").map(Number);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return new Date(Date.UTC(y, (m || 1) - 1, d || 1));
 | 
			
		
	
		
			
				
					|  |  |  |  | //   };
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const normalizeWeeklyCount = (freq, wc) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (freq === "weekly_once") return 1;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (freq === "weekly_twice") return 2;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (freq === "weekly_thrice") return 3;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (freq === "weekly") return wc || 1;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return 1;
 | 
			
		
	
		
			
				
					|  |  |  |  | //   };
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const computeWeeklyDOWs = ({ anchorDow, weeklyCount }) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (weeklyCount === 1) return [anchorDow];
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (weeklyCount === 2) return [anchorDow, (anchorDow + 3) % 7];
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (weeklyCount === 3) return [anchorDow, (anchorDow + 2) % 7, (anchorDow + 4) % 7];
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return [anchorDow];
 | 
			
		
	
		
			
				
					|  |  |  |  | //   };
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const generateDates = ({ frequency, start_date, end_date, weekly_count }) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const start = mkUTCDate(start_date);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const end = mkUTCDate(end_date);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (isNaN(start) || isNaN(end)) throw new Error("Invalid start_date or end_date");
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (end < start) throw new Error("end_date must be after or equal to start_date");
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // ~3 months cap
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const maxMs = 92 * 24 * 60 * 60 * 1000;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if ((end - start) > maxMs) throw new Error("Range exceeds 3 months");
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const out = [];
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (frequency === "daily") {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //         out.push(toISODate(d));
 | 
			
		
	
		
			
				
					|  |  |  |  | //       }
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return out;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // helpers inside function (as you prefer)
 | 
			
		
	
		
			
				
					|  |  |  |  |   const parseCapacity = (v) => parseFloat((v || "0").toString().replace(/,/g, "")) || 0; | 
			
		
	
		
			
				
					|  |  |  |  |   const parseIntSafe = (v) => parseInt((v || "0").toString().replace(/,/g, ""), 10) || 0; | 
			
		
	
		
			
				
					|  |  |  |  |   const toISODate = (d) => d.toISOString().slice(0, 10); | 
			
		
	
		
			
				
					|  |  |  |  |   const mkUTCDate = (yyyy_mm_dd) => { | 
			
		
	
		
			
				
					|  |  |  |  |     const [y, m, d] = (yyyy_mm_dd || "").split("-").map(Number); | 
			
		
	
		
			
				
					|  |  |  |  |     return new Date(Date.UTC(y, (m || 1) - 1, d || 1)); | 
			
		
	
		
			
				
					|  |  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  |  |   const normalizeWeeklyCount = (freq, wc) => { | 
			
		
	
		
			
				
					|  |  |  |  |     if (freq === "weekly_once") return 1; | 
			
		
	
		
			
				
					|  |  |  |  |     if (freq === "weekly_twice") return 2; | 
			
		
	
		
			
				
					|  |  |  |  |     if (freq === "weekly_thrice") return 3; | 
			
		
	
		
			
				
					|  |  |  |  |     if (freq === "weekly") return wc || 1; | 
			
		
	
		
			
				
					|  |  |  |  |     return 1; | 
			
		
	
		
			
				
					|  |  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  |  |   const computeWeeklyDOWs = ({ anchorDow, weeklyCount }) => { | 
			
		
	
		
			
				
					|  |  |  |  |     if (weeklyCount === 1) return [anchorDow]; | 
			
		
	
		
			
				
					|  |  |  |  |     if (weeklyCount === 2) return [anchorDow, (anchorDow + 3) % 7]; | 
			
		
	
		
			
				
					|  |  |  |  |     if (weeklyCount === 3) return [anchorDow, (anchorDow + 2) % 7, (anchorDow + 4) % 7]; | 
			
		
	
		
			
				
					|  |  |  |  |     return [anchorDow]; | 
			
		
	
		
			
				
					|  |  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  |  |   const generateDates = ({ frequency, start_date, end_date, weekly_count }) => { | 
			
		
	
		
			
				
					|  |  |  |  |     const start = mkUTCDate(start_date); | 
			
		
	
		
			
				
					|  |  |  |  |     const end = mkUTCDate(end_date); | 
			
		
	
		
			
				
					|  |  |  |  |     if (isNaN(start) || isNaN(end)) throw new Error("Invalid start_date or end_date"); | 
			
		
	
		
			
				
					|  |  |  |  |     if (end < start) throw new Error("end_date must be after or equal to start_date"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // ~3 months cap
 | 
			
		
	
		
			
				
					|  |  |  |  |     const maxMs = 92 * 24 * 60 * 60 * 1000; | 
			
		
	
		
			
				
					|  |  |  |  |     if ((end - start) > maxMs) throw new Error("Range exceeds 3 months"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const out = []; | 
			
		
	
		
			
				
					|  |  |  |  |     if (frequency === "daily") { | 
			
		
	
		
			
				
					|  |  |  |  |       for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { | 
			
		
	
		
			
				
					|  |  |  |  |         out.push(toISODate(d)); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |       return out; | 
			
		
	
		
			
				
					|  |  |  |  | //     if (frequency.startsWith("weekly") || frequency === "weekly") {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       const wc = normalizeWeeklyCount(frequency, weekly_count);
 | 
			
		
	
		
			
				
					|  |  |  |  | //       const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc });
 | 
			
		
	
		
			
				
					|  |  |  |  | //       const set = new Set(dows);
 | 
			
		
	
		
			
				
					|  |  |  |  | //       for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //         if (set.has(d.getUTCDay())) out.push(toISODate(d));
 | 
			
		
	
		
			
				
					|  |  |  |  | //       }
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return out;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     throw new Error("Unsupported frequency");
 | 
			
		
	
		
			
				
					|  |  |  |  | //   };
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //   try {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (!customerId || !type_of_water || !capacity || !quantity ||
 | 
			
		
	
		
			
				
					|  |  |  |  | //         !start_date || !end_date || !time || !frequency || !requested_suppliers) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(400).send({
 | 
			
		
	
		
			
				
					|  |  |  |  | //         status_code: 400,
 | 
			
		
	
		
			
				
					|  |  |  |  | //         message: "Missing required fields"
 | 
			
		
	
		
			
				
					|  |  |  |  | //       });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const cap = parseCapacity(capacity);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const qty = parseIntSafe(quantity);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const total_required_capacity = cap * qty;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const dates = generateDates({ frequency, start_date, end_date, weekly_count });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (dates.length === 0) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(400).send({ status_code: 400, message: "No dates generated for given inputs" });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const doc = new RecurringRequestedBooking({
 | 
			
		
	
		
			
				
					|  |  |  |  | //       customerId,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       type_of_water,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       capacity,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       quantity,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       total_required_capacity,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       frequency,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       weekly_count: normalizeWeeklyCount(frequency, weekly_count),
 | 
			
		
	
		
			
				
					|  |  |  |  | //       start_date,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       end_date,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       time,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       dates,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       requested_suppliers,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       status: "pending"
 | 
			
		
	
		
			
				
					|  |  |  |  | //     });
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     await doc.save();
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return reply.send({
 | 
			
		
	
		
			
				
					|  |  |  |  | //       status_code: 200,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       message: "Plan requested booking created successfully",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       count: dates.length,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       dates,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       data: doc
 | 
			
		
	
		
			
				
					|  |  |  |  | //     });
 | 
			
		
	
		
			
				
					|  |  |  |  | //   } catch (err) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     console.error(err);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return reply.code(500).send({
 | 
			
		
	
		
			
				
					|  |  |  |  | //       status_code: 500,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       message: "Something went wrong while saving",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       error: err.message
 | 
			
		
	
		
			
				
					|  |  |  |  | //     });
 | 
			
		
	
		
			
				
					|  |  |  |  | //   }
 | 
			
		
	
		
			
				
					|  |  |  |  | // };
 | 
			
		
	
		
			
				
					|  |  |  |  | // controllers/plan.controller.js
 | 
			
		
	
		
			
				
					|  |  |  |  | //const RecurringRequestedBooking = require("../models/RecurringRequestedBooking");
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ---------- Helpers ----------
 | 
			
		
	
		
			
				
					|  |  |  |  | const MONTHS = { | 
			
		
	
		
			
				
					|  |  |  |  |   jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5, | 
			
		
	
		
			
				
					|  |  |  |  |   jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11, | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const parseCapacity = (v) => parseFloat((v ?? "0").toString().replace(/,/g, "")) || 0; | 
			
		
	
		
			
				
					|  |  |  |  | const parseIntSafe  = (v) => parseInt((v ?? "0").toString().replace(/,/g, ""), 10) || 0; | 
			
		
	
		
			
				
					|  |  |  |  | const toISODate     = (d) => d.toISOString().slice(0, 10); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  |  |  * Accepts: | 
			
		
	
		
			
				
					|  |  |  |  |  *  - "YYYY-MM-DD" | 
			
		
	
		
			
				
					|  |  |  |  |  *  - "DD-MMM-YYYY" | 
			
		
	
		
			
				
					|  |  |  |  |  *  - "DD-MMM-YYYY - HH:mm" (time portion ignored for date gen) | 
			
		
	
		
			
				
					|  |  |  |  |  * Returns a Date in UTC midnight of that calendar day. | 
			
		
	
		
			
				
					|  |  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  |  | const mkUTCDate = (input) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (!input || typeof input !== "string") throw new Error("Invalid date string"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   const s = input.trim(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // ISO: YYYY-MM-DD (optionally with time, but we only take the first three parts)
 | 
			
		
	
		
			
				
					|  |  |  |  |   const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})/); | 
			
		
	
		
			
				
					|  |  |  |  |   if (iso) { | 
			
		
	
		
			
				
					|  |  |  |  |     const y = Number(iso[1]); | 
			
		
	
		
			
				
					|  |  |  |  |     const m = Number(iso[2]) - 1; | 
			
		
	
		
			
				
					|  |  |  |  |     const d = Number(iso[3]); | 
			
		
	
		
			
				
					|  |  |  |  |     return new Date(Date.UTC(y, m, d)); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // D-MMM-YYYY (optional " - HH:mm")
 | 
			
		
	
		
			
				
					|  |  |  |  |   const mmm = s.match(/^(\d{1,2})-([A-Za-z]{3})-(\d{4})(?:\s*-\s*(\d{1,2}):(\d{2}))?$/); | 
			
		
	
		
			
				
					|  |  |  |  |   if (mmm) { | 
			
		
	
		
			
				
					|  |  |  |  |     const d   = Number(mmm[1]); | 
			
		
	
		
			
				
					|  |  |  |  |     const mon = MONTHS[mmm[2].toLowerCase()]; | 
			
		
	
		
			
				
					|  |  |  |  |     const y   = Number(mmm[3]); | 
			
		
	
		
			
				
					|  |  |  |  |     if (mon == null) throw new Error("Invalid month abbreviation in date"); | 
			
		
	
		
			
				
					|  |  |  |  |     return new Date(Date.UTC(y, mon, d)); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   throw new Error("Unsupported date format. Use YYYY-MM-DD or DD-MMM-YYYY (- HH:mm)."); | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const normalizeWeeklyCount = (freq, wc) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (freq === "weekly_once") return 1; | 
			
		
	
		
			
				
					|  |  |  |  |   if (freq === "weekly_twice") return 2; | 
			
		
	
		
			
				
					|  |  |  |  |   if (freq === "weekly_thrice") return 3; | 
			
		
	
		
			
				
					|  |  |  |  |   if (freq === "weekly") return wc || 1; | 
			
		
	
		
			
				
					|  |  |  |  |   return 1; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const computeWeeklyDOWs = ({ anchorDow, weeklyCount }) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (weeklyCount === 1) return [anchorDow]; | 
			
		
	
		
			
				
					|  |  |  |  |   if (weeklyCount === 2) return [anchorDow, (anchorDow + 3) % 7]; | 
			
		
	
		
			
				
					|  |  |  |  |   if (weeklyCount === 3) return [anchorDow, (anchorDow + 2) % 7, (anchorDow + 4) % 7]; | 
			
		
	
		
			
				
					|  |  |  |  |   return [anchorDow]; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const generateDates = ({ frequency, start_date, end_date, weekly_count }) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const start = mkUTCDate(start_date); | 
			
		
	
		
			
				
					|  |  |  |  |   const end   = mkUTCDate(end_date); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) { | 
			
		
	
		
			
				
					|  |  |  |  |     throw new Error("Invalid start_date or end_date"); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   if (end < start) throw new Error("end_date must be after or equal to start_date"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // ~3 months cap
 | 
			
		
	
		
			
				
					|  |  |  |  |   const maxMs = 92 * 24 * 60 * 60 * 1000; | 
			
		
	
		
			
				
					|  |  |  |  |   if ((end - start) > maxMs) throw new Error("Range exceeds 3 months"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   const out = []; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (frequency === "daily") { | 
			
		
	
		
			
				
					|  |  |  |  |     for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { | 
			
		
	
		
			
				
					|  |  |  |  |       out.push(toISODate(d)); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     return out; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (frequency.startsWith("weekly") || frequency === "weekly") { | 
			
		
	
		
			
				
					|  |  |  |  |       const wc = normalizeWeeklyCount(frequency, weekly_count); | 
			
		
	
		
			
				
					|  |  |  |  |       const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc }); | 
			
		
	
		
			
				
					|  |  |  |  |       const set = new Set(dows); | 
			
		
	
		
			
				
					|  |  |  |  |       for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (set.has(d.getUTCDay())) out.push(toISODate(d)); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |       return out; | 
			
		
	
		
			
				
					|  |  |  |  |   if (frequency.startsWith("weekly") || frequency === "weekly") { | 
			
		
	
		
			
				
					|  |  |  |  |     const wc = normalizeWeeklyCount(frequency, weekly_count); | 
			
		
	
		
			
				
					|  |  |  |  |     const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc }); | 
			
		
	
		
			
				
					|  |  |  |  |     const set = new Set(dows); | 
			
		
	
		
			
				
					|  |  |  |  |     for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { | 
			
		
	
		
			
				
					|  |  |  |  |       if (set.has(d.getUTCDay())) out.push(toISODate(d)); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     return out; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     throw new Error("Unsupported frequency"); | 
			
		
	
		
			
				
					|  |  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  |  |   throw new Error("Unsupported frequency"); | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const ensureRequestedSuppliers = (arr) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const inArr = Array.isArray(arr) ? arr : []; | 
			
		
	
		
			
				
					|  |  |  |  |   return inArr.map((x) => ({ | 
			
		
	
		
			
				
					|  |  |  |  |     supplierId: x?.supplierId ?? "", | 
			
		
	
		
			
				
					|  |  |  |  |     quoted_amount: typeof x?.quoted_amount === "number" ? x.quoted_amount : 0, | 
			
		
	
		
			
				
					|  |  |  |  |     time: x?.time ?? null, | 
			
		
	
		
			
				
					|  |  |  |  |     status: x?.status ?? "pending", | 
			
		
	
		
			
				
					|  |  |  |  |   })); | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ---------- Controller ----------
 | 
			
		
	
		
			
				
					|  |  |  |  | exports.createRequestedPlanBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |   try { | 
			
		
	
		
			
				
					|  |  |  |  |     if (!customerId || !type_of_water || !capacity || !quantity || | 
			
		
	
		
			
				
					|  |  |  |  |         !start_date || !end_date || !time || !frequency || !requested_suppliers) { | 
			
		
	
		
			
				
					|  |  |  |  |     const { | 
			
		
	
		
			
				
					|  |  |  |  |       customerId, | 
			
		
	
		
			
				
					|  |  |  |  |       type_of_water, | 
			
		
	
		
			
				
					|  |  |  |  |       capacity, | 
			
		
	
		
			
				
					|  |  |  |  |       quantity, | 
			
		
	
		
			
				
					|  |  |  |  |       start_date, | 
			
		
	
		
			
				
					|  |  |  |  |       end_date, | 
			
		
	
		
			
				
					|  |  |  |  |       time, | 
			
		
	
		
			
				
					|  |  |  |  |       frequency,          // "daily" | "weekly_once" | "weekly_twice" | "weekly_thrice" | "weekly"
 | 
			
		
	
		
			
				
					|  |  |  |  |       weekly_count,       // used only if frequency === "weekly"
 | 
			
		
	
		
			
				
					|  |  |  |  |       requested_suppliers | 
			
		
	
		
			
				
					|  |  |  |  |     } = req.body || {}; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Basic presence check
 | 
			
		
	
		
			
				
					|  |  |  |  |     const missing = [ | 
			
		
	
		
			
				
					|  |  |  |  |       ["customerId", customerId], | 
			
		
	
		
			
				
					|  |  |  |  |       ["type_of_water", type_of_water], | 
			
		
	
		
			
				
					|  |  |  |  |       ["capacity", capacity], | 
			
		
	
		
			
				
					|  |  |  |  |       ["quantity", quantity], | 
			
		
	
		
			
				
					|  |  |  |  |       ["start_date", start_date], | 
			
		
	
		
			
				
					|  |  |  |  |       ["end_date", end_date], | 
			
		
	
		
			
				
					|  |  |  |  |       ["time", time], | 
			
		
	
		
			
				
					|  |  |  |  |       ["frequency", frequency], | 
			
		
	
		
			
				
					|  |  |  |  |       ["requested_suppliers", requested_suppliers], | 
			
		
	
		
			
				
					|  |  |  |  |     ].filter(([k, v]) => v == null || (typeof v === "string" && v.trim() === "")); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (missing.length) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ | 
			
		
	
		
			
				
					|  |  |  |  |         status_code: 400, | 
			
		
	
		
			
				
					|  |  |  |  |         message: `Missing required fields: ${missing.map(([k]) => k).join(", ")}` | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Validate frequency early
 | 
			
		
	
		
			
				
					|  |  |  |  |     const ALLOWED_FREQ = new Set(["daily", "weekly_once", "weekly_twice", "weekly_thrice", "weekly"]); | 
			
		
	
		
			
				
					|  |  |  |  |     if (!ALLOWED_FREQ.has(frequency)) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ | 
			
		
	
		
			
				
					|  |  |  |  |         status_code: 400, | 
			
		
	
		
			
				
					|  |  |  |  |         message: "Missing required fields" | 
			
		
	
		
			
				
					|  |  |  |  |         message: "Invalid frequency. Allowed: daily, weekly_once, weekly_twice, weekly_thrice, weekly" | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Parse numbers
 | 
			
		
	
		
			
				
					|  |  |  |  |     const cap = parseCapacity(capacity); | 
			
		
	
		
			
				
					|  |  |  |  |     const qty = parseIntSafe(quantity); | 
			
		
	
		
			
				
					|  |  |  |  |     const total_required_capacity = cap * qty; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const dates = generateDates({ frequency, start_date, end_date, weekly_count }); | 
			
		
	
		
			
				
					|  |  |  |  |     if (dates.length === 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ status_code: 400, message: "No dates generated for given inputs" }); | 
			
		
	
		
			
				
					|  |  |  |  |     if (cap <= 0 || qty <= 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ | 
			
		
	
		
			
				
					|  |  |  |  |         status_code: 400, | 
			
		
	
		
			
				
					|  |  |  |  |         message: "capacity and quantity must be positive numbers" | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Build dates
 | 
			
		
	
		
			
				
					|  |  |  |  |     let dates; | 
			
		
	
		
			
				
					|  |  |  |  |     try { | 
			
		
	
		
			
				
					|  |  |  |  |       dates = generateDates({ frequency, start_date, end_date, weekly_count }); | 
			
		
	
		
			
				
					|  |  |  |  |     } catch (e) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ | 
			
		
	
		
			
				
					|  |  |  |  |         status_code: 400, | 
			
		
	
		
			
				
					|  |  |  |  |         message: e.message || "Invalid dates" | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (!Array.isArray(dates) || dates.length === 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ | 
			
		
	
		
			
				
					|  |  |  |  |         status_code: 400, | 
			
		
	
		
			
				
					|  |  |  |  |         message: "No dates generated for the given inputs" | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Suppliers normalization
 | 
			
		
	
		
			
				
					|  |  |  |  |     const suppliers = ensureRequestedSuppliers(requested_suppliers); | 
			
		
	
		
			
				
					|  |  |  |  |     if (suppliers.length === 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ | 
			
		
	
		
			
				
					|  |  |  |  |         status_code: 400, | 
			
		
	
		
			
				
					|  |  |  |  |         message: "requested_suppliers must contain at least one supplier" | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const doc = new RecurringRequestedBooking({ | 
			
		
	
	
		
			
				
					|  |  |  | @ -2231,7 +2456,7 @@ exports.createRequestedPlanBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |       end_date, | 
			
		
	
		
			
				
					|  |  |  |  |       time, | 
			
		
	
		
			
				
					|  |  |  |  |       dates, | 
			
		
	
		
			
				
					|  |  |  |  |       requested_suppliers, | 
			
		
	
		
			
				
					|  |  |  |  |       requested_suppliers: suppliers, | 
			
		
	
		
			
				
					|  |  |  |  |       status: "pending" | 
			
		
	
		
			
				
					|  |  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -2254,3 +2479,4 @@ exports.createRequestedPlanBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | 
 |