|  |  |  | @ -311,6 +311,236 @@ exports.respondToRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  | // const Customer = require("../models/customer.model"); // e.g., { customerId, name, phone, address, latitude, longitude }
 | 
			
		
	
		
			
				
					|  |  |  |  | // const Supplier = require("../models/supplier.model"); // e.g., { supplierId, name, phone, tankerName, address, latitude, longitude }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // const parseNumber = (v, def = 0) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //   if (v === null || v === undefined) return def;
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const n = parseFloat(String(v).replace(/,/g, ""));
 | 
			
		
	
		
			
				
					|  |  |  |  | //   return Number.isFinite(n) ? n : def;
 | 
			
		
	
		
			
				
					|  |  |  |  | // };
 | 
			
		
	
		
			
				
					|  |  |  |  | // const mkBookingId = (prefix = "RBK") => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const ts = new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const rnd = Math.floor(Math.random() * 1e6).toString().padStart(6, "0");
 | 
			
		
	
		
			
				
					|  |  |  |  | //   return `${prefix}-${ts}-${rnd}`;
 | 
			
		
	
		
			
				
					|  |  |  |  | // };
 | 
			
		
	
		
			
				
					|  |  |  |  | // const isIsoYMD = (s) => /^\d{4}-\d{2}-\d{2}$/.test(s);
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // // "2025-10-21" -> "21-Oct-2025" (to match your old saved sample)
 | 
			
		
	
		
			
				
					|  |  |  |  | // const formatDDMonYYYY = (isoYmd) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //   if (!isIsoYMD(isoYmd)) return isoYmd;
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const [y, m, d] = isoYmd.split("-").map(Number);
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const mon = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][(m || 1) - 1];
 | 
			
		
	
		
			
				
					|  |  |  |  | //   return `${String(d).padStart(2,"0")}-${mon}-${y}`;
 | 
			
		
	
		
			
				
					|  |  |  |  | // };
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // exports.respondToRecurringRequestedBooking = async (req, reply) => {
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const { _id } = req.params;
 | 
			
		
	
		
			
				
					|  |  |  |  | //   const { action, supplierId } = req.body;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //   if (!mongoose.Types.ObjectId.isValid(_id)) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return reply.code(400).send({ message: "Invalid recurring requested booking ID" });
 | 
			
		
	
		
			
				
					|  |  |  |  | //   }
 | 
			
		
	
		
			
				
					|  |  |  |  | //   if (!["accept", "reject"].includes(action)) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return reply.code(400).send({ message: "Action must be 'accept' or 'reject'" });
 | 
			
		
	
		
			
				
					|  |  |  |  | //   }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //   try {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const booking = await RecurringRequestedBooking.findById(_id);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (!booking) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(404).send({ message: "Recurring requested booking not found" });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const supplierEntry = booking.requested_suppliers.find((s) => s.supplierId === supplierId);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (!supplierEntry) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(404).send({ message: "Supplier not found in this booking" });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // Update supplier response on the recurring request
 | 
			
		
	
		
			
				
					|  |  |  |  | //     supplierEntry.status = action === "accept" ? "accepted" : "rejected";
 | 
			
		
	
		
			
				
					|  |  |  |  | //     await booking.save();
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (action === "reject") {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(200).send({
 | 
			
		
	
		
			
				
					|  |  |  |  | //         status_code: 200,
 | 
			
		
	
		
			
				
					|  |  |  |  | //         message: "Recurring booking rejected by supplier successfully",
 | 
			
		
	
		
			
				
					|  |  |  |  | //         data: booking,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // ACCEPT: build per-date TankerBooking docs with rich fields
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const allDates = Array.isArray(booking.dates) ? booking.dates.filter(isIsoYMD) : [];
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (!allDates.length) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(400).send({ message: "No valid ISO dates found in booking.dates" });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // Preload related party info to fill address/phones/names
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const [customerDoc, supplierDoc] = await Promise.all([
 | 
			
		
	
		
			
				
					|  |  |  |  | //       User.findOne({ customerId: booking.customerId }).lean(),
 | 
			
		
	
		
			
				
					|  |  |  |  | //       Supplier.findOne({ supplierId: supplierId }).lean(),
 | 
			
		
	
		
			
				
					|  |  |  |  | //     ]);
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // Pull commonly needed values (tolerant defaults to match legacy)
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const customerPhone = customerDoc?.phone ?? null;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const supplierPhone = supplierDoc?.phone ?? null;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const customerName  = customerDoc?.username ?? customerDoc?.displayName ?? "";
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const supplierName  = supplierDoc?.suppliername ?? supplierDoc?.companyName ?? "";
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const tankerName    = supplierDoc?.tankerName ?? null;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const tankName      = null; // if you associate a tank per-customer, populate from your Tank model here
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const tankLocation  = null; // same as above
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // prefer customer address (your legacy sample stores a geocoded customer address)
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const address       = customerDoc?.profile.address1 ?? null;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const latitude      = customerDoc?.latitude ?? null;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const longitude     = customerDoc?.longitude ?? null;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // price: from supplier’s quoted_amount in this request (fallback null)
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const price         = (supplierEntry?.quoted_amount ?? null) !== null
 | 
			
		
	
		
			
				
					|  |  |  |  | //       ? String(supplierEntry.quoted_amount)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       : null;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // numeric fields
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const numericCapacity = parseNumber(booking.capacity);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const numericQuantity = parseNumber(booking.quantity);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const totalRequired   = Number.isFinite(booking.total_required_capacity)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       ? booking.total_required_capacity
 | 
			
		
	
		
			
				
					|  |  |  |  | //       : numericCapacity * numericQuantity;
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // dedupe check
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const existing = await Tankerbooking.find(
 | 
			
		
	
		
			
				
					|  |  |  |  | //       {
 | 
			
		
	
		
			
				
					|  |  |  |  | //         customerId: booking.customerId,
 | 
			
		
	
		
			
				
					|  |  |  |  | //         supplierId: supplierId,
 | 
			
		
	
		
			
				
					|  |  |  |  | //         date: { $in: allDates },
 | 
			
		
	
		
			
				
					|  |  |  |  | //         time: booking.time, // keep your stored time format intact
 | 
			
		
	
		
			
				
					|  |  |  |  | //       },
 | 
			
		
	
		
			
				
					|  |  |  |  | //       { date: 1 }
 | 
			
		
	
		
			
				
					|  |  |  |  | //     ).lean();
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const existingSet = new Set((existing || []).map(e => e.date));
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const newDates = allDates.filter(d => !existingSet.has(d));
 | 
			
		
	
		
			
				
					|  |  |  |  | //     if (!newDates.length) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       return reply.code(200).send({
 | 
			
		
	
		
			
				
					|  |  |  |  | //         status_code: 200,
 | 
			
		
	
		
			
				
					|  |  |  |  | //         message: "All dates already have bookings; nothing to create.",
 | 
			
		
	
		
			
				
					|  |  |  |  | //         data: { created: 0, skippedExistingDates: allDates },
 | 
			
		
	
		
			
				
					|  |  |  |  | //       });
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // ---------- BUILD ENRICHED DOCS (matches your legacy example fields) ----------
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const todayIso = new Date().toISOString().slice(0, 10);
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     const docs = newDates.map((d) => ({
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Required/IDs
 | 
			
		
	
		
			
				
					|  |  |  |  | //       customerId: booking.customerId,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       supplierId: supplierId,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       bookingid: mkBookingId("RBK"),
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Legacy display & logistics
 | 
			
		
	
		
			
				
					|  |  |  |  | //       tankName,                        // null (fill if you link tank per-customer)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       tankLocation,                    // null (fill if available)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       tankerName,                      // from Supplier if present
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Dates/times (kept both ISO & legacy formats as you showed)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       dateOfOrder: todayIso,           // "2025-09-10"
 | 
			
		
	
		
			
				
					|  |  |  |  | //       expectedDateOfDelivery: formatDDMonYYYY(d), // "21-Oct-2025" style (legacy sample)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       date: d,                         // keep ISO in `date` too
 | 
			
		
	
		
			
				
					|  |  |  |  | //       time: booking.time,              // keep your request time as-is
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Water & capacity
 | 
			
		
	
		
			
				
					|  |  |  |  | //       type_of_water: booking.type_of_water,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       typeofwater: booking.type_of_water,  // legacy field name kept too
 | 
			
		
	
		
			
				
					|  |  |  |  | //       capacity: booking.capacity,          // e.g., "100" or "10,000 L"
 | 
			
		
	
		
			
				
					|  |  |  |  | //       quantity: booking.quantity,          // string
 | 
			
		
	
		
			
				
					|  |  |  |  | //       total_required_capacity: totalRequired,
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Money / status
 | 
			
		
	
		
			
				
					|  |  |  |  | //       price: price,                    // from quoted_amount (string) or null
 | 
			
		
	
		
			
				
					|  |  |  |  | //       payment_status: "due",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       orderStatus: "accepted",
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Contacts & names
 | 
			
		
	
		
			
				
					|  |  |  |  | //       address: address,                // from customer
 | 
			
		
	
		
			
				
					|  |  |  |  | //       customerPhone: customerPhone,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       supplierPhone: supplierPhone,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       customerName: customerName,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       supplierName: supplierName,
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Delivery defaults (match your legacy doc)
 | 
			
		
	
		
			
				
					|  |  |  |  | //       delivery_agent: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       delivery_agent_mobile: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       delivery_agent_alternative_mobile: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Metering defaults
 | 
			
		
	
		
			
				
					|  |  |  |  | //       initial_water_level: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       final_water_level: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       start_time: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       stop_time: "null",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       quantityDelivered: null,
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Accounting defaults
 | 
			
		
	
		
			
				
					|  |  |  |  | //       amount_paid: null,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       amount_due: null,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       distrubance_price: "none",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       amount_difference: "none",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       payment_mode: null,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       remarks: null,
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Device/geo defaults
 | 
			
		
	
		
			
				
					|  |  |  |  | //       tankerRunningStatus: "0",
 | 
			
		
	
		
			
				
					|  |  |  |  | //       latitude: latitude ?? undefined,   // keep same field names as your legacy doc
 | 
			
		
	
		
			
				
					|  |  |  |  | //       longitude: longitude ?? undefined, // if not available, omit field
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //       // Misc you already store
 | 
			
		
	
		
			
				
					|  |  |  |  | //       frequency: booking.frequency,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       weekly_count: booking.weekly_count ?? 1,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       deliveredDate: null,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       distrubance_status: "0",
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }));
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // ---------------------------------------------------------------------------
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     // Insert without transactions, tolerate duplicates if unique index exists
 | 
			
		
	
		
			
				
					|  |  |  |  | //     let insertedCount = 0;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     let duplicateErrors = 0;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     try {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       const res = await Tankerbooking.collection.insertMany(docs, { ordered: false });
 | 
			
		
	
		
			
				
					|  |  |  |  | //       insertedCount = res.insertedCount || 0;
 | 
			
		
	
		
			
				
					|  |  |  |  | //     } catch (e) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //       if (e && e.writeErrors && Array.isArray(e.writeErrors)) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //         insertedCount = e.result?.nInserted ?? 0;
 | 
			
		
	
		
			
				
					|  |  |  |  | //         duplicateErrors = e.writeErrors.length;
 | 
			
		
	
		
			
				
					|  |  |  |  | //       } else {
 | 
			
		
	
		
			
				
					|  |  |  |  | //         throw e;
 | 
			
		
	
		
			
				
					|  |  |  |  | //       }
 | 
			
		
	
		
			
				
					|  |  |  |  | //     }
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | //     return reply.code(200).send({
 | 
			
		
	
		
			
				
					|  |  |  |  | //       status_code: 200,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       message: `Recurring booking accepted. Created ${insertedCount} tanker booking(s).`,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       data: {
 | 
			
		
	
		
			
				
					|  |  |  |  | //         createdDates: newDates.slice(0, insertedCount),
 | 
			
		
	
		
			
				
					|  |  |  |  | //         skippedExistingDates: allDates.filter(d => existingSet.has(d)),
 | 
			
		
	
		
			
				
					|  |  |  |  | //         duplicateConflicts: duplicateErrors,
 | 
			
		
	
		
			
				
					|  |  |  |  | //       },
 | 
			
		
	
		
			
				
					|  |  |  |  | //     });
 | 
			
		
	
		
			
				
					|  |  |  |  | //   } catch (err) {
 | 
			
		
	
		
			
				
					|  |  |  |  | //     console.error(err);
 | 
			
		
	
		
			
				
					|  |  |  |  | //     throw boom.internal("Failed to update recurring supplier response", err);
 | 
			
		
	
		
			
				
					|  |  |  |  | //   }
 | 
			
		
	
		
			
				
					|  |  |  |  | // };
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |    | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // controllers/supplier.controller.js (only the changed parts shown for brevity)
 | 
			
		
	
		
			
				
					|  |  |  |  | // const boom = require("@hapi/boom");
 | 
			
		
	
		
			
				
					|  |  |  |  | // const mongoose = require("mongoose");
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // const RecurringRequestedBooking = require("../models/recurringRequestedBooking.model");
 | 
			
		
	
		
			
				
					|  |  |  |  | // const TankerBooking = require("../models/tankerBooking.model");
 | 
			
		
	
		
			
				
					|  |  |  |  | // const Customer = require("../models/customer.model");
 | 
			
		
	
		
			
				
					|  |  |  |  | // const Supplier = require("../models/supplier.model");
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ---------- helpers (IST + formatting) ----------
 | 
			
		
	
		
			
				
					|  |  |  |  | const parseNumber = (v, def = 0) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (v === null || v === undefined) return def; | 
			
		
	
		
			
				
					|  |  |  |  |   const n = parseFloat(String(v).replace(/,/g, "")); | 
			
		
	
	
		
			
				
					|  |  |  | @ -322,19 +552,135 @@ const mkBookingId = (prefix = "RBK") => { | 
			
		
	
		
			
				
					|  |  |  |  |   return `${prefix}-${ts}-${rnd}`; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | const isIsoYMD = (s) => /^\d{4}-\d{2}-\d{2}$/.test(s); | 
			
		
	
		
			
				
					|  |  |  |  | const MON = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // Return a Date adjusted to IST (UTC+5:30) without changing the original instant
 | 
			
		
	
		
			
				
					|  |  |  |  | const toIST = (d = new Date()) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const utc = d.getTime() + (d.getTimezoneOffset() * 60000); | 
			
		
	
		
			
				
					|  |  |  |  |   // IST = UTC + 5:30
 | 
			
		
	
		
			
				
					|  |  |  |  |   return new Date(utc + (5 * 60 + 30) * 60000); | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // "2025-10-21" -> "21-Oct-2025" (to match your old saved sample)
 | 
			
		
	
		
			
				
					|  |  |  |  | const formatDDMonYYYY = (isoYmd) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (!isIsoYMD(isoYmd)) return isoYmd; | 
			
		
	
		
			
				
					|  |  |  |  | // Format Date -> "DD-Mon-YYYY - HH:MM" in IST
 | 
			
		
	
		
			
				
					|  |  |  |  | const fmtDDMonYYYY_HHMM = (dateObj) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const dd = String(dateObj.getDate()).padStart(2, "0"); | 
			
		
	
		
			
				
					|  |  |  |  |   const mon = MON[dateObj.getMonth()]; | 
			
		
	
		
			
				
					|  |  |  |  |   const yyyy = dateObj.getFullYear(); | 
			
		
	
		
			
				
					|  |  |  |  |   const hh = String(dateObj.getHours()).padStart(2, "0"); | 
			
		
	
		
			
				
					|  |  |  |  |   const mm = String(dateObj.getMinutes()).padStart(2, "0"); | 
			
		
	
		
			
				
					|  |  |  |  |   return `${dd}-${mon}-${yyyy} - ${hh}:${mm}`; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // Extract "HH:MM" (24h) from arbitrary string like "21-Nov-2025 - 14:37" or "4:00 PM to 6:00 PM"
 | 
			
		
	
		
			
				
					|  |  |  |  | // If not found, default "00:00"
 | 
			
		
	
		
			
				
					|  |  |  |  | const extractHHMM = (timeStr) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (!timeStr) return "00:00"; | 
			
		
	
		
			
				
					|  |  |  |  |   // First try 24h "14:37"
 | 
			
		
	
		
			
				
					|  |  |  |  |   const m24 = timeStr.match(/\b(\d{1,2}):(\d{2})\b/); | 
			
		
	
		
			
				
					|  |  |  |  |   if (m24) { | 
			
		
	
		
			
				
					|  |  |  |  |     const h = String(Math.min(23, parseInt(m24[1],10))).padStart(2,"0"); | 
			
		
	
		
			
				
					|  |  |  |  |     const m = m24[2]; | 
			
		
	
		
			
				
					|  |  |  |  |     return `${h}:${m}`; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   // crude parse for "4:00 PM" → 16:00
 | 
			
		
	
		
			
				
					|  |  |  |  |   const ampm = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i); | 
			
		
	
		
			
				
					|  |  |  |  |   if (ampm) { | 
			
		
	
		
			
				
					|  |  |  |  |     let h = parseInt(ampm[1],10); | 
			
		
	
		
			
				
					|  |  |  |  |     const m = ampm[2]; | 
			
		
	
		
			
				
					|  |  |  |  |     const p = ampm[3].toUpperCase(); | 
			
		
	
		
			
				
					|  |  |  |  |     if (p === "PM" && h !== 12) h += 12; | 
			
		
	
		
			
				
					|  |  |  |  |     if (p === "AM" && h === 12) h = 0; | 
			
		
	
		
			
				
					|  |  |  |  |     return `${String(h).padStart(2,"0")}:${m}`; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   return "00:00"; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // Combine ISO date "YYYY-MM-DD" + a time string → IST "DD-Mon-YYYY - HH:MM"
 | 
			
		
	
		
			
				
					|  |  |  |  | const fmtFromISOAndTime = (isoYmd, timeStr) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (!isIsoYMD(isoYmd)) return isoYmd; // fallback
 | 
			
		
	
		
			
				
					|  |  |  |  |   const [y, m, d] = isoYmd.split("-").map(Number); | 
			
		
	
		
			
				
					|  |  |  |  |   const mon = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][(m || 1) - 1]; | 
			
		
	
		
			
				
					|  |  |  |  |   return `${String(d).padStart(2,"0")}-${mon}-${y}`; | 
			
		
	
		
			
				
					|  |  |  |  |   const [hh, mm] = extractHHMM(timeStr).split(":").map(Number); | 
			
		
	
		
			
				
					|  |  |  |  |   // Construct a Date in UTC for that local time, then convert to IST display
 | 
			
		
	
		
			
				
					|  |  |  |  |   const dt = new Date(Date.UTC(y, (m - 1), d, hh, mm, 0)); | 
			
		
	
		
			
				
					|  |  |  |  |   // We only care about display in IST:
 | 
			
		
	
		
			
				
					|  |  |  |  |   return fmtDDMonYYYY_HHMM(toIST(dt)); | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | // ----
 | 
			
		
	
		
			
				
					|  |  |  |  | // --------------------------------------------
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ---------- add these helpers near your other helpers ----------
 | 
			
		
	
		
			
				
					|  |  |  |  | const to12h = (h24, m) => { | 
			
		
	
		
			
				
					|  |  |  |  |   let h = h24 % 12 || 12; | 
			
		
	
		
			
				
					|  |  |  |  |   const ampm = h24 >= 12 ? "PM" : "AM"; | 
			
		
	
		
			
				
					|  |  |  |  |   return `${h}:${String(m).padStart(2,"0")} ${ampm}`; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // Extract first time from any string: supports "14:37", "4:00 PM", "21-Nov-2025 - 14:37", "4:00 PM to 6:00 PM"
 | 
			
		
	
		
			
				
					|  |  |  |  | const parseFirstTimeToHM = (timeStr) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (!timeStr) return { h: 0, m: 0 }; | 
			
		
	
		
			
				
					|  |  |  |  |   // Try explicit 12h with AM/PM
 | 
			
		
	
		
			
				
					|  |  |  |  |   const ampm = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i); | 
			
		
	
		
			
				
					|  |  |  |  |   if (ampm) { | 
			
		
	
		
			
				
					|  |  |  |  |     let h = parseInt(ampm[1], 10); | 
			
		
	
		
			
				
					|  |  |  |  |     const m = parseInt(ampm[2], 10); | 
			
		
	
		
			
				
					|  |  |  |  |     const p = ampm[3].toUpperCase(); | 
			
		
	
		
			
				
					|  |  |  |  |     if (p === "PM" && h !== 12) h += 12; | 
			
		
	
		
			
				
					|  |  |  |  |     if (p === "AM" && h === 12) h = 0; | 
			
		
	
		
			
				
					|  |  |  |  |     return { h, m }; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   // Try any 24h HH:MM in the string
 | 
			
		
	
		
			
				
					|  |  |  |  |   const m24 = timeStr.match(/\b(\d{1,2}):(\d{2})\b/); | 
			
		
	
		
			
				
					|  |  |  |  |   if (m24) { | 
			
		
	
		
			
				
					|  |  |  |  |     const h = Math.max(0, Math.min(23, parseInt(m24[1], 10))); | 
			
		
	
		
			
				
					|  |  |  |  |     const m = Math.max(0, Math.min(59, parseInt(m24[2], 10))); | 
			
		
	
		
			
				
					|  |  |  |  |     return { h, m }; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   return { h: 0, m: 0 }; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // If already "X to Y" (case-insensitive), keep it. Otherwise, make a slot of `windowHours` starting at first parsed time.
 | 
			
		
	
		
			
				
					|  |  |  |  | const normalizeTimeForLegacy = (timeStr, windowHours = 2) => { | 
			
		
	
		
			
				
					|  |  |  |  |   if (typeof timeStr === "string" && / to /i.test(timeStr)) { | 
			
		
	
		
			
				
					|  |  |  |  |     return timeStr; // already in "4:00 PM to 6:00 PM"
 | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   const { h, m } = parseFirstTimeToHM(timeStr); | 
			
		
	
		
			
				
					|  |  |  |  |   const start = to12h(h, m); | 
			
		
	
		
			
				
					|  |  |  |  |   const endDate = new Date(Date.UTC(2000, 0, 1, h, m)); // dummy date, add hours
 | 
			
		
	
		
			
				
					|  |  |  |  |   endDate.setUTCHours(endDate.getUTCHours() + windowHours); | 
			
		
	
		
			
				
					|  |  |  |  |   const end = to12h(endDate.getUTCHours(), endDate.getUTCMinutes()); | 
			
		
	
		
			
				
					|  |  |  |  |   return `${start} to ${end}`; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ---- Booking ID helpers (IST-based) ----
 | 
			
		
	
		
			
				
					|  |  |  |  | // Get YYYYMMDD in IST (UTC+05:30)
 | 
			
		
	
		
			
				
					|  |  |  |  | const getISTDatePart = (d = new Date()) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const utcMs = d.getTime() + d.getTimezoneOffset() * 60000; | 
			
		
	
		
			
				
					|  |  |  |  |   const ist = new Date(utcMs + (5 * 60 + 30) * 60000); | 
			
		
	
		
			
				
					|  |  |  |  |   const y = ist.getFullYear(); | 
			
		
	
		
			
				
					|  |  |  |  |   const m = String(ist.getMonth() + 1).padStart(2, "0"); | 
			
		
	
		
			
				
					|  |  |  |  |   const day = String(ist.getDate()).padStart(2, "0"); | 
			
		
	
		
			
				
					|  |  |  |  |   return `${y}${m}${day}`; // YYYYMMDD
 | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // Make ARM id with one random digit (0-9)
 | 
			
		
	
		
			
				
					|  |  |  |  | const mkArmBookingIdIST = () => `ARM${getISTDatePart()}${Math.floor(Math.random() * 10)}`; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // Ensure uniqueness in DB (recommended since you may create many in one day)
 | 
			
		
	
		
			
				
					|  |  |  |  | const generateUniqueArmId = async () => { | 
			
		
	
		
			
				
					|  |  |  |  |   // up to 20 tries with single digit; then fall back to two digits
 | 
			
		
	
		
			
				
					|  |  |  |  |   for (let i = 0; i < 20; i++) { | 
			
		
	
		
			
				
					|  |  |  |  |     const id = mkArmBookingIdIST(); | 
			
		
	
		
			
				
					|  |  |  |  |     const exists = await Tankerbooking.exists({ bookingid: id }); | 
			
		
	
		
			
				
					|  |  |  |  |     if (!exists) return id; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   // fallback with two random digits to avoid collisions
 | 
			
		
	
		
			
				
					|  |  |  |  |   const fallback = `ARM${getISTDatePart()}${Math.floor(Math.random()*10)}${Math.floor(Math.random()*10)}`; | 
			
		
	
		
			
				
					|  |  |  |  |   return fallback; | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | exports.respondToRecurringRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |   const { _id } = req.params; | 
			
		
	
		
			
				
					|  |  |  |  |   const { action, supplierId } = req.body; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (!mongoose.Types.ObjectId.isValid(_id)) { | 
			
		
	
		
			
				
					|  |  |  |  |     return reply.code(400).send({ message: "Invalid recurring requested booking ID" }); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
	
		
			
				
					|  |  |  | @ -344,16 +690,13 @@ exports.respondToRecurringRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   try { | 
			
		
	
		
			
				
					|  |  |  |  |     const booking = await RecurringRequestedBooking.findById(_id); | 
			
		
	
		
			
				
					|  |  |  |  |     if (!booking) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(404).send({ message: "Recurring requested booking not found" }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     if (!booking) return reply.code(404).send({ message: "Recurring requested booking not found" }); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const supplierEntry = booking.requested_suppliers.find((s) => s.supplierId === supplierId); | 
			
		
	
		
			
				
					|  |  |  |  |     const supplierEntry = booking.requested_suppliers.find(s => s.supplierId === supplierId); | 
			
		
	
		
			
				
					|  |  |  |  |     if (!supplierEntry) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(404).send({ message: "Supplier not found in this booking" }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Update supplier response on the recurring request
 | 
			
		
	
		
			
				
					|  |  |  |  |     supplierEntry.status = action === "accept" ? "accepted" : "rejected"; | 
			
		
	
		
			
				
					|  |  |  |  |     await booking.save(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -365,57 +708,40 @@ exports.respondToRecurringRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // ACCEPT: build per-date TankerBooking docs with rich fields
 | 
			
		
	
		
			
				
					|  |  |  |  |     const allDates = Array.isArray(booking.dates) ? booking.dates.filter(isIsoYMD) : []; | 
			
		
	
		
			
				
					|  |  |  |  |     if (!allDates.length) { | 
			
		
	
		
			
				
					|  |  |  |  |       return reply.code(400).send({ message: "No valid ISO dates found in booking.dates" }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Preload related party info to fill address/phones/names
 | 
			
		
	
		
			
				
					|  |  |  |  |     const [customerDoc, supplierDoc] = await Promise.all([ | 
			
		
	
		
			
				
					|  |  |  |  |       User.findOne({ customerId: booking.customerId }).lean(), | 
			
		
	
		
			
				
					|  |  |  |  |       Supplier.findOne({ supplierId: supplierId }).lean(), | 
			
		
	
		
			
				
					|  |  |  |  |       Supplier.findOne({ supplierId }).lean(), | 
			
		
	
		
			
				
					|  |  |  |  |     ]); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Pull commonly needed values (tolerant defaults to match legacy)
 | 
			
		
	
		
			
				
					|  |  |  |  |     const customerPhone = customerDoc?.phone ?? null; | 
			
		
	
		
			
				
					|  |  |  |  |     const supplierPhone = supplierDoc?.phone ?? null; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const customerName  = customerDoc?.username ?? customerDoc?.displayName ?? ""; | 
			
		
	
		
			
				
					|  |  |  |  |     const supplierName  = supplierDoc?.suppliername ?? supplierDoc?.companyName ?? ""; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const tankerName    = supplierDoc?.tankerName ?? null; | 
			
		
	
		
			
				
					|  |  |  |  |     const tankName      = null; // if you associate a tank per-customer, populate from your Tank model here
 | 
			
		
	
		
			
				
					|  |  |  |  |     const tankLocation  = null; // same as above
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // prefer customer address (your legacy sample stores a geocoded customer address)
 | 
			
		
	
		
			
				
					|  |  |  |  |     const address       = customerDoc?.profile.address1 ?? null; | 
			
		
	
		
			
				
					|  |  |  |  |     const latitude      = customerDoc?.latitude ?? null; | 
			
		
	
		
			
				
					|  |  |  |  |     const longitude     = customerDoc?.longitude ?? null; | 
			
		
	
		
			
				
					|  |  |  |  |     const latitude      = customerDoc?.latitude ?? undefined; | 
			
		
	
		
			
				
					|  |  |  |  |     const longitude     = customerDoc?.longitude ?? undefined; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // price: from supplier’s quoted_amount in this request (fallback null)
 | 
			
		
	
		
			
				
					|  |  |  |  |     const price         = (supplierEntry?.quoted_amount ?? null) !== null | 
			
		
	
		
			
				
					|  |  |  |  |     const price = (supplierEntry?.quoted_amount ?? null) !== null | 
			
		
	
		
			
				
					|  |  |  |  |       ? String(supplierEntry.quoted_amount) | 
			
		
	
		
			
				
					|  |  |  |  |       : null; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // numeric fields
 | 
			
		
	
		
			
				
					|  |  |  |  |     const numericCapacity = parseNumber(booking.capacity); | 
			
		
	
		
			
				
					|  |  |  |  |     const numericQuantity = parseNumber(booking.quantity); | 
			
		
	
		
			
				
					|  |  |  |  |     const totalRequired   = Number.isFinite(booking.total_required_capacity) | 
			
		
	
		
			
				
					|  |  |  |  |     const totalRequired = Number.isFinite(booking.total_required_capacity) | 
			
		
	
		
			
				
					|  |  |  |  |       ? booking.total_required_capacity | 
			
		
	
		
			
				
					|  |  |  |  |       : numericCapacity * numericQuantity; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // dedupe check
 | 
			
		
	
		
			
				
					|  |  |  |  |     // dedupe by (customerId, supplierId, date, time)
 | 
			
		
	
		
			
				
					|  |  |  |  |     const existing = await Tankerbooking.find( | 
			
		
	
		
			
				
					|  |  |  |  |       { | 
			
		
	
		
			
				
					|  |  |  |  |         customerId: booking.customerId, | 
			
		
	
		
			
				
					|  |  |  |  |         supplierId: supplierId, | 
			
		
	
		
			
				
					|  |  |  |  |         date: { $in: allDates }, | 
			
		
	
		
			
				
					|  |  |  |  |         time: booking.time, // keep your stored time format intact
 | 
			
		
	
		
			
				
					|  |  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |  |       { customerId: booking.customerId, supplierId, date: { $in: allDates }, time: booking.time }, | 
			
		
	
		
			
				
					|  |  |  |  |       { date: 1 } | 
			
		
	
		
			
				
					|  |  |  |  |     ).lean(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const existingSet = new Set((existing || []).map(e => e.date)); | 
			
		
	
		
			
				
					|  |  |  |  |     const newDates = allDates.filter(d => !existingSet.has(d)); | 
			
		
	
		
			
				
					|  |  |  |  |     if (!newDates.length) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -425,59 +751,61 @@ exports.respondToRecurringRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |         data: { created: 0, skippedExistingDates: allDates }, | 
			
		
	
		
			
				
					|  |  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // ---------- BUILD ENRICHED DOCS (matches your legacy example fields) ----------
 | 
			
		
	
		
			
				
					|  |  |  |  |     const todayIso = new Date().toISOString().slice(0, 10); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const docs = newDates.map((d) => ({ | 
			
		
	
		
			
				
					|  |  |  |  |       // Required/IDs
 | 
			
		
	
		
			
				
					|  |  |  |  |     const legacyTime = normalizeTimeForLegacy(booking.time);  | 
			
		
	
		
			
				
					|  |  |  |  |     // --- FORMAT THESE THREE FIELDS EXACTLY AS LEGACY EXPECTS ---
 | 
			
		
	
		
			
				
					|  |  |  |  |     const nowIST = toIST(new Date()); | 
			
		
	
		
			
				
					|  |  |  |  |     const dateOfOrderFmt = fmtDDMonYYYY_HHMM(nowIST); // "DD-Mon-YYYY - HH:MM"
 | 
			
		
	
		
			
				
					|  |  |  |  |     const today = new Date(); | 
			
		
	
		
			
				
					|  |  |  |  | const datePart = today.toISOString().slice(0, 10).replace(/-/g, ''); // YYYYMMDD
 | 
			
		
	
		
			
				
					|  |  |  |  | const randomDigit = Math.floor(Math.random() * 10); // 0–9
 | 
			
		
	
		
			
				
					|  |  |  |  | const bookingId = `ARM${datePart}${randomDigit}`; | 
			
		
	
		
			
				
					|  |  |  |  |     // ADD: pre-generate unique booking IDs (one per date)
 | 
			
		
	
		
			
				
					|  |  |  |  | const bookingIds = await Promise.all(newDates.map(() => generateUniqueArmId())); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // -----------------------------------------------------------
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const docs = newDates.map((d,i) => ({ | 
			
		
	
		
			
				
					|  |  |  |  |       // IDs
 | 
			
		
	
		
			
				
					|  |  |  |  |       customerId: booking.customerId, | 
			
		
	
		
			
				
					|  |  |  |  |       supplierId: supplierId, | 
			
		
	
		
			
				
					|  |  |  |  |       bookingid: mkBookingId("RBK"), | 
			
		
	
		
			
				
					|  |  |  |  |       supplierId, | 
			
		
	
		
			
				
					|  |  |  |  |       bookingid: bookingIds[i],         | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Legacy display & logistics
 | 
			
		
	
		
			
				
					|  |  |  |  |       tankName,                        // null (fill if you link tank per-customer)
 | 
			
		
	
		
			
				
					|  |  |  |  |       tankLocation,                    // null (fill if available)
 | 
			
		
	
		
			
				
					|  |  |  |  |       tankerName,                      // from Supplier if present
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Legacy fields (match your old document)
 | 
			
		
	
		
			
				
					|  |  |  |  |       tankName: null, | 
			
		
	
		
			
				
					|  |  |  |  |       tankLocation: null, | 
			
		
	
		
			
				
					|  |  |  |  |       tankerName, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Dates/times (kept both ISO & legacy formats as you showed)
 | 
			
		
	
		
			
				
					|  |  |  |  |       dateOfOrder: todayIso,           // "2025-09-10"
 | 
			
		
	
		
			
				
					|  |  |  |  |       expectedDateOfDelivery: formatDDMonYYYY(d), // "21-Oct-2025" style (legacy sample)
 | 
			
		
	
		
			
				
					|  |  |  |  |       date: d,                         // keep ISO in `date` too
 | 
			
		
	
		
			
				
					|  |  |  |  |       time: booking.time,              // keep your request time as-is
 | 
			
		
	
		
			
				
					|  |  |  |  |       dateOfOrder: dateOfOrderFmt,                          // e.g., "03-Sep-2025 - 13:25"
 | 
			
		
	
		
			
				
					|  |  |  |  |       expectedDateOfDelivery: fmtFromISOAndTime(d, booking.time), // "DD-Mon-YYYY - HH:MM"
 | 
			
		
	
		
			
				
					|  |  |  |  |       date: d,                                              // keep ISO for backend logic
 | 
			
		
	
		
			
				
					|  |  |  |  |       time: legacyTime,                               // keep whatever UI sent
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Water & capacity
 | 
			
		
	
		
			
				
					|  |  |  |  |       type_of_water: booking.type_of_water, | 
			
		
	
		
			
				
					|  |  |  |  |       typeofwater: booking.type_of_water,  // legacy field name kept too
 | 
			
		
	
		
			
				
					|  |  |  |  |       capacity: booking.capacity,          // e.g., "100" or "10,000 L"
 | 
			
		
	
		
			
				
					|  |  |  |  |       quantity: booking.quantity,          // string
 | 
			
		
	
		
			
				
					|  |  |  |  |       typeofwater: booking.type_of_water, | 
			
		
	
		
			
				
					|  |  |  |  |       capacity: booking.capacity, | 
			
		
	
		
			
				
					|  |  |  |  |       quantity: booking.quantity, | 
			
		
	
		
			
				
					|  |  |  |  |       total_required_capacity: totalRequired, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Money / status
 | 
			
		
	
		
			
				
					|  |  |  |  |       price: price,                    // from quoted_amount (string) or null
 | 
			
		
	
		
			
				
					|  |  |  |  |       price, | 
			
		
	
		
			
				
					|  |  |  |  |       payment_status: "due", | 
			
		
	
		
			
				
					|  |  |  |  |       orderStatus: "accepted", | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Contacts & names
 | 
			
		
	
		
			
				
					|  |  |  |  |       address: address,                // from customer
 | 
			
		
	
		
			
				
					|  |  |  |  |       customerPhone: customerPhone, | 
			
		
	
		
			
				
					|  |  |  |  |       supplierPhone: supplierPhone, | 
			
		
	
		
			
				
					|  |  |  |  |       customerName: customerName, | 
			
		
	
		
			
				
					|  |  |  |  |       supplierName: supplierName, | 
			
		
	
		
			
				
					|  |  |  |  |       address, | 
			
		
	
		
			
				
					|  |  |  |  |       customerPhone, | 
			
		
	
		
			
				
					|  |  |  |  |       supplierPhone, | 
			
		
	
		
			
				
					|  |  |  |  |       customerName, | 
			
		
	
		
			
				
					|  |  |  |  |       supplierName, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Delivery defaults (match your legacy doc)
 | 
			
		
	
		
			
				
					|  |  |  |  |       delivery_agent: "null", | 
			
		
	
		
			
				
					|  |  |  |  |       delivery_agent_mobile: "null", | 
			
		
	
		
			
				
					|  |  |  |  |       delivery_agent_alternative_mobile: "null", | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Metering defaults
 | 
			
		
	
		
			
				
					|  |  |  |  |       initial_water_level: "null", | 
			
		
	
		
			
				
					|  |  |  |  |       final_water_level: "null", | 
			
		
	
		
			
				
					|  |  |  |  |       start_time: "null", | 
			
		
	
		
			
				
					|  |  |  |  |       stop_time: "null", | 
			
		
	
		
			
				
					|  |  |  |  |       quantityDelivered: null, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Accounting defaults
 | 
			
		
	
		
			
				
					|  |  |  |  |       amount_paid: null, | 
			
		
	
		
			
				
					|  |  |  |  |       amount_due: null, | 
			
		
	
		
			
				
					|  |  |  |  |       distrubance_price: "none", | 
			
		
	
	
		
			
				
					|  |  |  | @ -485,20 +813,17 @@ exports.respondToRecurringRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |       payment_mode: null, | 
			
		
	
		
			
				
					|  |  |  |  |       remarks: null, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Device/geo defaults
 | 
			
		
	
		
			
				
					|  |  |  |  |       tankerRunningStatus: "0", | 
			
		
	
		
			
				
					|  |  |  |  |       latitude: latitude ?? undefined,   // keep same field names as your legacy doc
 | 
			
		
	
		
			
				
					|  |  |  |  |       longitude: longitude ?? undefined, // if not available, omit field
 | 
			
		
	
		
			
				
					|  |  |  |  |       latitude, | 
			
		
	
		
			
				
					|  |  |  |  |       longitude, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // Misc you already store
 | 
			
		
	
		
			
				
					|  |  |  |  |       frequency: booking.frequency, | 
			
		
	
		
			
				
					|  |  |  |  |       weekly_count: booking.weekly_count ?? 1, | 
			
		
	
		
			
				
					|  |  |  |  |       deliveredDate: null, | 
			
		
	
		
			
				
					|  |  |  |  |       deliveredDate: null,                 // new bookings: not delivered yet
 | 
			
		
	
		
			
				
					|  |  |  |  |       distrubance_status: "0", | 
			
		
	
		
			
				
					|  |  |  |  |     })); | 
			
		
	
		
			
				
					|  |  |  |  |     // ---------------------------------------------------------------------------
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // Insert without transactions, tolerate duplicates if unique index exists
 | 
			
		
	
		
			
				
					|  |  |  |  |     // insert (no transactions, ordered:false)
 | 
			
		
	
		
			
				
					|  |  |  |  |     let insertedCount = 0; | 
			
		
	
		
			
				
					|  |  |  |  |     let duplicateErrors = 0; | 
			
		
	
		
			
				
					|  |  |  |  |     try { | 
			
		
	
	
		
			
				
					|  |  |  | @ -527,6 +852,3 @@ exports.respondToRecurringRequestedBooking = async (req, reply) => { | 
			
		
	
		
			
				
					|  |  |  |  |     throw boom.internal("Failed to update recurring supplier response", err); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |    |