|
|
|
|
const fastify = require("fastify")({
|
|
|
|
|
logger: true,
|
|
|
|
|
});
|
|
|
|
|
const boom = require("boom");
|
|
|
|
|
const customJwtAuth = require("../customAuthJwt");
|
|
|
|
|
|
|
|
|
|
const bcrypt = require("bcrypt");
|
|
|
|
|
const saltRounds = 10;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Get the data models
|
|
|
|
|
const { RecurringRequestedBooking,RequestedBooking,Supplier , generateSupplierId, DeliveryBoy} = require('../models/supplier');
|
|
|
|
|
|
|
|
|
|
const { Tankerbooking} = require("../models/tankers")
|
|
|
|
|
|
|
|
|
|
// Get Data Models
|
|
|
|
|
const { User,Counter, generateBookingId,resetCounter,generateCustomerId,ProfilePicture, AddTeamMembers,Cart} = require('../models/User')
|
|
|
|
|
async function bcryptPassword(password) {
|
|
|
|
|
encryptedPwd = bcrypt.hash(password, saltRounds);
|
|
|
|
|
return encryptedPwd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function bcryptComparePassword(pwd, encpassword) {
|
|
|
|
|
isSame = bcrypt.compare(pwd, encpassword);
|
|
|
|
|
return isSame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Supplier Login Controller
|
|
|
|
|
|
|
|
|
|
exports.loginSupplier = async (req) => {
|
|
|
|
|
try {
|
|
|
|
|
const phone = req.body.phone;
|
|
|
|
|
const password = req.body.password;
|
|
|
|
|
|
|
|
|
|
const supplier = await Supplier.findOne({ phone: phone });
|
|
|
|
|
// compare supplier password with what is supplied
|
|
|
|
|
if (supplier) {
|
|
|
|
|
isSame = await bcryptComparePassword(
|
|
|
|
|
password,
|
|
|
|
|
supplier.services.password.bcrypt
|
|
|
|
|
);
|
|
|
|
|
// if password supplied matches return object
|
|
|
|
|
if (isSame) {
|
|
|
|
|
return { same: true, supplier: supplier };
|
|
|
|
|
} else {
|
|
|
|
|
return { same: false };
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return { same: false };
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw boom.boomify(err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//DeliveryBoy Login Controller
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.loginDeliveryBoy = async (req) => {
|
|
|
|
|
try {
|
|
|
|
|
const phone = req.body.phone;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const delivery = await DeliveryBoy.findOne({ phone: phone });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (delivery) {
|
|
|
|
|
return { same: true, delivery: delivery };
|
|
|
|
|
} else {
|
|
|
|
|
return { same: false };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw boom.boomify(err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
exports.addSupplier = async (req, reply) => {
|
|
|
|
|
try {
|
|
|
|
|
// await resetCounter();//to set customer id back to 0
|
|
|
|
|
var s_id = await generateSupplierId()
|
|
|
|
|
var building= ((req.body.suppliername).slice(0, 3)).toUpperCase();
|
|
|
|
|
var supplier_id = `AWSS${building}${s_id}`
|
|
|
|
|
// console.log("This is the reply in the handler after the validations", reply);
|
|
|
|
|
s_data = {
|
|
|
|
|
supplierId: supplier_id,
|
|
|
|
|
suppliername: req.body.suppliername,
|
|
|
|
|
emails: req.body.emails,
|
|
|
|
|
password: req.body.password,
|
|
|
|
|
phone: req.body.phone,
|
|
|
|
|
description: req.body.description,
|
|
|
|
|
bussinessname: req.body.description,
|
|
|
|
|
registration_number: req.body.description,
|
|
|
|
|
years_in_business: req.body.description,
|
|
|
|
|
profile: {
|
|
|
|
|
firstName: req.body.firstName,
|
|
|
|
|
lastName: req.body.lastName,
|
|
|
|
|
contactNumber: req.body.phone,
|
|
|
|
|
alternativeContactNumber: req.body.alternativeContactNumber,
|
|
|
|
|
office_address: req.body.office_address,
|
|
|
|
|
country: req.body.country,
|
|
|
|
|
state: req.body.state,
|
|
|
|
|
city: req.body.city,
|
|
|
|
|
office_adress: req.body.office_adress,
|
|
|
|
|
zip: req.body.zip,
|
|
|
|
|
},
|
|
|
|
|
latitude: req.body.latitude,
|
|
|
|
|
longitude: req.body.longitude,
|
|
|
|
|
fcmId : req.body.fcmId
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var supplier = new Supplier(s_data);
|
|
|
|
|
|
|
|
|
|
//password is not at the top level in the collection.
|
|
|
|
|
supplierpass = req.body.password;
|
|
|
|
|
|
|
|
|
|
// If fields are sent via form encoding , capture the fields and assign them to the supplier Object.
|
|
|
|
|
|
|
|
|
|
checkFormEncoding = isSupplierFormUrlEncoded(req);
|
|
|
|
|
if (checkFormEncoding.isSupplierFormUrlEncoded) {
|
|
|
|
|
suppliertobeInserted = checkFormEncoding.supplier;
|
|
|
|
|
console.log("thsi true url string");
|
|
|
|
|
supplier.suppliername = suppliertobeInserted.suppliername;
|
|
|
|
|
supplier.firstName = suppliertobeInserted.firstName;
|
|
|
|
|
supplier.lastName = suppliertobeInserted.lastName;
|
|
|
|
|
supplier.phone = suppliertobeInserted.phone;
|
|
|
|
|
supplier.emails = suppliertobeInserted.emails;
|
|
|
|
|
supplier.passsword = suppliertobeInserted.password;
|
|
|
|
|
supplier.supplierId = suppliertobeInserted.supplier_id;
|
|
|
|
|
supplier.office_adress = suppliertobeInserted.office_adress;
|
|
|
|
|
supplier.alternativeContactNumber = suppliertobeInserted.alternativeContactNumber;
|
|
|
|
|
supplier.latitude = suppliertobeInserted.latitude;
|
|
|
|
|
supplier.longitude = suppliertobeInserted.longitude;
|
|
|
|
|
supplier.fcmId = suppliertobeInserted.fcmId
|
|
|
|
|
supplier.description = suppliertobeInserted.description
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log("---------checkurl ecnoded string-----------------------");
|
|
|
|
|
|
|
|
|
|
// Store hash in your password DB.
|
|
|
|
|
hash = await bcryptPassword(supplierpass);
|
|
|
|
|
|
|
|
|
|
if (hash) {
|
|
|
|
|
supplier.services.password.bcrypt = hash;
|
|
|
|
|
if (req.body.role) {
|
|
|
|
|
supplier.profile.role = req.body.role;
|
|
|
|
|
console.log("******************************************************");
|
|
|
|
|
console.log(supplier);
|
|
|
|
|
} else {
|
|
|
|
|
role = ["supplier"];
|
|
|
|
|
supplier.profile.role = role;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
insertedSupplier = await supplier.save();
|
|
|
|
|
console.log(insertedSupplier);
|
|
|
|
|
if (insertedSupplier) {
|
|
|
|
|
// Prepare supplier object and wrap it inside the armintatankdata
|
|
|
|
|
var retSupplier = {
|
|
|
|
|
armintatankdata: {
|
|
|
|
|
suppliername: insertedSupplier.suppliername,
|
|
|
|
|
phone: insertedSupplier.phone,
|
|
|
|
|
supplierId: insertedSupplier.supplierId,
|
|
|
|
|
office_adress: insertedSupplier.office_adress,
|
|
|
|
|
emails: [
|
|
|
|
|
{
|
|
|
|
|
email: insertedSupplier.emails[0].email,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
profile: insertedSupplier.profile,
|
|
|
|
|
latitude: insertedSupplier.latitude,
|
|
|
|
|
longitude: insertedSupplier.longitude,
|
|
|
|
|
fcmId : insertedSupplier.fcmId,
|
|
|
|
|
description : insertedSupplier.description
|
|
|
|
|
},
|
|
|
|
|
status_code: 200,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return retSupplier;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw boom.boomify(err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
exports.editCuurentSupplierInfo = async (req, reply) => {
|
|
|
|
|
try {
|
|
|
|
|
const { supplierId } = req.params;
|
|
|
|
|
const supplierInfo = await Supplier.findOne({ supplierId: supplierId.toString() });
|
|
|
|
|
const updateData = req.body;
|
|
|
|
|
|
|
|
|
|
if (updateData.firstName) supplierInfo.profile.firstName = updateData.firstName;
|
|
|
|
|
if (updateData.lastName) supplierInfo.profile.lastName = updateData.lastName;
|
|
|
|
|
if (updateData.suppliername) supplierInfo.suppliername = updateData.suppliername;
|
|
|
|
|
if (updateData.phone) supplierInfo.profile.contactNumber = updateData.phone;
|
|
|
|
|
if (updateData.office_address) supplierInfo.profile.office_address = updateData.office_address;
|
|
|
|
|
if (updateData.alternativeContactNumber) supplierInfo.profile.alternativeContactNumber = updateData.alternativeContactNumber;
|
|
|
|
|
if (updateData.city) supplierInfo.profile.city = updateData.city;
|
|
|
|
|
if (updateData.state) supplierInfo.profile.state = updateData.state;
|
|
|
|
|
if (updateData.country) supplierInfo.profile.country = updateData.country;
|
|
|
|
|
if (updateData.zip) supplierInfo.profile.zip = updateData.zip;
|
|
|
|
|
if (updateData.phone) supplierInfo.phone = updateData.phone;
|
|
|
|
|
if (updateData.description) supplierInfo.description = updateData.description;
|
|
|
|
|
if (updateData.startingPrice) supplierInfo.startingPrice = updateData.startingPrice;
|
|
|
|
|
if (updateData.status) supplierInfo.status = updateData.status;
|
|
|
|
|
if (updateData.emails) supplierInfo.emails = updateData.emails;
|
|
|
|
|
console.log(supplierInfo.emails[0].email)
|
|
|
|
|
if (updateData.role) supplierInfo.profile.role = updateData.role;
|
|
|
|
|
|
|
|
|
|
if (updateData.phone) {
|
|
|
|
|
const phoneNumber = updateData.phone //libphonenumberjs.parsePhoneNumber(updateData.phone);
|
|
|
|
|
if (phoneNumber) {
|
|
|
|
|
// access returned collection
|
|
|
|
|
if (!phoneNumber) { //if (!phoneNumber.isValid()) {
|
|
|
|
|
error = {
|
|
|
|
|
armintatankdata: {
|
|
|
|
|
error: true,
|
|
|
|
|
code: 10002,
|
|
|
|
|
message:
|
|
|
|
|
"10002 - Phone # " +
|
|
|
|
|
updateData.phone +
|
|
|
|
|
" is not a valid phone number",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
req.body.regError = error;
|
|
|
|
|
reply.status(406).send(error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (supplierInfo.phone == updateData.phone) {
|
|
|
|
|
console.log("IF++++++++++++++=");
|
|
|
|
|
supplierInfo.phone = updateData.phone;
|
|
|
|
|
supplierInfo.phoneVerified = true;
|
|
|
|
|
} else {
|
|
|
|
|
console.log("Ilse++++++++++++++=");
|
|
|
|
|
supplierInfo.phone = updateData.phone;
|
|
|
|
|
supplierInfo.phoneVerified = false;
|
|
|
|
|
}
|
|
|
|
|
const supplier = await supplierInfo.save();
|
|
|
|
|
return supplier;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw boom.boomify(err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const mongoose = require('mongoose');
|
|
|
|
|
|
|
|
|
|
exports.respondToRequestedBooking = 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 requested booking ID" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!["accept", "reject"].includes(action)) {
|
|
|
|
|
return reply.code(400).send({ message: "Action must be 'accept' or 'reject'" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const booking = await RequestedBooking.findById(_id);
|
|
|
|
|
|
|
|
|
|
if (!booking) {
|
|
|
|
|
return reply.code(404).send({ message: "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 custom_field (status) for that supplier
|
|
|
|
|
supplierEntry.status = action === "accept" ? "accepted_by_supplier" : "rejected_by_supplier";
|
|
|
|
|
|
|
|
|
|
await booking.save();
|
|
|
|
|
|
|
|
|
|
return reply.code(200).send({
|
|
|
|
|
status_code: 200,
|
|
|
|
|
message: `Booking ${action}ed by supplier successfully`,
|
|
|
|
|
data: booking
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(err);
|
|
|
|
|
throw boom.internal("Failed to update supplier response", err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|