ashok 1 month ago
commit d7194027fe

@ -1929,46 +1929,135 @@ exports.getSuppliersForPlanSearch = async (req, reply) => {
type_of_water,
capacity: requestedCapacityStr,
quantity: requestedQuantityStr,
// frequency, start_date, end_date are provided by UI but not used for filtering now
frequency, start_date, end_date, // currently not used to filter suppliers
// new filters
radius_from, radius_to,
rating_from, rating_to,
price_from, price_to,
pump
} = req.body;
// helpers inside function (per your preference)
const parseCapacity = (v) => parseFloat((v || "0").toString().replace(/,/g, "")) || 0;
// ---- helpers (kept inside as you prefer) ----
const parseFloatSafe = (v) => {
const n = parseFloat((v ?? "").toString().replace(/,/g, ""));
return Number.isFinite(n) ? n : NaN;
};
const parseIntSafe = (v) => {
const n = parseInt((v ?? "").toString().replace(/,/g, ""), 10);
return Number.isFinite(n) ? n : NaN;
};
const isValid = (n) => Number.isFinite(n);
const inRange = (n, from, to) =>
(!isValid(from) || n >= from) && (!isValid(to) || n <= to);
const normalizePump = (val) => {
if (val == null) return undefined;
const s = String(val).trim().toLowerCase();
if (["1","true","yes","y"].includes(s)) return true;
if (["0","false","no","n"].includes(s)) return false;
return undefined; // ignore if unknown
};
const requestedCapacity = parseCapacity(requestedCapacityStr);
const requestedQuantity = parseInt((requestedQuantityStr || "0").toString(), 10) || 0;
const parseLatLng = (raw) => {
// supports: "17.38,78.49" | {lat: 17.38, lng: 78.49} | [17.38, 78.49]
if (!raw) return null;
try {
if (typeof raw === "string") {
const parts = raw.split(",").map(x => parseFloat(x.trim()));
if (parts.length === 2 && parts.every(Number.isFinite)) return { lat: parts[0], lng: parts[1] };
// try JSON
const j = JSON.parse(raw);
return parseLatLng(j);
}
if (Array.isArray(raw) && raw.length === 2) {
const [lat, lng] = raw.map(Number);
if (Number.isFinite(lat) && Number.isFinite(lng)) return { lat, lng };
}
if (typeof raw === "object" && raw !== null) {
const lat = parseFloat(raw.lat ?? raw.latitude);
const lng = parseFloat(raw.lng ?? raw.lon ?? raw.longitude);
if (Number.isFinite(lat) && Number.isFinite(lng)) return { lat, lng };
}
} catch (_) {}
return null;
};
const haversineKm = (a, b) => {
const R = 6371;
const dLat = (b.lat - a.lat) * Math.PI / 180;
const dLng = (b.lng - a.lng) * Math.PI / 180;
const s1 = Math.sin(dLat/2) ** 2;
const s2 = Math.cos(a.lat*Math.PI/180) * Math.cos(b.lat*Math.PI/180) * Math.sin(dLng/2) ** 2;
return 2 * R * Math.asin(Math.sqrt(s1 + s2));
};
const getSupplierRating = (s) => {
// adapt to whatever field you actually store
const cands = [s.rating, s.avgRating, s.averageRating, s.overallRating];
const n = cands.find(x => Number.isFinite(Number(x)));
return Number(n ?? NaN);
};
// ---- end helpers ----
// parse inputs
const requestedCapacity = parseFloatSafe(requestedCapacityStr) || 0;
const requestedQuantity = parseIntSafe(requestedQuantityStr) || 0;
const totalRequiredCapacity = requestedCapacity * requestedQuantity;
const priceFrom = parseIntSafe(price_from);
const priceTo = parseIntSafe(price_to);
const ratingFrom = parseFloatSafe(rating_from);
const ratingTo = parseFloatSafe(rating_to);
const radiusFrom = parseFloatSafe(radius_from);
const radiusTo = parseFloatSafe(radius_to);
const pumpWanted = normalizePump(pump);
try {
// favorites
const customer = await User.findOne({ customerId }, { favorate_suppliers: 1 }).lean();
// favorites + customer coords (for radius)
const customer = await User.findOne({ customerId }, { favorate_suppliers: 1, googleLocation: 1, location: 1 }).lean();
const favoriteSet = new Set(customer?.favorate_suppliers || []);
const customerCoords =
parseLatLng(customer?.googleLocation) ||
parseLatLng(customer?.location);
// Tanker filter: ONLY by type_of_water (NO booked tanker exclusion, NO price/radius/rating)
// 1) Tankers base query: by type_of_water (+ pump if requested)
const tankerQuery = {};
if (type_of_water && type_of_water.trim() !== "") {
tankerQuery.typeofwater = type_of_water;
if (type_of_water?.trim()) tankerQuery.typeofwater = type_of_water.trim();
if (pumpWanted !== undefined) {
// try to match common representations
tankerQuery.$or = [
{ pump: pumpWanted ? { $in: [true, "1", "yes", "true", 1, "Y", "y"] } : { $in: [false, "0", "no", "false", 0, "N", "n"] } },
{ pumpAvailable: pumpWanted } // if you store as boolean
];
}
let tankers = await Tanker.find(tankerQuery).lean();
// 2) Price range on tanker.price
if (isValid(priceFrom) || isValid(priceTo)) {
tankers = tankers.filter(t => {
const p = parseIntSafe(t.price);
return isValid(p) && inRange(p, priceFrom, priceTo);
});
}
const tankers = await Tanker.find(tankerQuery).lean();
// Group tankers by supplier
// 3) Group by supplier
const supplierTankerMap = {};
for (const t of tankers) {
if (!supplierTankerMap[t.supplierId]) supplierTankerMap[t.supplierId] = [];
supplierTankerMap[t.supplierId].push(t);
if (!t?.supplierId) continue;
(supplierTankerMap[t.supplierId] ||= []).push(t);
}
// Capacity check per supplier (capacity * quantity)
const qualified = [];
// 4) Capacity qualification
let qualified = [];
for (const [supplierId, supplierTankers] of Object.entries(supplierTankerMap)) {
const totalAvail = supplierTankers.reduce((sum, tt) => sum + parseCapacity(tt.capacity), 0);
if (requestedCapacity > 0 && requestedQuantity > 0) {
if (totalAvail < totalRequiredCapacity) continue;
}
const totalAvail = supplierTankers.reduce((sum, tt) => sum + (parseFloatSafe(tt.capacity) || 0), 0);
if (requestedCapacity > 0 && requestedQuantity > 0 && totalAvail < totalRequiredCapacity) continue;
qualified.push({ supplierId, tankers: supplierTankers });
}
// Fetch suppliers + connection flags
// 5) Fetch suppliers for remaining filters (rating & radius) + flags
const supplierIds = qualified.map(q => q.supplierId);
const [suppliersData, acceptedReqs] = await Promise.all([
Supplier.find({ supplierId: { $in: supplierIds } }).lean(),
@ -1977,31 +2066,59 @@ exports.getSuppliersForPlanSearch = async (req, reply) => {
{ supplierId: 1, _id: 0 }
).lean()
]);
// Build quick lookup
const supplierById = new Map(suppliersData.map(s => [s.supplierId, s]));
const connectedSet = new Set(acceptedReqs.map(r => r.supplierId));
// Optional: check if any (single-day) requested booking exists with that supplier
// 6) Apply rating & radius filters on suppliers
if (isValid(ratingFrom) || isValid(ratingTo) || (isValid(radiusFrom) || isValid(radiusTo))) {
qualified = qualified.filter(q => {
const s = supplierById.get(q.supplierId);
if (!s) return false;
// rating
if (isValid(ratingFrom) || isValid(ratingTo)) {
const r = getSupplierRating(s);
if (!isValid(r) || !inRange(r, ratingFrom, ratingTo)) return false;
}
// radius (requires coords on both sides)
if ((isValid(radiusFrom) || isValid(radiusTo)) && customerCoords) {
const supCoords =
parseLatLng(s.googleLocation) ||
parseLatLng(s.location) ||
parseLatLng(s.addressLocation);
if (!supCoords) return false;
const distKm = haversineKm(customerCoords, supCoords);
if (!inRange(distKm, radiusFrom, radiusTo)) return false;
}
return true;
});
}
// 7) Build response with flags + optional 'requestedBooking' flag
const suppliers = [];
for (const q of qualified) {
const supplierData = supplierById.get(q.supplierId);
const friendRequestAccepted = connectedSet.has(q.supplierId);
const s = supplierById.get(q.supplierId);
if (!s) continue;
const isConnected = connectedSet.has(q.supplierId);
const isFavorite = favoriteSet.has(q.supplierId);
// If you want to expose a hint that user has already sent a single-day request earlier
const requestedBookingRecord = await RequestedBooking.findOne({
customerId,
"requested_suppliers.supplierId": q.supplierId
}, { time: 1 }).lean();
const requestedBooking = requestedBookingRecord
? { status: true, time: requestedBookingRecord.time }
: { status: false };
suppliers.push({
supplier: supplierData,
supplier: s,
tankers: q.tankers,
isConnected: friendRequestAccepted,
isConnected,
isFavorite,
requestedBooking
requestedBooking: requestedBookingRecord ? { status: true, time: requestedBookingRecord.time } : { status: false }
});
}
@ -2016,7 +2133,6 @@ exports.getSuppliersForPlanSearch = async (req, reply) => {
}
};
// controllers/validationHandler.js (add below the previous handler)
exports.createRequestedPlanBooking = async (req, reply) => {

@ -164,7 +164,7 @@ fastify.post("/api/requestedbookings", {
schema: {
tags: ["Supplier-Data"],
summary: "Search suppliers for Plans page",
description: "Filters by type_of_water and capacity×quantity. No bookings/price/radius/rating filters.",
description: "Filters by type_of_water, capacity×quantity, price, rating, radius, pump. No booked-tanker exclusion.",
params: {
type: "object",
required: ["customerId"],
@ -174,15 +174,25 @@ fastify.post("/api/requestedbookings", {
type: "object",
required: ["type_of_water", "capacity", "quantity", "frequency", "start_date", "end_date"],
properties: {
// UI fields
type_of_water: { type: "string" },
capacity: { type: "string" }, // UI field
quantity: { type: "string" }, // UI field
frequency: { // UI field: daily/weekly_once/_twice/_thrice
capacity: { type: "string" },
quantity: { type: "string" },
frequency: {
type: "string",
enum: ["daily","weekly_once","weekly_twice","weekly_thrice","weekly"]
},
start_date: { type: "string" }, // UI field "Start date"
end_date: { type: "string" } // UI field "End date"
start_date: { type: "string" },
end_date: { type: "string" },
// Extra filters from your payload
radius_from: { type: "string" },
radius_to: { type: "string" },
rating_from: { type: "string" },
rating_to: { type: "string" },
price_from: { type: "string" },
price_to: { type: "string" },
pump: { type: "string" }, // "true"/"false" | "1"/"0" | "yes"/"no"
},
additionalProperties: false
},

Loading…
Cancel
Save