Skip to content

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.md on 2026-07-07 against aashray-backend main. 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 default open status comes from the model default, not an explicit set in createAdhyayan).


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 duplicate speaker + start_date pair; derives month from start_date; sets available_seats = total_seats; default status open. If location === '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 > 0 and status === 'open' → reserve a seat (decrement), set status = pending when amount > 0 else confirmed; 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 a confirmed / waiting / pending booking for the same shibir twice.
  • Validity window (validateAdhyayans): the shibir must exist and its start_date >= today − 15 days (so recently-past shibirs are still bookable/valid; the variable is misnamed sevenDaysAgo). [src: helpers/adhyayanBooking.helper.js:109]
  • Availability preview (checkAdhyayanAvailabilityForMumukshus): for a group, computes available = min(available_seats, groupSize), waiting = groupSize − available, charge = available × amount — only when status === 'open', else everyone waits. [src: helpers/adhyayanBooking.helper.js:403-409]
  • Update (updateAdhyayan): if total_seats changed, available_seats shifts by the same delta (floored at 0); else an explicit available_seats override 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): flips shibir_db.status to the :activate path value (open/closed). A closed shibir forces new bookings to waiting.
  • Soft delete (softDeleteShibir): sets status deleted (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: confirmedconfirmed+cash completed; pendingpending+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 2 regular sessions (numbered (d−1)*2+1 at 10:00, (d−1)*2+2 at 15:45) and 1 MV session (numbered 2*days + d at 04:30). Orphaned sessions/records with session_number > totalSessions are 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 shibir location === 'Research Centre'. Idempotent (skips if a row for the booking exists). Seeds session_1..9 = 1 (all first-9 sessions marked "applicable"), sets days, and initializes shibir_sessions if absent. Created automatically whenever a booking becomes confirmed/pending.
  • Reset (resetShibirAttendance): deletes both the shibir_attendance_db row and all shibir_attendance_records for a booking (called on cancel / admin-cancel).
  • Marking: only bookings that are confirmed (or cash 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), reads cardnumber=<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 a shibir_attendance_records row (attended = true); if session_no ≤ 9 it also flips the legacy session_N_attendance column.
  • Report (fetchAdhyayanAttendanceReport): auto-initializes sessions if none exist, then builds a per-confirmed-registrant row with Yes/No for each session (from the records table). Summary (fetchAdhyayanAttendanceSummary): counts attended per session and derives absentees = totalRegistrants − attended.
  • PGS report (fetchPGSpgsReport.js): a special view of shibirs whose name 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): validates starting_meal/ending_meal (arrays of breakfast|lunch|dinner, non-empty, no dupes); requires registration_deadline strictly before start_date; rejects duplicate name + start_date; sets available_seats = total_seats, status open, location default Research Centre. If Research Centre, it also creates a block_dates row (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 hybrid available_seats logic as shibir (delta on total_seats change, 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 via JSON_ARRAYAGG; only surface utsavs whose registration_deadline is 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 requires amount > 0 and rejects in-request duplicate names. updateUtsavPackage edits name/dates/amount.
  • Samvatsari overlap guard (checkOverlapWithSamvatsari): hardcoded SAMVATSARI_PACKAGE_ID = 21 and 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 ≤ 0waiting; else amount > 0pending (decrement seat); else → confirmed (free package auto-confirm, decrement seat). If the utsav is closed, force waiting. 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's starting_meal applies only if the package starts on the utsav start date, and ending_meal only if it ends on the utsav end date (boundary-day meal trimming). Delegates to bookFoodForAllMeals. [src: helpers/utsavBooking.helper.js:152-161]
  • Admin booking (bookUtsavForMumukshusAdmin): always creates waiting, 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 sets checkedin; 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 (issuePlateissueFoodPlate) hands out a physical meal plate. Room assignment via uploadRoomNoExcel matches each row by cardno||utsavid||packageid against confirmed/checkedin bookings and validates the bookingid before a single bulk UPDATE; mismatches are skipped and reported. Occupancy reports (ReservationReport) join room_booking for 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_db 1—N shibir_booking_db (shibir_id). A booking belongs to a card_db (cardno) and optionally a booker (bookedBy, null for self). shibir_db also 1—N adhyayan_feedback, shibir_sessions, shibir_attendance_db, shibir_attendance_records. Booking status ENUM does not include cash completed/cash pending in the model definition (allowed values are waiting, 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, and shibir_attendance_records all reference shibir_db.id. Records reference shibir_booking_db.bookingid (unique on (bookingid, session_number)) and card_db.cardno. The legacy shibir_attendance_db is a denormalized convenience mirror of the first 9 sessions.
  • adhyayan_feedbackshibir_db (shibir_id) and card_db (cardno). Unique index unique_feedback_per_user_per_adhyayan (shibir_id, cardno).
  • utsav_db 1—N utsav_packages_db and 1—N utsav_booking (utsavid) and 1—N utsav_feedback. A booking references a package (packageid), a card (cardno), an optional booker (bookedBy), and carries arrival, carno, volunteer, roomno, other, status. Creating a RC utsav also writes block_dates.
  • utsav_booking.roomno is a free string (assigned by admin, not a FK to room_db). ReservationReport cross-references room_booking + card_db. Food bookings are created in the food domain keyed on package dates + cardno.
  • utsav_feedbackutsav_db + card_db; utsav_feedback_answersutsav_feedback (feedback_id, cascade delete). question_type ENUM(rating,text).
  • AVT reads card_db directly; no event tables involved.

Edge cases & branches

Admin adhyayanStatusUpdate state machine

  • Same-status or already-cancelled (cancelled/admin cancelled) → rejected.
  • confirmed: if from waiting, reserve a seat; create the pending transaction if missing; mark the transaction completed; ensure an attendance entry exists.
  • pending: rejected from confirmed; only from waiting (reserve seat, create transaction). If credits already fully cover it (transaction.status === completed), it is auto-upgraded to confirmed with a confirmation notification.
  • admin cancelled: from confirmed/pending it opens the seat (auto-promoting the oldest waitlister via openAdhyayanSeat); resets attendance; cancels the transaction (admin cancel path).
  • waiting: always invalid.

Admin utsavStatusUpdate state machine

  • Same-status or already admin cancelled → rejected.
  • confirmed: only from pending or cancelled. Creates/reopens/completes the transaction and books food. (There is a dead if (status === waiting) reserveUtsavSeat branch that the guard makes unreachable.)
  • pending: only from waiting. 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: from waiting/pending/confirmed/cancelled. Cancels food (RC), frees the seat if it was confirmed/pending, and handles the transaction per the issueCredits flag (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 ≤ 9 guard just skips the legacy mirror), but any consumer reading the old session_N_attendance columns 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 pending bookings too (not just confirmed), but only confirmed rows are counted in reports and eligible to be marked.

Feedback

  • The showFeedback flag in FetchBookedShibir uses end_date + 15 days and requires status === confirmed; the eligibility gate uses start_date + 15 days and accepts confirmed or cash completed.
  • The list-screen showFeedback flag in ViewUtsavBookings uses a fixed 8-day window and accepts confirmed/checkedin; the eligibility gate uses 8-or-15 days and accepts confirmed/cash completed/checkedin.

Utsav add-ons

  • Check-in and plate-issue are public (unauthenticated) routes — gate volunteers use them without an admin login.
  • uploadRoomNoExcel requires all rows to share one utsavid and builds a raw SQL CASE update (string-interpolated bookingids/roomnos).
  • Free (₹0) packages auto-confirm at booking time and again on the admin pending transition.

AVT

  • getAll returns the entire card_db with 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 pfp URL is still in the response.

Discrepancies

  1. softDeleteShibir orphans bookings. It flips the parent to deleted and 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]
  2. validateAdhyayans misnomer. The "7 days ago" variable (sevenDaysAgo) actually subtracts 15 days. [src: helpers/adhyayanBooking.helper.js:109]
  3. getAdhyayanBookings helper is dead/broken. It uses [Op.in] but never imports Op (only Sequelize). Not reached by any live flow, but it would throw. [src: helpers/adhyayanBooking.helper.js:36,432 (no callers)]
  4. fetchAdhyayanBookings status test if (status != null || status != undefined) is always true (|| of two negations) — harmless (it just trims), but logically wrong. [src: controllers/admin/adhyayanManagement.controller.js:322]
  5. FetchShibirById has no status filter → it will return deleted and long-past shibirs by id. [src: controllers/client/adhyayanBooking.controller.js:263-280]
  6. Dual attendance schema drift. The fixed session_1..9 mirror vs the dynamic shibir_attendance_records table 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]
  7. Attendance is silently a Research-Centre-only feature (createShibirAttendanceEntry returns 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]
  8. Adhyayan feedback window inconsistency. The button-visibility flag counts 15 days from end_date and requires status === confirmed, but validateFeedbackEligibility counts 15 days from start_date (despite the error message saying "within 15 days after the adhyayan ends") and accepts confirmed/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)]
  9. The button flag requires confirmed; the gate also allows cash 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]
  10. No waitlist auto-promotion on Utsav. openUtsavSeat merely increments available_seats (and only if the utsav is still open); 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]
  11. Pagination off-by-one: FetchUpcoming computes offset = (page − 1) * (pageSize − 1) instead of * pageSize, so pages overlap/skip. [src: controllers/client/utsavBooking.controller.js:38]
  12. createUtsav runs inside a transaction but has no local try/catch rollback (relies on the global req.transaction middleware to roll back on throw). [src: controllers/admin/utsavManagement.controller.js:195-274]
  13. Utsav-by-id (and upcoming) hide any utsav whose registration_deadline has 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]
  14. 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]
  15. openUtsavSeat only returns a seat when the utsav is open; cancelling from a closed utsav silently does not restore the seat. [src: helpers/utsavBooking.helper.js:406]
  16. Cancel path uses booking.status !== STATUS_WAITING to decide food provisioning at creation, but cancel always calls cancelUtsavFoodBookings — 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]
  17. Admin "overall" column is always blank. fetchUtsavFeedbacks reads answersObj.overall_rating, but the survey's question ids are event_rating/stay_rating/food_rating/program_rating/… — there is no overall_rating, so the flattened value always falls back to '-'. [src: controllers/admin/utsavManagement.controller.js:1776-1777; controllers/client/utsavBooking.controller.js:344-369]
  18. 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]
  19. 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]
  20. getAll (AVT) returns the entire card_db with no pagination or column projection. [src: controllers/admin/avtManagement.controller.js:4-9]
  21. The AVT photo "protection" is client-side only (overlay + disabled events); the pfp URL is still present in the API response. (Client-side concern; pfp is 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 via userCancelBooking / adminCancelTransaction / cancelTransaction (credits, refunds, cash flows all live there). pending bookings 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 read room_booking. Adhyayan food is a simple food_allowed flag 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_adhyayan flag tie travel to event/adhyayan schedules; the planned adhyayan cross-check would read ShibirBookingDb/ShibirDb start/end dates to replace it.
  • Guest & Mumukshu booking (business-logic 02): the registration modal routes into booking / guestBooking / mumukshuBooking stacks; admins can book on members' behalf (booking/admin, /utsav/booking) which set bookedBy = null and start as waiting.
  • Notifications (business-logic 10): status changes fan out via push (sendDualUserNotifications — primary + booker), email (rajAdhyayanUpdate / utsavStatusUpdate templates), and WhatsApp (sendAdhyayanStatusChangeWhatsApp / sendUtsavStatusChangeWhatsApp).
  • Feedback ↔ Bookings tab: the getbooked / booking list endpoints compute the showFeedback / hasSubmittedFeedback flags 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.