Adhyayan & Utsav — Technical Reference¶
Part of the Aashray Technical Reference. Pairs with Business Logic — Adhyayan & Utsav, which describes these rules in plain terms with no code detail. Related technical detail currently lives inline in: Booking Lifecycle, Food, Travel, Payments, Admin Panel Map, API Reference (not yet split out into their own technical files).
This file is 100% derived from reading the backend, admin, and app source on main — no operational/staff-knowledge content lives here (that's in the business-logic companion). It covers exactly how the two event domains — Raj Adhyayan (study sessions, stored as shibir) and Raj Utsav (festivals, sold as packages) — plus the unrelated AVT management screen are implemented: every route, the data model, admin mechanics, edge cases, and known code-level defects.
Verification status: audited & anchored per
docs/DOCS-METHODOLOGY.mdon 2026-07-07 againstaashray-backendmain. Every rule carries a[src: …]anchor. This domain was found fully accurate — all 21 discrepancies confirmed real against code and no drift found (one trivial nuance: a shibir's defaultopenstatus comes from the model default, not an explicit set increateAdhyayan).
Shared foundations¶
Statuses (config/constants.js). Booking statuses used across both domains: waiting, pending (payment pending), confirmed, cancelled (self-cancel), admin cancelled, cash pending, cash completed, credited, checkedin (Utsav only). Event (parent) statuses: open, closed; shibir also has deleted (soft delete). Note the string values that surprise: STATUS_PAYMENT_PENDING === 'pending', STATUS_PAYMENT_COMPLETED === 'completed', STATUS_ADMIN_CANCELLED === 'admin cancelled'.
Feedback hour. FEEDBACK_ELIGIBILITY_HOUR = 13 — feedback windows open at 1:00 PM IST (Asia/Kolkata) on the event's start date. [src: config/constants.js:19]
Research Centre pivot. RESEARCH_CENTRE = 'Research Centre'. This single string gates on-campus behavior: attendance tracking (shibir), auto food provisioning + date-blocking (utsav), and the member-app add-ons routing. Off-campus events (e.g. Kolkata, Dharampur/DHU, Rajnandgaon) skip all of it.
Seat model. Both shibir_db and utsav_db carry total_seats and a mutable available_seats counter. Booking decrements it; cancel/admin-cancel returns it (with different waitlist rules per domain, below). There is no per-package seat pool — Utsav seats are pooled at the utsav level regardless of package. [src: models/shibir_db.model.js:43,47; models/utsav_db.model.js:33,37; helpers/adhyayanBooking.helper.js:228-300; helpers/utsavBooking.helper.js:402-415]
Auth. All client routes are guarded by validateCard middleware (a signed-in member; req.user.cardno). All admin routes are guarded by auth + authorizeRoles(...). Client route mount prefixes are /adhyayan and /utsav; admin prefixes are the admin /adhyayan, /utsav, /avt bases (route paths below are router-relative).
Raj Adhyayan (Shibir) — event, browse, registration, status¶
Stored in shibir_db. Members register a seat for themselves, guests, or other mumukshus; payment is optional (amount may be 0 → auto-confirmed). Client browse is GET /adhyayan/:id (speaker, dates, location, price, availability). Registration modal captures who (Self / Guest / Mumukshus); Guest-class accounts (res_status === 'GUEST') can only book Self. Self routes straight to review when off-campus, or through the add-ons screen when at Research Centre.
- Create (
createAdhyayan): rejects a duplicatespeaker+start_datepair; derivesmonthfromstart_date; setsavailable_seats = total_seats; default statusopen. Iflocation === 'Research Centre', sessions are initialized immediately (initializeShibirSessions, see attendance section below). Wrapped in a transaction. - Booking creation (
createAdhyayanBooking/...Admin): for each (user × shibir): - If
available_seats > 0andstatus === 'open'→ reserve a seat (decrement), set status =pendingwhenamount > 0elseconfirmed; create an attendance entry (RC only) and, if payable, a pending transaction. - Otherwise → create the booking as
waiting(waitlisted); no seat, no transaction. - Duplicate guard (
checkAdhyayanAlreadyBooked): a member cannot hold aconfirmed/waiting/pendingbooking for the same shibir twice. - Validity window (
validateAdhyayans): the shibir must exist and itsstart_date >= today − 15 days(so recently-past shibirs are still bookable/valid; the variable is misnamedsevenDaysAgo). [src: helpers/adhyayanBooking.helper.js:109] - Availability preview (
checkAdhyayanAvailabilityForMumukshus): for a group, computesavailable = min(available_seats, groupSize),waiting = groupSize − available,charge = available × amount— only whenstatus === 'open', else everyone waits. [src: helpers/adhyayanBooking.helper.js:403-409] - Update (
updateAdhyayan): iftotal_seatschanged,available_seatsshifts by the same delta (floored at 0); else an explicitavailable_seatsoverride is honored. Duration cannot change once attendance records exist. On RC it re-syncs sessions; if moved off RC it deletes sessions + attendance records. - Open/Close (
activateAdhyayan,PUT /:id/:activate): flipsshibir_db.statusto the:activatepath value (open/closed). Aclosedshibir forces new bookings towaiting. - Soft delete (
softDeleteShibir): sets statusdeleted(hidden from client browse) and push-notifies everyone with a waiting/confirmed/pending booking that the admin cancelled it. Note: it only flips the parent status — it does not cancel the child bookings or refund them. - Admin status update (
adhyayanStatusUpdate,PUT /status): state machine — see Edge cases.
Every route — Adhyayan¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| GET | /adhyayan/getall |
member | Upcoming shibirs (start_date > today, not deleted), grouped by month |
page, page_size |
| GET | /adhyayan/getrange |
member | Shibirs overlapping a date range | start_date, end_date (default start_date + 15d) |
| GET | /adhyayan/:id |
member | Single shibir detail | id |
| GET | /adhyayan/getbooked |
member | Member's shibir bookings (own or booked-by) + feedback flags | page, page_size |
| DELETE | /adhyayan/cancel |
member | Cancel a shibir booking | body bookingid |
| POST | /adhyayan/create |
adhyayan admins¹ | Create shibir | name, speaker, start_date, end_date, amount, location, total_seats, food_allowed, comments |
| PUT | /adhyayan/update/:id |
admins¹ | Edit shibir | + optional available_seats |
| PUT | /adhyayan/:id/:activate |
admins¹ | Open/close shibir | id, activate = open/closed |
| DELETE | /adhyayan/:id |
admins¹ | Soft-delete shibir | id |
| GET | /adhyayan/fetchALLadhyayan |
admins¹ | All shibirs start_date ≥ today−7d + status counts |
— |
| GET | /adhyayan/fetchAdhyayan |
admins¹ | Shibirs by location ≥ today−15d + sessions |
location |
| GET | /adhyayan/fetchPGS |
admins¹ | "Param Gyaan Sabha%" shibirs ≥ today−30d |
— |
| GET | /adhyayan/fetch/:id |
admins¹ | Single shibir (admin) | id |
| GET | /adhyayan/fetchList |
admins¹ | id + name of every shibir (dropdowns) | — |
| GET | /adhyayan/bookings |
admins¹ | Bookings for a shibir filtered by status bucket | shibir_id, status, page |
| GET | /adhyayan/waitlist/:id |
admins¹ | All waiting bookings (future shibirs) |
id (unused in query) |
| GET | /adhyayan/pendinglist/:id |
admins¹ | All pending/cash pending bookings |
id (unused) |
| PUT | /adhyayan/status |
admins¹ | Change one booking's status | shibir_id, bookingid, status, description |
| POST | /adhyayan/booking/admin |
admins¹ | Admin books shibirs for mumukshus | shibir_ids[], mumukshus[] |
¹ officeAdmin, adhyayanAdmin, superAdmin, adhyayanAdminDhu, adhyayanAdminRaj, adhyayanAdminKol, accountsAdmin, accountsAdminPra, adhyayanAdminReadOnly, utsavAdmin.
Admin operations — Adhyayan¶
Attendance-scan pages and status/report pages consume the routes above. fetchALLadhyayan / fetchAdhyayanByLocation / fetchPGS return per-shibir aggregate counts (confirmed_count counts both confirmed and cash completed; plus waitlist_count, pending_count, and — by location — selfcancel_count / admin_cancelled_count). The admin booking list (/bookings) buckets statuses: confirmed → confirmed+cash completed; pending → pending+cash pending; waiting, cancelled, admin cancelled each their own.
Shibir sessions & attendance (scan, tap, toggle, reports, PGS)¶
For Research-Centre shibirs, the backend tracks per-day, per-session attendance. Two tables cooperate: shibir_attendance_db (one row per registrant, with legacy fixed columns session_1..9 = "session applicable" and session_1_attendance..9 = "attended") and shibir_attendance_records (the modern dynamic table: one row per (booking × session_number) with attended boolean). shibir_sessions defines the session schedule. Attendance is admin-only; members are scanned via their card QR (which encodes cardnumber=<cardno>).
- Session generation (
initializeShibirSessions):days = end − start + 1;totalSessions = days × 3. Per day it upserts 2regularsessions (numbered(d−1)*2+1at 10:00,(d−1)*2+2at 15:45) and 1MVsession (numbered2*days + dat 04:30). Orphaned sessions/records withsession_number > totalSessionsare deleted (so shortening a shibir prunes them). Uniqueness on(shibir_id, session_number). [src: helpers/adhyayanBooking.helper.js:578-646; models/shibir_sessions.model.js:45-48] - Attendance-entry creation (
createShibirAttendanceEntry): only when the shibirlocation === 'Research Centre'. Idempotent (skips if a row for the booking exists). Seedssession_1..9 = 1(all first-9 sessions marked "applicable"), setsdays, and initializesshibir_sessionsif absent. Created automatically whenever a booking becomesconfirmed/pending. - Reset (
resetShibirAttendance): deletes both theshibir_attendance_dbrow and allshibir_attendance_recordsfor a booking (called on cancel / admin-cancel). - Marking: only bookings that are
confirmed(orcash completed) are eligible to be marked.
Every route — attendance¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| POST | /adhyayan/attendance/:shibir_id/:session_no/:cardno |
admins¹ | Mark attendance (mobile QR scan + tap-card) | path params |
| PUT | /adhyayan/attendance/toggle |
admins¹ | Toggle one registrant's session on/off | shibir_id, cardno, sessionNumber, value (0/1) |
| POST | /adhyayan/attendance/bulk-toggle |
admins¹ | Toggle many registrants for one session | shibir_id, sessionNumber, cardnos[], value |
| POST | /adhyayan/attendance/create |
admins¹ | Manually create an attendance-db row for a booking | bookingid |
| GET | /adhyayan/attendance/report/:shibir_id |
admins¹ | Per-registrant Yes/No matrix across sessions | shibir_id |
| GET | /adhyayan/attendance/summary/:shibir_id |
admins¹ | Per-session attended / absentee counts | shibir_id |
| GET | /adhyayan/feedback/:shibir_id |
admins¹ | Feedback rows + aggregate stats (see feedback section) | shibir_id, page |
Admin operations — attendance¶
- Mobile QR scan (
adhyayanAttendanceScanMob.js): opens the device camera (Html5Qrcode), readscardnumber=<cardno>, POSTs to the mark route, then shows success / "already marked" / "not found" and re-arms after 1.5 s.?session=and?shibir_id=come from the URL. - Tap-card scan (
adhyayanAttendanceScanTap.js): a hardware card reader types the cardno into a focused input and sends Enter; same mark route. Auto-refocuses and auto-submits. - Marking logic (
markAdhyayanAttendance): validates the session exists, the registrant has an eligible (confirmed/cash completed) attendance record, and it isn't already marked; upserts ashibir_attendance_recordsrow (attended = true); ifsession_no ≤ 9it also flips the legacysession_N_attendancecolumn. - Report (
fetchAdhyayanAttendanceReport): auto-initializes sessions if none exist, then builds a per-confirmed-registrant row withYes/Nofor each session (from the records table). Summary (fetchAdhyayanAttendanceSummary): counts attended per session and derives absentees =totalRegistrants − attended. - PGS report (
fetchPGS→pgsReport.js): a special view of shibirs whosename LIKE 'Param Gyaan Sabha%'within the last 30 days, with confirmed/pending/waitlist counts and an open/close toggle. (PGS = Param Gyaan Sabha, a recurring assembly modeled as a shibir.)
Adhyayan feedback¶
A post-event survey (8 questions: 6 ratings/boolean + free-text) stored one row per (shibir × member) in adhyayan_feedback, with a unique constraint preventing duplicates. A "Give Feedback" button appears on a past Adhyayan booking only when the backend flags it eligible, it isn't already submitted, and it's the member's own booking (not one they made for someone else).
validateFeedbackEligibility(cardno, shibir_id): [src: helpers/adhyayanBooking.helper.js:439-494]
1. Shibir must exist.
2. feedbackStartDate = start_date at 13:00 IST; now must be ≥ that (else ERR_ADHYAYAN_NOT_COMPLETED — "Cannot submit feedback for ongoing or future adhyayan").
3. now.diff(feedbackStartDate, 'days') ≤ 15 (else expired).
4. The member must hold a confirmed or cash completed booking for the shibir.
5. No existing feedback row (unique (shibir_id, cardno)).
submitFeedback writes the row (fields: swadhay_karta_rating, personal_interaction_rating, swadhay_karta_suggestions, raj_adhyayan_interest boolean, future_topics, loved_most, improvement_suggestions, food_rating, stay_rating); ratings are 1–5 (model-validated). feedbackValidation runs the same gate for the entry screen.
Every route — Adhyayan feedback¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| GET | /adhyayan/feedback/validate |
member | Check eligibility before showing the survey | shibir_id |
| POST | /adhyayan/feedback |
member | Submit feedback | shibir_id + rating/text fields |
| GET | /adhyayan/feedback/:shibir_id |
admins¹ | Read all feedback + aggregate stats | shibir_id, page, page_size |
getAdhyayanFeedback returns paginated feedback joined to card details (name, mobile, gender, res_status, center) and shibir name, plus getFeedbackStats: total responses, average swadhay_karta/personal_interaction/food/stay ratings, and a count of raj_adhyayan_interest = true.
The showFeedback flag in FetchBookedShibir uses end_date + 15 days as the window and requires status === confirmed; the eligibility gate above uses start_date + 15 days and accepts confirmed or cash completed (see Discrepancies #8–9).
Raj Utsav — event, packages, browse, admin status¶
A festival (utsav_db) sold as one or more dated packages (utsav_packages_db, each with its own start/end/amount). Members pick a package and register; check-in and per-participant logistics (arrival, car, volunteer role, room) are tracked on the booking. Upcoming utsavs are visible only while registration_deadline is null or ≥ today, grouped by month with packages inlined; detail screen shows package cards ("Starting from ₹{min}"), an availability card, and static Daily Schedule + Guidelines modals. Registration modal captures package + "arriving by own car?" (+ car number, exactly 10 chars, when yes) + optional volunteer role + free-text; Self always routes through the add-ons screen (unlike Adhyayan).
- Create (
createUtsav): validatesstarting_meal/ending_meal(arrays ofbreakfast|lunch|dinner, non-empty, no dupes); requiresregistration_deadlinestrictly beforestart_date; rejects duplicatename+start_date; setsavailable_seats = total_seats, statusopen, location default Research Centre. If Research Centre, it also creates ablock_datesrow (checkin = start_date,checkout = end_date + 1) so room bookings are blocked across the festival. [src: controllers/admin/utsavManagement.controller.js:195-274 (block_dates :259-268)] - Update (
updateUtsav): same hybridavailable_seatslogic as shibir (delta ontotal_seatschange, else explicit override, else keep). Also updatable: status, dates, comments, location,registration_deadline, meals. - Open/close (
activateUtsav,PUT /:id/:activate) and booking status (utsavStatusUpdate,PUT /status) — state machine in Edge cases. - Browse queries (
FetchUpcoming,FetchUtsavById): join packages viaJSON_ARRAYAGG; only surface utsavs whoseregistration_deadlineis null or ≥ today (so a past-deadline utsav returns 404 by id). - Packages:
addUtsavPackage/addUtsavPackagesBulk: package dates must fall within the utsav dates,end ≥ start, name unique per utsav; bulk additionally requiresamount > 0and rejects in-request duplicate names.updateUtsavPackageedits name/dates/amount. - Samvatsari overlap guard (
checkOverlapWithSamvatsari): hardcodedSAMVATSARI_PACKAGE_ID = 21and overlapping package ids[18, 20]— a member holding one cannot also book the conflicting one. [src: helpers/utsavBooking.helper.js:45-46]
Every route — Utsav¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| GET | /utsav/upcoming |
member | Upcoming utsavs (+ packages) grouped by month | page, page_size |
| GET | /utsav/:id |
member | Single utsav + packages | id |
| GET | /utsav/booking |
member | Member's utsav bookings + feedback flags | page, page_size |
| DELETE | /utsav/booking |
member | Cancel a utsav booking | body bookingid |
| POST | /utsav/create |
utsav admins² | Create utsav (+ block_dates on RC) | name, start_date, end_date, total_seats, location, registration_deadline, starting_meal, ending_meal |
| PUT | /utsav/update/:id |
utsav admins² | Edit utsav | + available_seats, status, comments |
| PUT | /utsav/:id/:activate |
utsav admins² | Open/close utsav | activate |
| PUT | /utsav/status |
utsav admins² | Change one booking's status | utsav_id, bookingid, status, description, issueCredits |
| POST | /utsav/package |
utsav admins² | Add one package | utsavid, name, start_date, end_date, amount |
| POST | /utsav/package/bulk |
utsav admins² | Add many packages | packages[] |
| PUT | /utsav/updatepackage/:id |
utsav admins² | Edit a package | name, start_date, end_date, amount |
| GET | /utsav/fetch |
utsav admins² | All utsavs + status counts | — |
| GET | /utsav/fetchUtsav |
utsav admins² | Utsavs by location + counts | location |
| GET | /utsav/fetch/:id |
utsav admins² | Single utsav (admin) | id |
| GET | /utsav/fetchList |
utsav admins² | id + name of every utsav | — |
| GET | /utsav/fetchpackage |
utsav admins² | All future packages + waitlist counts | — |
| GET | /utsav/fetchPackagesByUtsav |
utsav admins² | Packages for one utsav | utsavid |
| GET | /utsav/fetchpackage/:id |
utsav admins² | Single package | id |
| GET | /utsav/bookings |
utsav admins² | Bookings for a utsav by status bucket | utsavid, status, page |
| POST | /utsav/booking |
utsav admins³ | Admin books a utsav for mumukshus | utsavid, mumukshus[] |
² utsavAdmin, superAdmin, accountsAdminPra, accountsAdmin, utsavAdminReadOnly, utsavAdminRaj. ³ /booking narrows to utsavAdmin, superAdmin, accountsAdminPra, accountsAdmin (excludes read-only + Raj). (Role gating per the utsav admin routes file; the two public routes utsavCheckin/issue/:cardno sit on the unauthenticated utsavPublicRouter — see Discrepancy #14.) [src: routes/admin/utsavManagement.routes.js:44-50]
Admin operations — Utsav¶
fetchAllUtsav / fetchUtsavByLocation return per-utsav counts: confirmed_count (confirmed + cash completed + checkedin), checkedin_count, waitlist_count, pending_count, self/admin-cancel counts, and volunteer_opted_count (confirmed-ish bookings whose volunteer role is a real selection). fetchAllPackages shows only future packages with waitlist counts.
Utsav booking add-ons — arrival, car, volunteer, food, check-in, room¶
The Utsav booking itself carries logistics beyond the seat: expected arrival, carno, a volunteer role, roomno, and free-text other. On-campus utsavs auto-provision meals; admins run check-in, plate issue, room assignment, and occupancy reports. The registration modal collects arrival / car / volunteer / other per participant; members do not self-check-in.
- Booking creation (
bookUtsavForMumukshus): per participant —available_seats ≤ 0→waiting; elseamount > 0→pending(decrement seat); else →confirmed(free package auto-confirm, decrement seat). If the utsav isclosed, forcewaiting. A pending transaction is created only when open + pending + amount > 0. Food is provisioned for every non-waitlisted booking (RC only). [src: helpers/utsavBooking.helper.js:87-137] - Food provisioning (
bookFoodForUtsav): RC only. Uses the package date range; the utsav'sstarting_mealapplies only if the package starts on the utsav start date, andending_mealonly if it ends on the utsav end date (boundary-day meal trimming). Delegates tobookFoodForAllMeals. [src: helpers/utsavBooking.helper.js:152-161] - Admin booking (
bookUtsavForMumukshusAdmin): always createswaiting,bookedBy = null, no transaction (converted to pending → confirmed later). [src: helpers/utsavBooking.helper.js:203-221] - Cancel (
CancelUtsavBooking):userCancelBooking(refund/credits),cancelUtsavFoodBookings(RC only, cancels the package's meals),openUtsavSeat(return seat if open), then notification + email + WhatsApp. - Check-in (
utsavCheckin, public): from a non-cancelled/non-waiting booking, blocks if payment still pending, else setscheckedin; idempotent.
Every route — Utsav add-ons¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| POST | /utsav/utsavCheckin |
public | Mark a booking checkedin |
cardno, utsavid |
| POST | /utsav/issue/:cardno |
public | Issue a food plate for a meal | cardno, body meal |
| GET | /utsav/volunteer |
utsav admins² | Volunteer access list (sorted) | utsavid |
| GET | /utsav/fetchVolunteerOptions |
utsav admins² | Static volunteer role list | — |
| GET | /utsav/utsavCheckinReport |
utsav admins² | Per-booking checked-in yes/no | utsavid, status |
| POST | /utsav/uploadRoomNo |
utsav admins² | Bulk-assign room numbers via Excel | multipart file |
| PUT | /utsav/updateRoomNo |
utsav admins² | Set one booking's room | bookingid, roomno |
| GET | /utsav/pre_event_room_occupancy |
utsav admins² | Room bookings ≤ 5 days before start | utsavid, statuses |
| GET | /utsav/post_event_room_occupancy |
utsav admins² | Room bookings ≤ 5 days after end | utsavid, statuses |
Admin operations — Utsav add-ons¶
- Check-in is typically driven at the gate (public route). Plate issue (
issuePlate→issueFoodPlate) hands out a physical meal plate. Room assignment viauploadRoomNoExcelmatches each row bycardno||utsavid||packageidagainst confirmed/checkedin bookings and validates thebookingidbefore a single bulkUPDATE; mismatches are skipped and reported. Occupancy reports (ReservationReport) joinroom_bookingfor registered participants whose check-in falls within a ±5-day window around the utsav. - Volunteer list (
fetchUtsavBookingsVolunteer) orders "not selected" and "Unable to Volunteer" last. Volunteer options are static: Admin, Logistics, Kitchen, Vitraag Vigyaan Bhavan, Samadhi Sthal, Unable to Volunteer.
Utsav feedback¶
A 6-question survey stored as a parent row (utsav_feedback, unique per utsav × member) plus N answer rows (utsav_feedback_answers). Unlike Adhyayan's fixed-column table, answers are generic (question_id, question_text, question_type, answer) tuples. Same "Give Feedback" gating as Adhyayan (eligible flag + not-submitted + own booking). Questions: event_rating, stay_rating, food_rating, program_rating (ratings 1–5) and loved_most, improvement_suggestions (text) — all required (no Skip).
validateFeedbackEligibility(cardno, utsav_id): [src: helpers/utsavBooking.helper.js:653-766]
1. Utsav must exist.
2. feedbackStartDate = start_date at 13:00 IST; now must be ≥ it.
3. Window = 15 days if the utsav duration ≥ 8 days, else 8 days from the start; now must be ≤ that.
4. Member must hold a confirmed / cash completed / checkedin booking.
5. No existing feedback (unique (utsav_id, cardno)).
submitUtsavFeedback: validates that the submitted answers exactly cover the six allowed question ids with the right types and ratings in 1–5, then creates the utsav_feedback row + bulk-inserts the answer rows in a transaction (rolls back on any error).
Every route — Utsav feedback¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| GET | /utsav/feedback/validate |
member | Check eligibility | utsav_id |
| POST | /utsav/feedback |
member | Submit feedback | utsav_id, answers[] |
| GET | /utsav/utsav-feedback |
utsav admins² | Read feedback + flattened answers | utsav_id, search |
fetchUtsavFeedbacks lists feedback joined to card details + utsav name, optional utsav_id and search (cardno/name) filters, and flattens answers into food_rating, stay_rating, overall_rating, loved_most, improvement_suggestions columns (plus the raw answers[]). The list-screen showFeedback flag in ViewUtsavBookings uses a fixed 8-day window (daysSinceStart ≤ 8) and accepts confirmed/checkedin; the eligibility gate above uses 8-or-15 days and accepts confirmed/cash completed/checkedin (see Discrepancies #17–19).
AVT management¶
A minimal admin member-card directory / photo lookup restricted to AVT admins. Not an event feature — grouped here because it lives beside Adhyayan/Utsav in the admin panel — it reads card_db only. Two read-only queries; no writes, no transactions. fetchAllCards returns every card row; searchCards does a LIKE %name% on issuedto.
Every route — AVT¶
| Method | Path | Auth | Purpose | Key params |
|---|---|---|---|---|
| GET | /avt/getAll |
avtAdmin, superAdmin |
All member cards | — |
| GET | /avt/search/:name |
avtAdmin, superAdmin |
Search members by name | name |
The AVT page (admin/avt/index.js) is a debounced search box; results render name, cardno, mobile, email, and a "View Photo" link that opens the member's pfp in a copy-/download-guarded overlay (right-click, drag, and text-selection disabled) — a light privacy measure for member photos.
Data & relationships¶
shibir_db1—Nshibir_booking_db(shibir_id). A booking belongs to acard_db(cardno) and optionally a booker (bookedBy, null for self).shibir_dbalso 1—Nadhyayan_feedback,shibir_sessions,shibir_attendance_db,shibir_attendance_records. BookingstatusENUM does not includecash completed/cash pendingin the model definition (allowed values arewaiting, confirmed, cancelled, admin cancelled, pending) even though the code writes/reads them (MySQL ENUM would coerce/reject — a latent schema mismatch). [src: models/shibir_booking_db.model.js:46-52]shibir_sessions,shibir_attendance_db, andshibir_attendance_recordsall referenceshibir_db.id. Records referenceshibir_booking_db.bookingid(unique on(bookingid, session_number)) andcard_db.cardno. The legacyshibir_attendance_dbis a denormalized convenience mirror of the first 9 sessions.adhyayan_feedback→shibir_db(shibir_id) andcard_db(cardno). Unique indexunique_feedback_per_user_per_adhyayan (shibir_id, cardno).utsav_db1—Nutsav_packages_dband 1—Nutsav_booking(utsavid) and 1—Nutsav_feedback. A booking references a package (packageid), a card (cardno), an optional booker (bookedBy), and carriesarrival,carno,volunteer,roomno,other,status. Creating a RC utsav also writesblock_dates.utsav_booking.roomnois a free string (assigned by admin, not a FK toroom_db).ReservationReportcross-referencesroom_booking+card_db. Food bookings are created in the food domain keyed on package dates + cardno.utsav_feedback→utsav_db+card_db;utsav_feedback_answers→utsav_feedback(feedback_id, cascade delete).question_typeENUM(rating,text).- AVT reads
card_dbdirectly; no event tables involved.
Edge cases & branches¶
Admin adhyayanStatusUpdate state machine¶
- Same-status or already-cancelled (
cancelled/admin cancelled) → rejected. - →
confirmed: if fromwaiting, reserve a seat; create the pending transaction if missing; mark the transactioncompleted; ensure an attendance entry exists. - →
pending: rejected fromconfirmed; only fromwaiting(reserve seat, create transaction). If credits already fully cover it (transaction.status === completed), it is auto-upgraded toconfirmedwith a confirmation notification. - →
admin cancelled: fromconfirmed/pendingit opens the seat (auto-promoting the oldest waitlister viaopenAdhyayanSeat); resets attendance; cancels the transaction (admin cancel path). - →
waiting: always invalid.
Admin utsavStatusUpdate state machine¶
- Same-status or already
admin cancelled→ rejected. - →
confirmed: only frompendingorcancelled. Creates/reopens/completes the transaction and books food. (There is a deadif (status === waiting) reserveUtsavSeatbranch that the guard makes unreachable.) - →
pending: only fromwaiting. If the package amount is 0 → auto-confirm (reserve seat, book food, commit, return). Else reserve seat and create/reopen the transaction + food. - →
admin cancelled: fromwaiting/pending/confirmed/cancelled. Cancels food (RC), frees the seat if it was confirmed/pending, and handles the transaction per theissueCreditsflag (issue credits vs. mark admin-cancelled without credits vs. leave a completed txn alone). - Check-in is a separate public route (
utsavCheckin).
Attendance¶
- Sessions beyond 9 have no legacy column. A shibir of ≥ 4 days generates ≥ 12 sessions; sessions 10+ are tracked only in
shibir_attendance_records. Marking them still works (the≤ 9guard just skips the legacy mirror), but any consumer reading the oldsession_N_attendancecolumns sees an incomplete picture. - Reports/summaries auto-create sessions on read if missing (inside their own transaction) — a read endpoint with a write side effect.
- Attendance rows are created for
pendingbookings too (not just confirmed), but onlyconfirmedrows are counted in reports and eligible to be marked.
Feedback¶
- The
showFeedbackflag inFetchBookedShibirusesend_date + 15 daysand requiresstatus === confirmed; the eligibility gate usesstart_date + 15 daysand acceptsconfirmedorcash completed. - The list-screen
showFeedbackflag inViewUtsavBookingsuses a fixed 8-day window and acceptsconfirmed/checkedin; the eligibility gate uses 8-or-15 days and acceptsconfirmed/cash completed/checkedin.
Utsav add-ons¶
- Check-in and plate-issue are public (unauthenticated) routes — gate volunteers use them without an admin login.
uploadRoomNoExcelrequires all rows to share oneutsavidand builds a raw SQLCASEupdate (string-interpolated bookingids/roomnos).- Free (₹0) packages auto-confirm at booking time and again on the admin
pendingtransition.
AVT¶
getAllreturns the entirecard_dbwith no pagination or column projection — a heavy, all-fields payload as the directory grows.- The photo "protection" is client-side only (overlay + disabled events); the
pfpURL is still in the response.
Discrepancies¶
softDeleteShibirorphans bookings. It flips the parent todeletedand notifies users, but does not cancel/refund the child bookings or free their transactions. Cancelled-event money handling is left undone.[src: controllers/admin/adhyayanManagement.controller.js:749-801]validateAdhyayansmisnomer. The "7 days ago" variable (sevenDaysAgo) actually subtracts 15 days.[src: helpers/adhyayanBooking.helper.js:109]getAdhyayanBookingshelper is dead/broken. It uses[Op.in]but never importsOp(onlySequelize). Not reached by any live flow, but it would throw.[src: helpers/adhyayanBooking.helper.js:36,432 (no callers)]fetchAdhyayanBookingsstatus testif (status != null || status != undefined)is always true (||of two negations) — harmless (it just trims), but logically wrong.[src: controllers/admin/adhyayanManagement.controller.js:322]FetchShibirByIdhas no status filter → it will returndeletedand long-past shibirs by id.[src: controllers/client/adhyayanBooking.controller.js:263-280]- Dual attendance schema drift. The fixed
session_1..9mirror vs the dynamicshibir_attendance_recordstable is a half-finished migration; the two can drift for long shibirs (sessions 10+ only exist in the records table).[src: models/shibir_attendance_db.model.js; models/shibir_attendance_records.model.js; helpers/adhyayanBooking.helper.js:551-553] - Attendance is silently a Research-Centre-only feature (
createShibirAttendanceEntryreturns early otherwise), yet the report endpoints will still initialize sessions for a non-RC shibir if called — producing an empty registrant list.[src: helpers/adhyayanBooking.helper.js:542] - Adhyayan feedback window inconsistency. The button-visibility flag counts 15 days from end_date and requires
status === confirmed, butvalidateFeedbackEligibilitycounts 15 days from start_date (despite the error message saying "within 15 days after the adhyayan ends") and acceptsconfirmed/cash completed. For a multi-day shibir these disagree.[src: controllers/client/adhyayanBooking.controller.js:128-129,145; helpers/adhyayanBooking.helper.js:461-474 (msg :465)] - The button flag requires
confirmed; the gate also allowscash completed. A cash-paying attendee may be blocked from seeing the button but allowed to submit via deep link.[src: controllers/client/adhyayanBooking.controller.js:145; helpers/adhyayanBooking.helper.js:474] - No waitlist auto-promotion on Utsav.
openUtsavSeatmerely incrementsavailable_seats(and only if the utsav is stillopen); unlike Adhyayan there is no "promote the oldest waitlister" logic anywhere. A freed Utsav seat is not offered to the queue.[src: helpers/utsavBooking.helper.js:402-415] - Pagination off-by-one:
FetchUpcomingcomputesoffset = (page − 1) * (pageSize − 1)instead of* pageSize, so pages overlap/skip.[src: controllers/client/utsavBooking.controller.js:38] createUtsavruns inside a transaction but has no local try/catch rollback (relies on the globalreq.transactionmiddleware to roll back on throw).[src: controllers/admin/utsavManagement.controller.js:195-274]- Utsav-by-id (and upcoming) hide any utsav whose
registration_deadlinehas passed — so a member cannot open the detail of a utsav they already booked once registration closes.[src: controllers/client/utsavBooking.controller.js:61,298] - Public write routes with no auth (
utsavCheckin,issue/:cardno) are a security surface — anyone who can reach the API can check a member in or issue a plate.[src: routes/admin/utsavManagement.routes.js:44-46,50] openUtsavSeatonly returns a seat when the utsav isopen; cancelling from aclosedutsav silently does not restore the seat.[src: helpers/utsavBooking.helper.js:406]- Cancel path uses
booking.status !== STATUS_WAITINGto decide food provisioning at creation, but cancel always callscancelUtsavFoodBookings— for a waitlisted booking (which never got food) this is a harmless no-op but relies on RC + package lookup.[src: helpers/utsavBooking.helper.js:135-136] - Admin "overall" column is always blank.
fetchUtsavFeedbacksreadsanswersObj.overall_rating, but the survey's question ids areevent_rating/stay_rating/food_rating/program_rating/… — there is nooverall_rating, so the flattened value always falls back to'-'.[src: controllers/admin/utsavManagement.controller.js:1776-1777; controllers/client/utsavBooking.controller.js:344-369] - Utsav feedback window inconsistency: the list-screen button flag uses a fixed 8-day window, but the backend gate accepts submissions up to 15 days for a long utsav (≥ 8 days); for a short utsav they agree.
[src: controllers/client/utsavBooking.controller.js:180; helpers/utsavBooking.helper.js:689-692] - The Utsav feedback button flag excludes
cash completed; the gate includes it (a cash payer may need the deep link).[src: controllers/client/utsavBooking.controller.js:181; helpers/utsavBooking.helper.js:726-730] getAll(AVT) returns the entirecard_dbwith no pagination or column projection.[src: controllers/admin/avtManagement.controller.js:4-9]- The AVT photo "protection" is client-side only (overlay + disabled events); the
pfpURL is still present in the API response. (Client-side concern;pfpis a plain column in the API response.)
How this connects to other domains (technical)¶
- Booking Lifecycle & engine (business-logic 03): both domains create seats via the shared
createPendingTransaction, and cancel viauserCancelBooking/adminCancelTransaction/cancelTransaction(credits, refunds, cash flows all live there).pendingbookings inherit the engine's 24-hour payment-expiry behavior. Utsav credit issuance on admin-cancel (issueCredits) feeds the credits ledger. - Rooms & food (business-logic 04, 05): a Research-Centre Utsav auto-blocks room dates (
block_dates) and auto-books/cancels meals (food domain, package-dated); occupancy reports readroom_booking. Adhyayan food is a simplefood_allowedflag surfaced in the app; RC shibirs bundle room/food/travel add-ons via the client pipeline. - Travel (06): the festival-date location list switch and the self-reported
leaving_post_adhyayanflag tie travel to event/adhyayan schedules; the planned adhyayan cross-check would readShibirBookingDb/ShibirDbstart/end dates to replace it. - Guest & Mumukshu booking (business-logic 02): the registration modal routes into
booking/guestBooking/mumukshuBookingstacks; admins can book on members' behalf (booking/admin,/utsav/booking) which setbookedBy = nulland start aswaiting. - Notifications (business-logic 10): status changes fan out via push (
sendDualUserNotifications— primary + booker), email (rajAdhyayanUpdate/utsavStatusUpdatetemplates), and WhatsApp (sendAdhyayanStatusChangeWhatsApp/sendUtsavStatusChangeWhatsApp). - Feedback ↔ Bookings tab: the
getbooked/bookinglist endpoints compute theshowFeedback/hasSubmittedFeedbackflags the Bookings tab uses to render the "Give Feedback" button. - Admin Panel Map (11): Adhyayan/Utsav/AVT pages and their role gating (10 adhyayan roles, 6 utsav roles, AVT-only) are catalogued there; attendance scanners (mobile QR + tap-card) and the PGS report are Adhyayan-specific admin surfaces.