From 1880763a8a91fa12588b09940fcf87bdae39d2c8 Mon Sep 17 00:00:00 2001 From: Varun Date: Wed, 10 Sep 2025 11:46:20 +0530 Subject: [PATCH] accept plans for supplier --- src/controllers/supplierController.js | 233 +++++++++++++++++++++++++- src/routes/supplierRoute.js | 33 +++- 2 files changed, 264 insertions(+), 2 deletions(-) diff --git a/src/controllers/supplierController.js b/src/controllers/supplierController.js index 827e2313..70e2d2ff 100644 --- a/src/controllers/supplierController.js +++ b/src/controllers/supplierController.js @@ -10,7 +10,7 @@ const saltRounds = 10; //Get the data models -const { RequestedBooking,Supplier , generateSupplierId, DeliveryBoy} = require('../models/supplier'); +const { RecurringRequestedBooking,RequestedBooking,Supplier , generateSupplierId, DeliveryBoy} = require('../models/supplier'); const { Tankerbooking} = require("../models/tankers") @@ -298,4 +298,235 @@ exports.respondToRequestedBooking = async (req, reply) => { } }; + +// controllers/supplier.controller.js +// const boom = require("@hapi/boom"); +// const mongoose = require("mongoose"); + +// // MODELS (adjust paths/names to your project) +// const RecurringRequestedBooking = require("../models/recurringRequestedBooking.model"); +// const TankerBooking = require("../models/tankerBooking.model"); + +// // Common party models you likely have in your DB: +// 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); + } +}; + + \ No newline at end of file diff --git a/src/routes/supplierRoute.js b/src/routes/supplierRoute.js index 376eabe0..4d18c391 100644 --- a/src/routes/supplierRoute.js +++ b/src/routes/supplierRoute.js @@ -746,7 +746,38 @@ fastify.route({ handler: supplierController.respondToRequestedBooking }); - + fastify.route({ + method: "POST", + url: "/api/supplier/recurring/respond/:_id", + schema: { + description: + "Supplier accepts or rejects a recurring requested booking; on accept, creates bookings for each date in the stored 'dates' array.", + tags: ["Supplier-Data"], + summary: "Supplier action on recurring requested booking", + params: { + type: "object", + properties: { + _id: { type: "string", description: "Recurring Requested Booking ID" }, + }, + required: ["_id"], + }, + body: { + type: "object", + properties: { + supplierId: { type: "string", description: "Supplier ID" }, + action: { + type: "string", + enum: ["accept", "reject"], + description: "Action to perform by supplier", + }, + }, + required: ["supplierId", "action"], + }, + security: [{ basicAuth: [] }], + }, + // preHandler: fastify.auth([fastify.authenticate]), + handler: supplierController.respondToRecurringRequestedBooking, + }); next(); }