Discrepancies & Open Questions¶
Part of the Aashray Technical Reference. This is the aggregated, backend-verified discrepancy log surfaced while auditing all three repos. Each item was found by reading actual backend/admin/app code on
main— these are not guesses. Severity is informal: 🔴 security/data-integrity, 🟠 functional bug, 🟡 inconsistency/UX gap, ⚪ note.
Per-domain detail and code-level context live in the "Discrepancies" section of each domain's technical reference file (this directory); the paired business-logic files instead carry a short, plain-language "Known limitations today" note wherever a gap is genuinely user-visible. This page is the cross-cutting index across all domains so nothing gets lost, sorted by severity rather than by domain. Superseded/duplicated app-only findings from the app's own data-model/appendices documentation (Appendix E) are folded in here where the backend confirms or corrects them.
🔴 Security & auth¶
- No member session token. Client (
/api/v1/*app-facing) endpoints are gated only byvalidateCard— a check that the suppliedcardnoexists inCardDb. There is no password/JWT check per request; possession of a 10-digit card number is effectively the bearer credential for that member's data. See 02, 01. POST /admin/auth/reset-passwordhas no auth middleware. Any caller who knows an admin username can reset that admin's password — a privilege-escalation path. See 02.POST /razorpay/verifyPaymentis a public, unauthenticated webhook with no signature verification. Anyone who can guess/construct the right JSON can mark a transaction paid and confirm a booking without ever paying. See 08.- Shared default password. Every new
CardDbrow is created with the same hardcoded bcrypt hash, and the "reset to default" admin action resets a member back to that same, effectively public, password. See 02. - Temporary password is only 5 alphanumeric characters (a code comment incorrectly claims special symbols are included) — weak for a credential sent over WhatsApp/email. See 02.
fetchTotalTransactionsbuilds SQL by string-interpolatingcardno(flagged// TODO: FIX thisin the code) — a SQL-injection surface. See 02.- Several admin/public endpoints are intentionally unauthenticated (kiosk/scanner use cases) but worth a deliberate security review:
POST /utsav/utsavCheckin,POST /utsav/issue/:cardno, thelocationadmin router (missingauthorizeRoles), and the short-link/go/:slugredirect. See 11, 07, 10. - Admin RBAC is broad by design —
superAdminis included in almost every route group's allow-list, and the panel's client-sideroleCheck.jsis cosmetic only (the real boundary is server-sideauthorizeRoles). Not a bug, but worth confirming matches intended least-privilege. See 11, 13.
🟠 Money, pricing & credits¶
- The app's
constants/prices.js(₹400/600/315/120/80/50) is dead/stale. The backend's real base prices areNAC_ROOM_PRICE=700, AC_ROOM_PRICE=1100, BREAKFAST_PRICE=60, LUNCH_PRICE=120, DINNER_PRICE=120(config/constants.js). See 13, 08. FoodRateDB table is never read or written — all food prices are hardcoded constants; there is no admin UI to change rates despite the table existing. See 05.- Flats are always billed at the NAC room rate (₹700/night) — there is no dedicated flat tariff or AC/NAC distinction for flats, despite a code comment implying one should exist. See 04, 08.
- Guest flat booking can double-create a Razorpay order (
bookFlatfor guests omits thecreateOrder=falseflag that the mumukshu path passes) and separately returns an amount already in paise that gets multiplied by 100 again at the controller — a likely charge-amount bug. See 03, 04. RoomBooking/FlatBookinghave noamountcolumn, yet status-update code writes one — Sequelize silently drops it. See 04.- Refund/credit asymmetry: cancelling a paid room or flat booking always credits the member; cancelling travel or utsav does not auto-credit (only an admin-initiated credit does); adhyayan credit is neither previewed nor earned automatically. This is a real, user-visible inconsistency, not just a doc gap. See 04, 06, 07, 08.
adjustAmountcallsaddCredit(user, …)butuseris not an in-scope parameter — a latentReferenceErrorin the admin re-price path. See 08.- Country-casing bug: booking code compares
country != 'India'while the auto-cancel cron filters oncountry = 'INDIA'— mismatched casing could mean some international-flagged cards are never auto-cancelled as expected (or vice versa). See 08. /api/v1/razorpay/pay(v1) rejects any food line item;/payv2allows food when the category matches — the two payment endpoints behave inconsistently for the same booking type. See 08.- Self-paid food transactions are created
pendingbut food cannot be paid online — they only ever clear via cash payment or the 24h auto-cancel cron, an easy-to-miss dead-end state. See 05, 08. - Guest self-paid meals aren't refunded on cancel —
cancelFoodonly reverses a transaction whenbookedForis set (an in-code FIXME confirms this is known). See 05. - Reconciliation is fully manual (Excel upload) and cross-app "Satshrut" payments can only be recovered by parsing raw
razorpay_webhookJSON — no structured link. See 08. - Settlement schema has a misspelled column
cerated_atthat is relied on throughout;settled_byis written on reconciliation upsert but not declared on the model. See 08.
🟠 Booking engine, availability & waitlists¶
- The
/api/v1/unified/*booking/validate routes are dead — they return HTTP 410. The real "unified" booking engine lives under/api/v1/guestand/api/v1/mumukshu. Per-type flat shortcuts (/stay/flat,/guest/flat) are marked deprecated but still function. See 03, 12. - Admin
bookings/cancel/:typeonly implements theroomcase — every other booking type falls through the switch to a 404; flat cancellation is handled by a separate endpoint entirely. See 03. - Room waitlist is never auto-promoted (unlike adhyayan/utsav/travel, which do auto-promote on a cancellation/seat-open). A room's
WLstatus appears not to clear automatically. See 04. - Utsav seats are never auto-promoted from the waitlist either —
openUtsavSeatonly increments a counter (and only while the utsav is stillopen); cancelling from aclosedutsav silently fails to restore a seat for anyone waiting. This is the mirror-image gap to #24. See 07. - Travel waitlist logic appears dormant: booking creation always sets
awaiting confirmation(neverwaiting) and reportswaitingBookingCount: 0, yet promotion logic and awaitingcolumn default still exist in the code — suggesting a half-migrated feature. Promotion, when it does run, matches only on the Research-Centre end + date, ignoring the specific city. See 06. - Travel's Research-Centre XOR pairing rule is validated client-side and on
/validate, but the actual create endpoint (bookTravelForMumukshus) never re-checks it — a client that skips validation could create an invalid pickup/drop pair. See 06. softDeleteShibir(adhyayan) orphans bookings — it flips the shibir todeletedand notifies registrants but never cancels or refunds their bookings. See 07.- Free-adhyayan waitlisters get promoted to
pending(with a ₹0 transaction) rather than being auto-confirmed. See 07. - Block dates are enforced for room bookings but not for flats, and the overstay/late-checkout extension handler exists in code but its call site is commented out (dead feature). See 04.
- Three error constants are referenced but never imported in
roomManagement.controller.js(ERR_INVALID_DATE,ERR_ROOM_FAILED_TO_BOOK,ERR_BOOKING_ALREADY_CANCELLED) — those branches throw a rawReferenceErrorinstead of a clean API error. See 04. pickupRC/dropRCadmin travel filters are dead code — they query for the literal string'RC', but the stored value is'Research Centre', so these filters match zero rows. See 06.- A cron routine to cancel unpaid past room/flat bookings exists in code but its call is commented out — only the 24-hour online-pending auto-cancel rule is actually active. See 13.
🟡 Content, data-model & UX inconsistencies¶
- The app's Credits card labels (Stay / Travel / Food / Utsav) don't match the "How to Use" modal copy (Room / Flat / Utsav / Guest Food) — a client-side copy inconsistency, not a backend issue. See the app's own data-model/appendices documentation, Appendix E.
- Flat credits fold into the same pool as Room credits (
getCreditType: flat → room) — undocumented anywhere in the UI, so a member who cancels a paid flat booking sees the credit appear under "Stay," which may be surprising. See 04. - Adhyayan feedback eligibility window is inconsistent between what's shown and what's enforced: the "Give Feedback" button's visibility counts 15 days from the shibir's
end_date, but the backend's actual eligibility gate counts 15 days fromstart_date— despite the error copy saying "after the adhyayan ends." A parallel 8-vs-15-day mismatch exists for Utsav. See 07. - Admin's Utsav feedback report always shows a blank "overall" rating — it reads a column
overall_ratingbut the actual survey question id isevent_rating. See 07. - WiFi's advertised validity periods are fiction: the app/admin copy claims "permanent = 1 year," "temporary = 2 weeks + data limit," but the backend enforces no expiry and no data cap at all.
wifi_pwdstatus semantics are also inverted (active= free/unclaimed,inactive= claimed) — a naming trap for anyone reading the code. See 09. deviceTypeis used in WiFi code but not declared on thepermanent_wifi_codesmodel — likely an unmodeled/ad-hoc column. See 09.- Support tickets have no admin surface at all. There is no admin route, controller, or panel page for support; tickets are effectively write-only from the member's side, the model has no status/response field, and the staff email notification is commented out in code. This is the single biggest functional gap found in the audit — members can submit a ticket that no one can ever see or resolve through the product. See 09.
- Maintenance eligibility only checks for a room check-in — flat and utsav guests can be locked out of filing a maintenance request even though they're on-site, because the eligibility check doesn't consider those booking types. See 09.
- Gate entry/exit is asymmetric: entry auto-checks-in both room and flat bookings; exit only auto-checks-out flat bookings, so a room-booked member's status can linger in
checkedinafter they've actually left.gateExitis also missing the null-guard thatgateEntryhas, a latent 500 on an unknown card. See 09. - Push notifications are Expo (via
expo-server-sdk), not raw Firebase/FCM as some client-side references imply — Firebase is only the underlying Android transport. See 10. - Push token lifecycle has no refresh path: the Expo token is written once at login and cleared at logout; stale tokens from
DeviceNotRegisteredreceipts are logged but never removed, and only one token is stored per card (last device wins). See 10. - Default push-notification deep-link target is inconsistent between two code paths —
NotificationServicedefaults to'/', whilesendDualUserNotificationsdefaults to'/home'. See 10. - The remote "force update" / app-config endpoint (
GET /api/v1/updates?os=) is undocumented on the client side — it exists and is public, but there's no admin UI to manageupdatesrows, and "latest" is resolved bycreatedAtrather than a proper semantic-version comparison. See 10. - Meal-count baseline state lives in a local file (
last_meals_count.json) — not durable across restarts or multiple server instances, so a meal-count-changed WhatsApp alert can be silently skipped after a deploy. See 05, 10. /go/:slugshort-link redirects are public and incrementclick_counton every hit, including bot/prefetch traffic, so click analytics are inflated; a disabled short link returns a bare 404 with no explanation. See 10.- Admin's
/admin/locationroutes are dead code —app.jsactually mounts the client location router at that admin path, so the parallelcontrollers/admin/location.*files are never reached. See 02, 12. - Admin's
updateCardguest-relationship write uses the wrong column names (referenceCardno/guestType/createdByinstead of the model'sguest/type/updatedBy) and keys on the member's card rather than the guest's — so editing an existing guest card silently fails to update the relationship row, while creating a new one works correctly. See 02. - Admin panel
cardDetails.jscallsPUT /admin/card/update/:cardno, but the backend route expectscardnoin the request body, not the URL — this call would 404 as written. See 02. - Admin's meal-count report page is broken:
mealCount.jschecksresult.success, but the backend'sgetMealCountByMobileresponse never includes asuccessfield, so every valid response renders as a failure in the UI. See 05. - Inconsistent food-booking cutoff enforcement: the member-facing 11 AM cutoff is enforced only client-side (by the app's calendar); the general food-booking API itself enforces no such cutoff. The Smilestones-admin bulk-food tooling additionally uses two different cutoffs between its create (11 AM) and edit (8 PM) flows. See 05.
- No database-level uniqueness constraint on
food_db (cardno, date)— the one-meal-record-per-day-per-meal-type invariant is enforced only in application code, not the schema. See 05. - Route-prefix trap: the admin "Room Management" module is mounted at
/api/v1/admin/stay, not/admin/roomas its name would suggest — easy to mistrace when following the code. See 11. - The
smilestonesadmin folder is largely a stub — thesmilesAdminrole's real working entry point isfood/manageBulkFood.html, reached from the home menu rather than a dedicated Smilestones module. See 11. - Non-ENUM statuses referenced in code:
updateBookingStatus(travel) has branches forseats full cancel/wrong form cancelthat would violate the declaredtravel_db.statusENUM if ever hit; the real cancellation path usesadmin cancelled+ a comments field instead. See 06. coordinator_bookingidon travel bookings is a plain string, not a declared foreign key, and theis_coordinatorflag can only be toggled when abus_group_idis also supplied in the same request. See 06.- Attendance schema drift for long adhyayans: the legacy
session_1..9columns onShibirAttendanceDbcannot represent a shibir longer than 9 sessions (~3 days); longer sessions are tracked only in the newershibir_attendance_recordstable, so the two attendance sources can disagree for multi-day shibirs. See 07. - Pagination off-by-one in
FetchUpcoming(adhyayan/utsav browse list):offset = (page - 1) * (pageSize - 1)— subtly wrong page-size arithmetic that will cause overlapping or skipped items across pages. See 07.
⚪ Notes (not bugs, but worth knowing)¶
- Hardcoded operational recipients exist in several places: the WiFi low-code WhatsApp alert number, and the specific card numbers used for nightly meal-count notifications. Changing staff assignments currently requires a code deploy. See 09, 10.
- (Resolved) This documentation set went through two structural changes since it was first assembled — first consolidating a duplicated app-spec copy, then splitting every domain file into a business-logic/technical pair. Both rounds have been fully reconciled and link-checked; if you ever find a dangling
[N](...)link, it's a new regression, not a leftover from either change. - This holistic set treats
mainon each repo, read at audit time, as ground truth. Bothaashray-adminandaashray-backendhad active feature branches (feat/support-ticketingat the time of this audit) not yet merged tomain— anything already fixed there (e.g., a support-admin surface, per finding #40) would not yet be reflected here. Re-run the relevant domain section after such branches merge.
Open questions for the product/eng team¶
- Is the lack of a real member session token (#1) intentional (trusted-network assumption) or a genuine gap to close?
- Should room and utsav gain waitlist auto-promotion (#24, #25) to match adhyayan/travel, or is manual admin promotion intended for those types?
- Is the credit-on-cancel asymmetry (#14) a deliberate business rule (only stay-type bookings are refundable-as-credit) or an oversight to fix uniformly?
- Does Support (#40) already have an admin surface on an in-progress branch (e.g.
feat/support-ticketing), or is this genuinely unbuilt? - What are the intended WiFi validity/data-cap rules (#38) — should the backend actually enforce what the UI claims, or should the UI copy be corrected instead?