changes in plans

master^2
Varun 1 month ago
parent 188ff245fa
commit d2540c55ee

@ -2135,88 +2135,313 @@ exports.getSuppliersForPlanSearch = async (req, reply) => {
// controllers/validationHandler.js (add below the previous handler) // controllers/validationHandler.js (add below the previous handler)
exports.createRequestedPlanBooking = async (req, reply) => { // exports.createRequestedPlanBooking = async (req, reply) => {
const { // const {
customerId, // customerId,
type_of_water, // type_of_water,
capacity, // capacity,
quantity, // quantity,
start_date, // start_date,
end_date, // end_date,
time, // time,
frequency, // "daily" | "weekly_once" | "weekly_twice" | "weekly_thrice" | "weekly" // frequency, // "daily" | "weekly_once" | "weekly_twice" | "weekly_thrice" | "weekly"
weekly_count, // used only if frequency === "weekly" // weekly_count, // used only if frequency === "weekly"
requested_suppliers // requested_suppliers
} = req.body; // } = 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) // if (frequency.startsWith("weekly") || frequency === "weekly") {
const parseCapacity = (v) => parseFloat((v || "0").toString().replace(/,/g, "")) || 0; // const wc = normalizeWeeklyCount(frequency, weekly_count);
const parseIntSafe = (v) => parseInt((v || "0").toString().replace(/,/g, ""), 10) || 0; // const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc });
const toISODate = (d) => d.toISOString().slice(0, 10); // const set = new Set(dows);
const mkUTCDate = (yyyy_mm_dd) => { // for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {
const [y, m, d] = (yyyy_mm_dd || "").split("-").map(Number); // if (set.has(d.getUTCDay())) out.push(toISODate(d));
return new Date(Date.UTC(y, (m || 1) - 1, d || 1)); // }
}; // return out;
const normalizeWeeklyCount = (freq, wc) => { // }
if (freq === "weekly_once") return 1;
if (freq === "weekly_twice") return 2; // throw new Error("Unsupported frequency");
if (freq === "weekly_thrice") return 3; // };
if (freq === "weekly") return wc || 1;
return 1; // try {
}; // if (!customerId || !type_of_water || !capacity || !quantity ||
const computeWeeklyDOWs = ({ anchorDow, weeklyCount }) => { // !start_date || !end_date || !time || !frequency || !requested_suppliers) {
if (weeklyCount === 1) return [anchorDow]; // return reply.code(400).send({
if (weeklyCount === 2) return [anchorDow, (anchorDow + 3) % 7]; // status_code: 400,
if (weeklyCount === 3) return [anchorDow, (anchorDow + 2) % 7, (anchorDow + 4) % 7]; // message: "Missing required fields"
return [anchorDow]; // });
}; // }
const generateDates = ({ frequency, start_date, end_date, weekly_count }) => {
const start = mkUTCDate(start_date); // const cap = parseCapacity(capacity);
const end = mkUTCDate(end_date); // const qty = parseIntSafe(quantity);
if (isNaN(start) || isNaN(end)) throw new Error("Invalid start_date or end_date"); // const total_required_capacity = cap * qty;
if (end < start) throw new Error("end_date must be after or equal to start_date");
// const dates = generateDates({ frequency, start_date, end_date, weekly_count });
// ~3 months cap // if (dates.length === 0) {
const maxMs = 92 * 24 * 60 * 60 * 1000; // return reply.code(400).send({ status_code: 400, message: "No dates generated for given inputs" });
if ((end - start) > maxMs) throw new Error("Range exceeds 3 months"); // }
const out = []; // const doc = new RecurringRequestedBooking({
if (frequency === "daily") { // customerId,
for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { // type_of_water,
out.push(toISODate(d)); // capacity,
} // quantity,
return out; // 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") { if (frequency.startsWith("weekly") || frequency === "weekly") {
const wc = normalizeWeeklyCount(frequency, weekly_count); const wc = normalizeWeeklyCount(frequency, weekly_count);
const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc }); const dows = computeWeeklyDOWs({ anchorDow: start.getUTCDay(), weeklyCount: wc });
const set = new Set(dows); const set = new Set(dows);
for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) { for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {
if (set.has(d.getUTCDay())) out.push(toISODate(d)); if (set.has(d.getUTCDay())) out.push(toISODate(d));
}
return out;
} }
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 { try {
if (!customerId || !type_of_water || !capacity || !quantity || const {
!start_date || !end_date || !time || !frequency || !requested_suppliers) { 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({ return reply.code(400).send({
status_code: 400, 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 cap = parseCapacity(capacity);
const qty = parseIntSafe(quantity); const qty = parseIntSafe(quantity);
const total_required_capacity = cap * qty; const total_required_capacity = cap * qty;
const dates = generateDates({ frequency, start_date, end_date, weekly_count }); if (cap <= 0 || qty <= 0) {
if (dates.length === 0) { return reply.code(400).send({
return reply.code(400).send({ status_code: 400, message: "No dates generated for given inputs" }); 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({ const doc = new RecurringRequestedBooking({
@ -2231,7 +2456,7 @@ exports.createRequestedPlanBooking = async (req, reply) => {
end_date, end_date,
time, time,
dates, dates,
requested_suppliers, requested_suppliers: suppliers,
status: "pending" status: "pending"
}); });
@ -2254,3 +2479,4 @@ exports.createRequestedPlanBooking = async (req, reply) => {
} }
}; };

@ -181,30 +181,62 @@ const requestedBookingSchema = new mongoose.Schema({
// models/RecurringRequestedBooking.js // models/RecurringRequestedBooking.js
// const requestedSupplier1Schema = new mongoose.Schema({
// supplierId: String,
// quoted_amount: Number,
// time: { type: String, default: null },
// status: { type: String, default: "pending" },
// }, { _id: false });
// const recurringRequestedBookingSchema = new mongoose.Schema({
// customerId: { type: String, required: true },
// type_of_water: String,
// capacity: String,
// quantity: String,
// total_required_capacity: Number,
// frequency: { type: String, enum: ["daily","weekly_once","weekly_twice","weekly_thrice","weekly"], required: true },
// weekly_count: { type: Number, enum: [1,2,3] },
// start_date: { type: String, required: true },
// end_date: { type: String, required: true },
// time: String,
// dates: [String],
// requested_suppliers: [requestedSupplier1Schema],
// status: { type: String, default: "pending" },
// }, { timestamps: true });
const requestedSupplier1Schema = new mongoose.Schema({ const requestedSupplier1Schema = new mongoose.Schema({
supplierId: String, supplierId: { type: String, required: true },
quoted_amount: Number, quoted_amount: { type: Number, default: 0 },
time: { type: String, default: null }, time: { type: String, default: null }, // keep as string to match current payloads
status: { type: String, default: "pending" }, status: { type: String, enum: ["pending", "accepted", "rejected"], default: "pending" },
}, { _id: false }); }, { _id: false });
const recurringRequestedBookingSchema = new mongoose.Schema({ const recurringRequestedBookingSchema = new mongoose.Schema({
customerId: { type: String, required: true }, customerId: { type: String, required: true },
type_of_water: String, type_of_water: { type: String, required: true },
capacity: String,
quantity: String, capacity: { type: String, required: true }, // keep as sent by UI, we also store parsed number below if you want
total_required_capacity: Number, quantity: { type: String, required: true },
frequency: { type: String, enum: ["daily","weekly_once","weekly_twice","weekly_thrice","weekly"], required: true }, total_required_capacity: { type: Number, required: true }, // capacity * quantity (numeric)
weekly_count: { type: Number, enum: [1,2,3] },
start_date: { type: String, required: true }, frequency: {
type: String,
enum: ["daily", "weekly_once", "weekly_twice", "weekly_thrice", "weekly"],
required: true
},
weekly_count: { type: Number, default: 1 },
start_date: { type: String, required: true }, // storing original string for audit
end_date: { type: String, required: true }, end_date: { type: String, required: true },
time: String, time: { type: String, required: true },
dates: [String],
requested_suppliers: [requestedSupplier1Schema], dates: { type: [String], default: [] }, // ISO "YYYY-MM-DD" strings
status: { type: String, default: "pending" },
}, { timestamps: true });
requested_suppliers: { type: [requestedSupplier1Schema], default: [] },
status: { type: String, default: "pending" },
}, { timestamps: true });
const RequestedBooking = mongoose.model('RequestedBooking', requestedBookingSchema); const RequestedBooking = mongoose.model('RequestedBooking', requestedBookingSchema);

Loading…
Cancel
Save