Staging Bugs - All Issues
Document Version: 5.0 Last Updated: 2026-06-25 Total Bugs: 62 Resolved & Verified: 49 | Pending: 13
✅ QA Verification Summary (PR #1735)
Source: GitHub PR #1735 Verified by: SinglaKartik Dates: 2026-06-24, 2026-06-25
| Category | Count | Bug Numbers |
|---|---|---|
| 🔴 Critical Bugs | 5 | #10, #17, #24, #26, #29 |
| 🟡 High Priority | 19 | #1, #2, #8, #9, #19, #21, #31, #32, #36, #38, #44, #45, #46, #47, #51, #55, #56, #62 |
| 🟠 Medium Priority | 16 | #5, #14, #15, #18, #20, #22, #23, #25, #31, #34, #48, #49, #52, #53, #54 |
| 🟢 Low Priority | 2 | (included in above) |
| ✅ Patient App (2026-06-25) | 7 | #3, #4, #5, #6, #7, #35, #6b |
| TOTAL VERIFIED | ~49 |
✨ New Feature Added (2026-06-25)
- Confirm Follow-Up Flow: Added confirm button, confirmation modal, and "Confirmed" tab in Follow-ups page
🔧 Bug Fixes (2026-06-25)
- Bug #3: OTP paste in reset password - Fixed missing paste handler in Patient App
- Bug #4: Photo upload preview - Already working, verified
- Bug #5: DOB allows future dates - Added
.refine()validation + max date attribute - Bug #6: No format validation on pincode/phone - Added regex validation (6 digits for pincode, 10-15 for phone)
- Bug #6b: Clear form fields not saved - Fixed backend to clear empty JSONB fields
- Bug #7: Special characters in name fields - Added regex to allow only letters, spaces, hyphens, apostrophes
- Bug #35: Email field missing in Patient App - Added email field to profile form
- Backend Fix: Rails update doesn't clear empty JSONB fields - Added explicit clearing logic
Bug Summary by Status
| Category | Count | Notes |
|---|---|---|
| ✅ Verified Fixed | 49 | All bugs fixed and tested |
| 🔧 Deferred (New Billing) | 5 | #11, #12, #13, #50, #37, #57, #60 |
| 🔧 Deferred (Architecture) | 2 | #58, #59 |
| ⚠️ DevOps | 1 | #30 - Sidekiq down on staging |
| ✅ Not a Bug | 2 | #33 (expected), #15 (fixed by #14) |
| TOTAL | ~59 | Excludes duplicate entries |
🔴 Critical Bugs (Block Production)
Bug #10: Cross-App Authentication Not Enforced
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 10 |
| Area | Authentication |
| Session | 03-clinic-app-auth |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Users can login to applications they shouldn't have access to based on their role.
Resolution: Backend ALLOWED_APPS role-to-app gate in SessionsController. CMS login sends app: 'cms', patient login sends app: 'patient' as top-level param. Gate checks before issuing tokens — 403 APP_ACCESS_DENIED on cross-app attempts.
Issues Identified:
- Patient accounts can login to Clinic/CMS app
- Staff/Doctor/Admin accounts can login to Patient app
Expected Behavior:
- Patient account → Patient app ONLY
- Staff/Doctor/Admin → Clinic app ONLY
Actual Behavior:
- No app-level authentication enforcement
- Any registered phone can login to either app
Bug #17: Cross-App Forgot Password Not Enforced
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 17 |
| Area | Authentication - Forgot Password |
| Related to | Bug #10 |
| Session | 03-clinic-app-auth |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Forgot password allows cross-app password reset.
Resolution: Cross-app check moved BEFORE ResetService.call in PasswordsController#reset — SMS never sent if user can't access the requesting app. Frontend forgot-password pages check response message and stay on page (no OTP navigation) when blocked.
Issues Identified:
- Patient account can reset password via Clinic app
- Clinic account can reset password via Patient app
Bug #24: Invite Sent to Registered Email
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 24 |
| Area | Invite Flow |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: Invite sent to email that already exists in system.
Current Flow:
- Invite created ✅
- User opens link
- Signup fails ❌ - "email already exists"
Expected: Block invite creation Actual: Invite created, acceptance fails
Bug #26: Invite to Active User
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 26 |
| Area | Invite Flow |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: Already active clinic members can receive new invites.
Expected: Block with error "User is already a member of this clinic" Actual: Invite created successfully
Bug #29: Doctor Can Set Other Doctor's Schedule — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 29 |
| Area | Availability - Authorization |
| Session | 06-availability-booking |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Resolution: Fixed in commit 7c713c176 — fix(identity): restrict practitioner schedule writes to owner/admin or self (#1689). The PractitionerSchedulePolicy now correctly gates create?/update?/destroy? on tenant_admin? || tenant_owner? || owns_practitioner_role?.
🟡 High Priority Bugs
Bug #2: Resend OTP Not Working (Patient)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 2 |
| Area | Patient Signup |
| Session | 01-patient-app-auth |
| Status | ✅ Closed |
Description: Resend OTP not working in patient signup flow.
Bug #9: Resend OTP 500 Error (Clinic)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 9 |
| Area | Clinic Signup |
| Session | 03-clinic-app-auth |
| Status | ✅ Closed |
Description: Resend OTP gives 500 Internal Server Error.
Bug #19: Backend Validation Error Not Displayed
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 19 |
| Area | Invite Flow |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: Frontend shows generic toast instead of backend error message.
Backend Response:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Invite already exists"
}
}Frontend shows: "Unable to send. Please try again." Should show: "Invite already exists"
Bug #21: Practitioner Invite to Existing Patient
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 21 |
| Area | Invite Flow |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: Mobile registered as patient can still receive doctor invite.
Expected: Block invite creation Actual: Invite created successfully
Bug #27: Set Consultation Fee (PATCH API)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 27 |
| Area | Online Booking |
| Session | 06-availability-booking |
| Status | ✅ Closed |
Description: Unable to set consultation fee via PATCH API.
API: PATCH /identity/practitioner_roles/:id
Bug #28: Set Telemedicine Fees (PATCH API)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 28 |
| Area | Online Booking |
| Session | 06-availability-booking |
| Status | ✅ Closed |
Description: Unable to set telemedicine fees via PATCH API.
API: PATCH /identity/practitioner_roles/:id
Bug #30: Newly Created Doctors/Hospitals Not Appearing in Search
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 30 |
| Area | Patient Search |
| Session | 07-patient-search |
| Status | ✅ Closed |
Description: Search functionality is working correctly (UI, filters, pagination), but newly created doctors and hospitals are not appearing in search results.
Root Cause: Data synchronization issue between database and Typesense search index.
🟠 Medium Priority Bugs
| Bug # | Description | Area | Session | Status |
|---|---|---|---|---|
| 1 | Error message not user-friendly (Patient) | Patient Signup | 01-patient-app-auth | ✅ Closed |
| 3 | OTP paste in reset password | Forgot Password | 01-patient-app-auth | ✅ Verified Fixed (2026-06-25) |
| 4 | Photo upload preview not showing | Profile | 02-patient-app-profile | ✅ Verified Fixed (2026-06-25) |
| 5 | DOB allows future dates | Profile | 02-patient-app-profile | ✅ Verified Fixed (2026-06-25) |
| 6 | No format validation on pincode/phone | Profile | 02-patient-app-profile | ✅ Verified Fixed (2026-06-25) |
| 6b | Clear form fields not saved | Profile | 02-patient-app-profile | ✅ Verified Fixed (2026-06-25) |
| 7 | Special characters in name fields | Profile | 02-patient-app-profile | ✅ Verified Fixed (2026-06-25) |
| 8 | Error message not user-friendly (Clinic) | Clinic Signup | 03-clinic-app-auth | ✅ Closed |
| 11 | Edit service not working (PATCH) | Clinic Settings | 04-clinic-app-settings | 🔧 Deferred |
| 12 | Edit procedure not working (PATCH) | Clinic Settings | 04-clinic-app-settings | 🔧 Deferred |
| 13 | Edit vital not working (PATCH) | Clinic Settings | 04-clinic-app-settings | 🔧 Deferred |
| 14 | Duplicate name (exact) not rejected | Patient Categories | 04-clinic-app-settings | ✅ Closed |
| 15 | Duplicate name (case-insensitive) | Patient Categories | 04-clinic-app-settings | ✅ Closed |
| 18 | Active practitioner API missing phone | Invite Flow | 05-clinic-app-invite-flow | ✅ Closed |
| 20 | Whitespace-only first/last name allowed | Invite Flow | 05-clinic-app-invite-flow | ✅ Closed |
| 22 | Cancelled invites not visible | Invite Flow | 05-clinic-app-invite-flow | ✅ Closed |
| 23 | Duplicate invite with same email | Invite Flow | 05-clinic-app-invite-flow | ✅ Closed |
| 25 | Invite modal form state not reset | Invite Flow | 05-clinic-app-invite-flow | ✅ Closed |
| 31 | Federated search full name | Add Patient | 08-add-patient-booking | ✅ Closed |
| 32 | DOB not auto-filled from Kulcare | Add Patient | 08-add-patient-booking | ✅ Closed |
| 33 | Patient visible across clinics | Add Patient | 08-add-patient-booking | ✅ Expected Behavior |
| 34 | No validation for patient form | Add Patient | 08-add-patient-booking | ✅ Closed |
| 35 | Email field missing in Patient App | Patient App - Profile | 02-patient-app-profile | ✅ Verified Fixed (2026-06-25) |
| 48 | Out-of-queue split inconsistent | Queue | 10-queue-booking | ✅ Closed |
| 49 | No couple session indicator | Queue | 10-queue-booking | ✅ Closed |
| 52 | Delete dialog reason not cleared | Reports | 12-reports | ✅ Closed |
| 53 | Phone not shown in follow-ups | Follow-ups | 11-follow-ups | ✅ Closed |
| 54 | ✓ icon = edit, not confirm | Follow-ups | 11-follow-ups | ✅ Closed |
| 58 | Case-sensitive service grouping | Revenue | 12-reports | 🔧 Deferred |
| 59 | Visit type breakdown incorrect | Revenue | 12-reports | 🔧 Deferred |
✅ Not a Bug
| Bug # | Description | Notes |
|---|---|---|
| 16 | Unregistered phone - forgot password | "User not found" shown correctly |
🆕 New Bugs (Session 08 - Add Patient & Booking)
Bug #31: Federated Search - Partial Name Works, Full Name Doesn't
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 31 |
| Area | Add Patient - Federated Search |
| Session | 08-add-patient-booking |
| Status | ✅ Closed |
Description: Search by partial name (e.g., "mohit") returns results, but search by full name (e.g., "mohit goyal") returns no results.
Steps to Reproduce:
- Search by "mohit" → Results found ✅
- Search by "mohit goyal" → No results ❌
Expected: Full name search should return results Actual: Only partial name search works
API Evidence:
# Partial name - WORKS
curl '/clinical/patients/federated_search?name=mohit'
# Returns: Patient "Mohit Goyal" found
# Full name - FAILS
curl '/clinical/patients/federated_search?name=mohit%20goyal'
# Returns: No resultsBug #32: DOB Not Auto-filled When Importing from Kulcare
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 32 |
| Area | Add Patient - Import from Kulcare |
| Session | 08-add-patient-booking |
| Status | ✅ Closed |
Description: When importing a patient from Kulcare network (in_kulcare bucket), the DOB is not auto-filled even though the patient has DOB set in their Patient App profile.
Steps to Reproduce:
- Patient "Mohit Goyal" has DOB set in Patient App
- Search in Clinic App → Find in "Elsewhere on Kulcare"
- Click "Import" → Form auto-fills
- Check DOB field → Empty ❌
Expected: DOB should be auto-filled from Kulcare profile Actual: DOB field is empty (phone, name, gender are auto-filled correctly)
Bug #33: Patient Visible Across Multiple Clinics
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 33 |
| Area | Add Patient - Multi-clinic |
| Session | 08-add-patient-booking |
| Status | ✅ Expected Behavior |
Description: When an admin has access to multiple clinics within the same tenant, patients added in one clinic are visible in other clinics.
Verified with Dev Team: ✅ This is expected behavior.
Explanation:
- Patients in the same tenant are shared across all clinics within that tenant
- This is by design for the federated search flow
- Patients can be found and booked across clinics in the same tenant
Architecture:
Clinical::Patientis tenant-scoped via RLSFederatedSearchService.in_clinicsearches within the current tenant- All clinics in the same tenant share the patient pool
Bug #34: No Validation for Patient Form (Frontend + Backend)
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 34 |
| Area | Add Patient - Form Validation |
| Session | 08-add-patient-booking |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Patient form accepts invalid data without validation. BOTH frontend AND backend accepted:
- First name: 100+ characters
- Last name: 100+ characters
- Phone: "dsadasdsadsads" (letters in phone field)
- DOB: Future date (2026-06-15)
Steps to Reproduce:
- Open Add Patient form
- Enter very long name (100+ chars)
- Enter letters in phone field
- Select future DOB
- Submit → Form accepts invalid data ❌
- API stores invalid data → Backend accepts it ❌
API Request That Succeeded:
curl 'https://api-stg.kulcare.com/clinical/patients' \
-H 'Content-Type: application/json' \
--data-raw '{
"first_name":"ddasdsadsadasdsadsadsadsadsafsdfsdfsdfdsfsdfsfdsfdsfdsfdsfdfadsfdsfadsfdsdsadsadsadsadsadsadsa",
"last_name":"dsadsadsadsadsadsadfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsfdsf",
"phone_number":"dsadasdsadsads",
"phone_country_code":"91",
"gender":"male",
"birth_date":"2026-06-15"
}'API Response: 200 OK - Patient created successfully
Issues Found:
- Frontend: No validation for name length, phone format, future DOB
- Backend: No validation for name length, phone format, future DOB
Recommendation: Add validation on BOTH frontend AND backend:
- Name length: min 2 chars, max 50-100 chars
- Phone: digits only, exactly 10 digits
- DOB: must not be future date
Bug #35: Email Field Missing in Patient App Profile Edit
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 35 |
| Area | Patient App - Profile Edit |
| Session | 02-patient-app-profile |
| Status | ✅ Closed |
Description: Patient signs up with email, but the email field is not visible in the profile edit page. Patient cannot update their email later.
Steps to Reproduce:
- Patient signs up with email "test@example.com"
- Go to Profile → Edit
- Check for email field → Not visible ❌
Expected: Email field should be editable in profile settings Actual: Email field missing from edit form
API Evidence: Patient profile PUT request (no email field in payload):
curl 'https://api-stg.kulcare.com/patient/profile' \
-X 'PUT' \
-H 'Authorization: Bearer ...' \
--data-raw '{
"profile": {
"first_name": "Uday",
"last_name": "Bisht",
"birth_date": "2016-01-22",
"gender": "male"
}
}'API Response (shows email is stored but null):
{
"data": {
"id": "019ecefa-da2e-71cc-bb60-cd7d53ee8aa5",
"first_name": "Uday",
"last_name": "Bisht",
"phone_number": "9988776655",
"email": null
}
}Impact: Patients cannot update their email address after signup
🆕 New Bugs (Session 09 - Patient Management List)
Bug #36: Phone Search Returns No Results on Patient List
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 36 |
| Area | Patient List - Search |
| Session | 09-patient-management-list |
| Test ID | LIST-12 |
| Status | ✅ Closed |
Description: Searching by phone number on the patient list page returns no results, even when patients with that phone number exist in the list.
Steps to Reproduce:
- Note a patient's phone number (e.g., ending in "988") from the patient list
- Type the phone digits in the search bar
- Observe: no results returned
API Evidence:
curl 'https://api-stg.kulcare.com/clinical/patients?search=988&page=1&per_page=10' \
-H 'Authorization: Bearer <token>'
# Returns: empty results, even though patient with phone containing "988" existsRoot Cause (Suspected):
The PatientsController#index search scope only queries first_name and last_name columns, not phone_number. Unlike the federated search endpoint which searches across multiple fields, the patient list search is name-only.
Expected: Phone number search should match phone_number column
Actual: Only name fields are searched
Recommendation:
Add phone_number to the search scope in Clinical::PatientsController#index:
scope.where('first_name ILIKE :q OR last_name ILIKE :q OR phone_number ILIKE :q', q: "%#{search}%")Bug #37: Export Report API Fails - EFS Permission Denied (Staging)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 37 |
| Area | Patient List - Export |
| Session | 09-patient-management-list |
| Test ID | LIST-51 |
| Status | ✅ Closed |
Description:
Patient export report (POST /clinical/patients/generate_report) fails with a filesystem permission error on staging. The EFS mount path /opt/kulcare-efs-stg/patient_reports_staging either doesn't exist or the Rails process lacks write permissions.
Steps to Reproduce:
- Go to Patient List → Click Export → Confirm
- Observe: error toast appears
API Evidence:
curl 'https://api-stg.kulcare.com/clinical/patients/generate_report?facility_id=<id>' \
-X POST \
-H 'Authorization: Bearer <token>' \
--data-raw '{}'
# Response: 422 Unprocessable Entity
{
"data": null,
"error": {
"code": "validation_failed",
"message": "Permission denied @ dir_s_mkdir - /opt/kulcare-efs-stg/patient_reports_staging",
"details": [{
"field": "base",
"message": "Permission denied @ dir_s_mkdir - /opt/kulcare-efs-stg/patient_reports_staging"
}]
},
"meta": { "request_id": "da9231c1-734b-4d77-97c0-095c9d473cdc" }
}Root Cause:
Staging infrastructure issue — Dir.mkdir fails with EACCES on the EFS mount path. The directory is either:
- Not mounted on the staging Rails server
- Missing write permissions for the Rails process user (
deploy)
Expected: Report should be generated and emailed to the user
Actual: Ruby Dir.mkdir raises Errno::EACCES — Permission denied
Recommendation:
- Verify EFS mount exists on staging:
ls -la /opt/kulcare-efs-stg/ - Ensure correct permissions:
chown deploy:deploy /opt/kulcare-efs-stg/patient_reports_staging - Consider adding a fallback to
/tmpif EFS is unavailable
Bug #38: Cross-Clinic Patient Flow Incomplete — No Facility Record Created
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 38 |
| Area | Patient List - Cross-Clinic |
| Session | 09-patient-management-list |
| Test ID | LIST-42 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Resolution: Two-part fix — (1) FindOrCreateService#auto_register_patient_facility creates facility link on search/view (commit 47118d2a9). (2) PatientsController#auto_register_facility_links batch-registers missing links on the patient LIST page before returning results. Uses a NOT IN subquery to find unlinked patients and creates PatientFacility records idempotently — one query on first load, zero queries on subsequent loads. All patients in the list now have valid patient_facility_id, all clickable, no crashes.
Files changed: find_or_create_service.rb, patients_controller.rb
Description: Patient "Mohit" was added in Clinic A. The clinic admin switches to Clinic B (same tenant). The patient appears in Clinic B's list — which is correct, patients are shared within a tenant. But the cross-clinic experience falls apart:
Real user flow (what happens today):
- Staff sees patient "Mohit" in Clinic B's list
- Tries to 👁️ View details → ❌ Broken (
/cms/patient/{id}/null) - Staff can ✏️ Edit the patient — it opens (uses
patient.id, not facility ID) - Staff can 📅 Book an appointment — the booking submits successfully
- But after booking, staff STILL can't 👁️ View the patient they just booked
- The appointment exists in Clinic B's queue, but the patient has no facility record in Clinic B — so the appointment has
patient_facility_id: null
This is confusing for users: "I just booked a patient, why can't I see their details? The appointment is in my queue!"
Architectural Gap (how healthcare apps handle this):
In healthcare systems, a patient's relationship with a facility is a first-class concept. There are two common patterns:
| Pattern | Behavior | Used By |
|---|---|---|
| Explicit registration | Patient must be formally registered in each clinic before any action | Large hospital chains with strict data boundaries |
| Implicit/Lazy registration | First access (view, edit, book) auto-creates the facility relationship | Most clinic management apps (Practo, Phreesia, etc.) |
Kulcare currently does neither — it's in an inconsistent middle ground:
- ✅ Patient is visible cross-clinic (tenant-scoped data)
- ❌ No facility record is created on access
- ❌ Booking succeeds but leaves the appointment orphaned
- ❌ View is broken because the URL depends on a relationship that was never established
Decision needed: Should Kulcare adopt implicit registration (auto-create facility record on first access)? This is the standard for multi-clinic platforms where patients flow between clinics naturally. The alternative is to explicitly scope the patient list to only show patients registered in the current facility.
Root Cause: When a cross-clinic patient is accessed (viewed, edited, booked), no Clinical::PatientFacility record is auto-created for the current facility. Two places are affected:
PatientBlueprint— resolvespatient_facility_idonly for current facility →nullfor cross-clinic patients → View link breaksFindOrCreateService#find_patient_facility_by_patient— returnsnilfor existing patients without a facility record in the current facility → Appointment created withoutpatient_facility_id
Why booking "works" but shouldn't: Clinical::Appointment model has belongs_to :patient_facility, optional: true — the appointment is created without validation errors, but it's orphaned from the facility context.
Recommended Fix: Auto-create PatientFacility record on first access. In FindOrCreateService:
if patient_id_provided? && find_by_id
facility = find_patient_facility_by_patient
facility ||= create_patient_facility(find_by_id) # auto-register
return success({ patient: find_by_id, patient_facility: facility })
endThis is the standard healthcare pattern — the act of providing care at a facility establishes the patient-facility relationship. After this fix: View works, appointments carry proper facility context, and the user experience is consistent.
Bug #44: Appointment Duration Not Validated — Allows Overlapping Bookings
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 44 |
| Area | Appointment Booking — Duration & Overlap |
| Session | 10-queue-booking |
| Test ID | B-51 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Two related issues in duration handling:
Issue 1 — Duration not enforced: Zod has max(120) but per-step validateCurrentStep() bypasses it (only checks doctor + time slot). Backend CreateService has no duration validation at all. User can set 500+ minutes and booking succeeds.
Issue 2 — Overlap check uses hardcoded 15-min, not actual duration: AvailabilityService#slot_conflict? had slot_duration = 15 hardcoded. Books 6:45 with 120 min → only checks 6:45 is free, ignores 7:00 and 7:15 which are already booked. Full 6:45→8:45 window overlaps booked slots but validation passes → double-booking.
Resolution (3-part fix):
- UTC timezone fix —
utc_datetimehelper builds UTC Time directly instead ofTime.zone.parse, which re-interpreted UTC HH:MM strings as server local time, shifting the overlap window by the timezone offset. - Appointment conflict check — Added
conflicting_appointmentsmethod to check standalone appointment rows (not just encounters). Freshly booked CMS appointments have no encounter yet, so the old encounter-only check missed them. - Proper overlap formula —
existing.start < new.end AND existing.end > new.startinstead ofBETWEEN, which falsely flagged back-to-back non-overlapping slots as conflicts.
Files changed: availability_service.rb
Bug #48: Out-of-Queue Split Inconsistent — Finished Patients Stay in Queue
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 48 |
| Area | Queue Page — Active/Completed Split |
| Session | 10-queue-booking |
| Status | ✅ Closed |
Description: The queue API splits patients inconsistently:
| Action | Status | In queue[] | In out_of_queue[] |
|---|---|---|---|
| New booking | booked | ✅ | ❌ |
| Start encounter | in_progress | ✅ | ❌ |
| Finish encounter | finished | ✅ (read-only badge) | ❌ |
| Manual → Completed | fulfilled | ❌ | ✅ |
| Cancel | cancelled | ❌ | ❌ (disappears) |
Three issues:
- Finished (encounter) never moves to out_of_queue — "finished" status stays in the main queue as a read-only badge. Only manual "Completed" triggers the OoQ split.
- No path from finished → completed — once encounter finishes, status becomes read-only. No dropdown to transition to completed.
- Cancelled disappears entirely — removed from both lists. Staff has no visibility into cancelled appointments.
Fix: Backend should consistently split based on status — finished AND fulfilled → out_of_queue[]. Encounter-finish flow should auto-transition appointment status to fulfilled so it moves naturally. Cancelled should appear in out_of_queue[] instead of disappearing.
# QueueService — consistent split logic
active_statuses = %w[proposed pending booked arrived]
out_of_queue_statuses = %w[fulfilled finished cancelled noshow]Bug #49: No Couple Session Indicator in Queue Row
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 49 |
| Area | Queue Page — Display |
| Session | 10-queue-booking |
| Status | ✅ Closed |
Description: API returns couple_session: true + spouse info but QueueRow renders nothing — no icon, badge, or spouse name. Staff can't tell it's a couple session from the queue.
Fix: Add indicator when item.couple_session === true — Users icon + "Couple" badge, or show spouse name under patient.
Bug #45: Invite Role Mismatch — Registered User Only Sees Matching Role Invites
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 45 |
| Area | Invite Flow — Role Scoping |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: Admin sends staff invite to a phone registered as "staff". If admin selects role "staff" → invite appears ✅. If admin selects any other role (doctor/admin/vendor) → invite does NOT appear for the user ❌.
Steps: 1) User registered as "staff" 2) Admin sends invite with role "doctor" 3) User checks invites → empty.
Expected: All invites to the user's phone should be visible, or block mismatched invites at creation time.
Bug #46: Invite Role Overwritten at Signup — Always Registers as Staff
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 46 |
| Area | Invite Flow — Role Assignment |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: Admin selects role "doctor" for an unregistered user invite. When user signs up via invite link, role is always "staff" — the selected role is not carried through to signup.
Steps: 1) Admin invites unregistered user as "doctor" 2) User completes signup 3) Role = "staff".
Expected: Invite role should be assigned at signup.
Bug #47: Registered User Receives Signup Link Instead of Accept/Join Link
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 47 |
| Area | Invite Flow — SMS Link |
| Session | 05-clinic-app-invite-flow |
| Status | ✅ Closed |
Description: When inviting an already registered phone number, SMS always contains a signup link. User is already registered — should receive an "accept invite" link to add the clinic role, not re-register.
Steps: 1) User registered in Clinic A 2) Admin in Clinic B invites same phone 3) SMS = signup link 4) User sees signup form, not accept flow.
Expected: Registered users → "Accept Invite" link. Unregistered users → "Signup" link.
Bug #50: Gateway Payment Method Returns 500 Internal Server Error
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 50 |
| Area | Billing — Payments |
| Session | 11-billing |
| Test ID | BILL-44 |
| Status | ✅ Closed |
Description: Gateway payment method crashes with 500. Cash/Card/UPI/Bank work.
API: POST /billing/patient_facilities/:id/payments with payment_method: "gateway" → 500.
Bug #51: Next Visit — Specific Date Fails "Outside Working Hours"
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 51 |
| Area | Billing — Follow-up |
| Session | 11-billing |
| Test ID | BILL-94 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Absolute date mode fails — "Appointment time is outside working hours". Interval mode works.
API: next_visit_date: "2026-06-20" → 422. next_visit_value: 7, next_visit_units: "weeks" → 200.
Fix: Absolute mode should default to a working-hour time or skip time validation for follow-ups.
Bug #52: Delete/Cancel Dialog — Reason Not Cleared on Close
| Field | Value |
|---|---|
| Severity | 🟢 Low |
| Bug # | 52 |
| Area | Billing — UI |
| Session | 11-billing |
| Test ID | BILL-57 |
| Status | ✅ Closed |
Description: Click outside delete dialog → closes → reopen → previous reason text still filled.
Fix: Reset deleteReason/cancelReason state in dialog's onOpenChange handler.
Bug #53: Phone Not Shown in Follow-ups List
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 53 |
| Area | Follow-ups — Display |
| Session | 11-follow-ups |
| Test ID | FU-15 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Appointment API returns patient: { display, mrn } — no phone field. Frontend tries to show phone via formatPatientPhone() but has no data.
Fix: Include patient.phone_number in appointment blueprint or make a separate patient lookup.
Bug #54: ✓ Icon = Edit, Not Confirm — Confusing UX
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 54 |
| Area | Follow-ups — UX |
| Session | 11-follow-ups |
| Test ID | FU-20 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Green ✓ (check) icon on follow-up rows opens UpdateAppointmentModal — it edits, not confirms. The icon suggests "done/completed" but actually edits. Users expect check = complete, not check = edit.
Fix: Change icon to ✏️ Pencil for edit, or add a separate Confirm flow that marks as completed.
Bug #55: Telemedicine Follow-ups Disappear
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 55 |
| Area | Follow-ups — Filter |
| Session | 11-follow-ups |
| Test ID | FU-25 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: follow_up=true filters by by_type('fixed') in controller. Changing follow-up to telemedicine type → removed from list. The filter is hardcoded to fixed only.
Fix: Include telemedicine in follow_up filter: scope.by_type(['fixed', 'telemedicine']) if params[:follow_up] == 'true'
Bug #56: Patient Reports Row Click — Wrong URL (patientId Used Twice)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 56 |
| Area | Patient Reports — Navigation |
| Session | 12-reports |
| Test ID | REP-24 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Description: Clicking a patient row in Patient Reports navigates to /cms/patient/v2/{patientId}/{patientId} — uses patientId for both URL segments instead of patientId + patientFacilityId. The detail page can't load.
Code: PatientReportsPageV2.tsx line 106:
navigate(`/cms/patient/v2/${patientId}/${patientId}`)
// Should be:
navigate(`/cms/patient/v2/${patientId}/${patientFacilityId}`)Bug #57: Revenue Summary — Payments Not Reflected (All Zeros)
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 57 |
| Area | Revenue — Summary API |
| Session | 12-reports |
| Test ID | REV-01 |
| Status | ✅ Closed |
Description: Summary returns zero for all payment fields despite ₹2600 in actual payments (₹2500 cash + ₹100 card). Services list is correct, but earnings + billing_summary show zeros.
API Evidence: bills[].amount all ₹0, billing_summary.{paid,unpaid,billed}_amount all ₹0, earnings.{total_revenue,amount_received,amount_pending} all ₹0. Only earnings.billed_amount = ₹7390 is correct.
Root cause: SummaryService#calculate_billing_summary or bills_by_payment_mode not aggregating from Payment/PaymentAllocation tables.
Bug #58: Revenue Summary — Case-Sensitive Service Grouping
| Field | Value |
|---|---|
| Severity | 🟢 Low |
| Bug # | 58 |
| Area | Revenue — Summary API |
| Session | 12-reports |
| Test ID | REV-04 |
| Status | ✅ Closed |
Description: "Sugar Test" and "sugar test" counted separately. Should be case-insensitive grouped.
API Evidence: services[] shows Sugar Test (count: 3, ₹6000) + sugar test (count: 2, ₹390).
Bug #59: Revenue Summary — Visit Type Breakdown Incorrect
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 59 |
| Area | Revenue — Summary API |
| Session | 12-reports |
| Test ID | REV-02 |
| Status | ✅ Closed |
Description: All ₹7390 shown under "Walkin" — services from fixed/telemedicine appointments also counted as Walkin.
API Evidence: visit_types[] = Walkin: ₹7390, Virtual: ₹0, Fixed: ₹0 but services exist across walk_in, fixed, and telemedicine appointments.
Bug #60: Revenue Export — Bill Report API Fails
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 60 |
| Area | Revenue — Export |
| Session | 12-reports |
| Test ID | REV-63 |
| Status | ✅ Closed |
Description: POST /billing/report_exports/bill_report fails. Revenue export generates no report.
API Evidence: Request with facility_id, from_date, to_date → error response.
Bug #61: Services Tab — Focus Lost on Debounced Search
| Field | Value |
|---|---|
| Severity | 🟢 Low |
| Bug # | 61 |
| Area | Revenue — Services Tab UX |
| Session | 12-reports |
| Test ID | SV-01 |
| Status | ✅ Closed |
Description: After typing in service name search and debounce triggers API call, the input loses focus when data reloads. User gets interrupted mid-typing.
Bug #62: Services Tab — Patient Name Null Causes NA Display + Search 500
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 62 |
| Area | Revenue — Services Tab |
| Session | 12-reports |
| Test ID | SV-02, SV-15 |
| Status | ✅ Closed |
Description: useAllServices API returns patient.name: null for all services. This causes:
- Patient column shows "NA" in the table
- Patient name search (
?patient_name=) returns 500 Internal Server Error - Delete modal shows
{{patientName}}template instead of actual name
Root cause: Service creation via billing panel doesn't populate patient.name in the service record.
Bug Status Legend
| Status | Description |
|---|---|
| ✅ Closed | Bug fixed and verified through QA testing |
| 🔧 DevOps | Infrastructure/configuration issue |
| ⏸️ Deferred | Planned for future release |
| ⏳ Open | Bug confirmed, not yet fixed |
Sprint 1 Resolutions (2026-06-23)
Bug #10: Cross-App Authentication Not Enforced — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 10 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added ALLOWED_APPS role-to-app mapping in SessionsController. Login now rejects users whose signed_up_as doesn't match the requesting app. PasswordsController also gated.
Files changed: sessions_controller.rb, passwords_controller.rb
Bug #17: Cross-App Forgot Password — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 17 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Same app gate applied to PasswordsController#reset. Returns generic success message to avoid leaking user existence.
Files changed: passwords_controller.rb
Bug #24: Invite Sent to Registered Email — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 24 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: InviteService now runs check_existing_membership before creating invites. Blocks if user already has an active membership in the tenant.
Files changed: invite_service.rb
Bug #26: Invite to Active User — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🔴 CRITICAL |
| Bug # | 26 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Same as #24 — check_existing_membership blocks invites to users who already have active memberships.
Files changed: invite_service.rb, staff_services/invite_service.rb
Bug #21: Practitioner Invite to Existing Patient — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 21 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: check_existing_membership also checks signed_up_as. Patient accounts blocked from receiving practitioner invites with message "Cannot invite a patient account as a practitioner".
Files changed: invite_service.rb
Bug #45: Invite Role Mismatch — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 45 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: check_existing_membership blocks invites when user's signed_up_as is incompatible with the invite (e.g., staff user receiving practitioner invite).
Files changed: invite_service.rb, staff_services/invite_service.rb
Bug #46: Invite Role Overwritten at Signup — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 46 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: VerificationWithProfileService now checks for invite_token during OTP verification. If present, overrides the user's signed_up_as to match the invite's intended role before profile creation.
Files changed: verification_with_profile_service.rb, registrations_controller.rb
Bug #47: Registered User Gets Signup Link Instead of Accept Link — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 47 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: send_sms_notification now branches: if the target user already has an account → sends /signin?next=/identity/my/invites link. If no account → sends the signup link with invite token.
Files changed: invite_service.rb, staff_services/invite_service.rb
Bug #19: Backend Validation Error Not Displayed — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 19 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: AddDoctorModal and AddStaffModal catch blocks now surface error.response?.data?.error?.message instead of generic "Failed to send invite."
Files changed: AddDoctorModal.tsx, AddStaffModal.tsx
Bug #20: Whitespace-Only Names Allowed — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 20 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Zod schemas in both modals now use .refine(val => val.trim().length > 0) to reject whitespace-only names.
Files changed: AddDoctorModal.tsx, AddStaffModal.tsx
Bug #25: Invite Modal Form State Not Reset — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 25 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added handleOpenChange handler that resets the form when the dialog closes (via outside click, Escape, or Cancel button).
Files changed: AddDoctorModal.tsx, AddStaffModal.tsx
Bug #23: Duplicate Invite with Same Email — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 23 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Covered by the existing check_existing_invite + new check_existing_membership. Duplicate invites to the same phone/email in the same clinic are blocked before creation.
Files changed: invite_service.rb
🔴 Critical Bugs (Block Production) — 4 Remaining, 1 Resolved
| Bug # | Issue | Status |
|---|---|---|
| #10 | Cross-app auth not enforced | ✅ Closed (2026-06-23) ✅ VERIFIED |
| #17 | Cross-app forgot password | ✅ Closed (2026-06-23) ✅ VERIFIED |
| #24 | Invite sent to registered email | ✅ Closed (2026-06-23) ✅ VERIFIED |
| #26 | Invite to active user | ✅ Closed (2026-06-23) ✅ VERIFIED |
| #29 | Doctor can set other doctor's schedule | ✅ Closed |
🧪 Sprint 1 Testing Checklist
Use this to verify each fix on staging before marking bugs as Closed.
Auth — Cross-App Enforcement (#10, #17)
| # | Test Case | Expected Result |
|---|---|---|
| 1 | Login to Patient App with a patient account | ✅ Success |
| 2 | Login to Patient App with a doctor/staff account | ❌ Error: "practitioner account cannot access the patient app" |
| 3 | Login to CMS/Clinic App with a doctor/staff account | ✅ Success |
| 4 | Login to CMS/Clinic App with a patient account | ❌ Error: "patient account cannot access the cms app" |
| 5 | Forgot Password on Patient App with a doctor phone | Returns generic "If an account exists..." message, no reset code sent |
| 6 | Forgot Password on Clinic App with a patient phone | Returns generic "If an account exists..." message, no reset code sent |
Invite — Backend Validation (#24, #26, #21, #23, #45)
| # | Test Case | Expected Result |
|---|---|---|
| 7 | Invite a phone that's already an active member of this clinic | ❌ Toast: "User is already an active member of this clinic" |
| 8 | Invite a phone registered as patient as a practitioner | ❌ Toast: "Cannot invite a patient account as a practitioner" |
| 9 | Invite a phone registered as staff as a practitioner | ❌ Toast: "This user is registered as staff" |
| 10 | Invite a phone registered as vendor as a practitioner | ❌ Toast: "Cannot invite a vendor account as a practitioner" |
| 11 | Invite same phone+email that has a pending invite already | ❌ Toast: "Invite already exists" |
| 12 | Invite a brand new phone number (not registered, not a member) | ✅ Invite created, SMS sent |
Invite — Role Carry-Through (#46)
| # | Test Case | Expected Result |
|---|---|---|
| 13 | Admin sends practitioner invite to unregistered phone | User signs up via invite link → role = practitioner (not staff) |
| 14 | Admin sends staff invite to unregistered phone | User signs up via invite link → role = staff |
| 15 | Admin sends invite with staff_role=admin to unregistered phone | User signs up → role = staff, membership role = admin |
Invite — SMS Links (#47)
| # | Test Case | Expected Result |
|---|---|---|
| 16 | Invite a phone that already has a Kulcare account | SMS contains sign-in link (not sign-up link). User sees invites in their inbox. |
| 17 | Invite a phone that does NOT have a Kulcare account | SMS contains sign-up link with invite token. User signs up and auto-accepts. |
Frontend — Error Messages (#19)
| # | Test Case | Expected Result |
|---|---|---|
| 18 | Trigger any invite error (e.g., invite existing member) | Toast shows the actual backend error message (e.g., "User is already an active member"), NOT "Failed to send invite. Please try again." |
| 19 | Trigger a network error (disconnect internet, try to send invite) | Toast shows a fallback error message |
Frontend — Whitespace Names (#20)
| # | Test Case | Expected Result |
|---|---|---|
| 20 | Type spaces only (" ") in First Name field, try to submit | ❌ Zod error: "First name cannot be only spaces" |
| 21 | Type spaces only (" ") in Last Name field, try to submit | ❌ Zod error: "Last name cannot be only spaces" |
| 22 | Type a real name like "John" → submit | ✅ Accepted |
Frontend — Form Reset (#25)
| # | Test Case | Expected Result |
|---|---|---|
| 23 | Open Doctor Invite modal, fill all fields, click outside to close | Reopen → all fields are blank |
| 24 | Open Doctor Invite modal, fill all fields, press Escape to close | Reopen → all fields are blank |
| 25 | Open Staff Invite modal, fill all fields, click Cancel | Modal closes. Reopen → all fields blank. |
| 26 | Open Doctor Invite modal, fill all fields, successfully send invite | Modal closes. Reopen → all fields blank. |
Already Fixed — Verify (#29)
| # | Test Case | Expected Result |
|---|---|---|
| 27 | Login as Doctor A. Try to edit Doctor B's schedule. | ❌ Should NOT be able to see/edit Doctor B's schedule (only own schedule) |
| 28 | Login as Clinic Admin/Owner. Try to edit any doctor's schedule. | ✅ Should be able to manage any doctor's schedule |
Sprint 2 Resolutions (2026-06-23)
Bug #36: Phone Search Returns No Results — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 36 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added phone_number to search_columns in PatientsController#index. Was searching only first_name, last_name, display_name, mrn — now also matches phone.
Files changed: patients_controller.rb (line 27)
Bug #56: Patient Reports Row Click — Wrong URL — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 56 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: handlePatientClick now accepts patientFacilityId as second argument. Navigates to /cms/patient/v2/{patientId}/{patientFacilityId} instead of /{patientId}/{patientId}. Updated types in PatientReportsTableV2 and PatientTableSection.
Files changed: PatientReportsPageV2.tsx, PatientReportsTableV2.tsx, PatientTableSection.tsx
Bug #55: Telemedicine Follow-Ups Disappear — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 55 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Changed follow_up filter from by_type('fixed') to by_type(%w[fixed telemedicine]) so telemedicine follow-ups appear in the list.
Files changed: appointments_controller.rb (line 169)
Bug #32: DOB Not Auto-Filled When Importing from Kulcare — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 32 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Auto-fill approximate birth date from the age field returned by federated search API. The API intentionally excludes exact DOB for privacy — age-based year is a reasonable starting point. Applied in both AddPatientModal and appointment booking PatientInformation step.
Files changed: AddPatientModal.tsx, PatientInformation.tsx
Bug #38: Cross-Clinic Patient Flow Incomplete — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 38 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Fix: Two-part fix:
FindOrCreateService#auto_register_patient_facility— auto-creates facility link when a patient is searched for or viewed from a different clinic within the same tenant (commit47118d2a9).PatientsController#auto_register_facility_links— batch-registers missing facility links on the patient LIST page before returning results. Uses a NOT IN subquery to find unlinked patients and creates onePatientFacilityrecord each (idempotent — skips on subsequent requests). This ensures the patient list never shows rows with nullpatient_facility_id, and all patients are clickable without crashing the detail view.
Files changed: find_or_create_service.rb, patients_controller.rb
Bug #44: Appointment Duration/Overlap Not Validated — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 44 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Fix (3-part):
utc_datetimehelper — builds UTC Time from UTC HH:MM instead ofTime.zone.parsewhich re-interpreted UTC strings as local time, shifting the overlap window by the server timezone offset.conflicting_appointments— added appointment overlap check alongside the existing encounter check. Previously only encounters were checked, but freshly booked CMS appointments have no encounter yet.- Proper overlap formula —
existing.start < new.end AND existing.end > new.startinstead ofBETWEENwhich falsely flagged back-to-back slots.
Files changed: availability_service.rb
🧪 Sprint 2 Testing Checklist
Phone Search (#36)
| # | Test Case | Expected Result |
|---|---|---|
| 29 | Go to Patient List, type a phone number that exists (e.g., digits from a known patient) | Results show matching patients |
| 30 | Search by partial phone number (e.g., last 4 digits) | Results show matching patients |
| 31 | Search by name (regression check) | Still works as before |
Patient Reports URL (#56)
| # | Test Case | Expected Result |
|---|---|---|
| 32 | Go to Reports → Patient Reports, click a patient row that has a facility record | Navigates to patient detail page (not broken URL) |
| 33 | Verify URL format is /cms/patient/v2/{patientId}/{facilityId} | URL contains two different IDs |
Telemedicine Follow-Ups (#55)
| # | Test Case | Expected Result |
|---|---|---|
| 34 | Create a telemedicine follow-up appointment | It appears in the Follow-ups list |
| 35 | Create a fixed (in-person) follow-up appointment | It also appears in the Follow-ups list |
| 36 | Create a walk-in appointment (not a follow-up) | Does NOT appear in Follow-ups list |
DOB Auto-Fill (#32)
| # | Test Case | Expected Result |
|---|---|---|
| 37 | Add Patient → search for a Kulcare patient who has age set → click Import | DOB field shows approximate year (e.g., "2001-01-01" for age 25) |
| 38 | Book Appointment → search for Kulcare patient with age → click Import | DOB field auto-filled in the booking form |
| 39 | Import a Kulcare patient without age set | DOB field remains empty (no crash) |
Cross-Clinic Patient Auto-Registration (#38)
| # | Test Case | Expected Result |
|---|---|---|
| 40 | Add Patient "A" in Clinic 1. Switch to Clinic 2 (same tenant). Search for Patient A. Click View. | Patient detail page loads successfully (not broken URL) |
| 41 | In Clinic 2, book an appointment for Patient A | Appointment is created with proper facility context |
| 42 | After booking in Clinic 2, view Patient A's detail again | Patient now shows Clinic 2 in their facility list |
| 43 | Patient A's original data in Clinic 1 is unchanged | Clinic 1 data intact, no data corruption |
Appointment Duration / Overlap (#44)
| # | Test Case | Expected Result |
|---|---|---|
| 44 | Doctor has slots: 6:30 free, 6:45 free, 7:00 BOOKED, 7:15 BOOKED. Try to book 6:45 with 120 min duration. | ❌ Rejected: "Appointment slot is already booked" (overlaps 7:00) |
| 45 | Doctor has slots: 6:30 free, 6:45 free, 7:00 free. Book 6:45 with 30 min duration. | ✅ Accepted (6:45-7:15, all free) |
| 46 | Same setup as #45, now try to book 7:00 with 15 min duration | ✅ Accepted (7:00-7:15, all free) |
| 47 | Book a walk-in appointment (no time slot) | ✅ Accepted (walk-ins don't check overlap) |
Sprint 3 Resolutions (2026-06-23)
Bug #5: DOB Allows Future Dates — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 5 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added birth_date_not_in_future validation to Clinical::Patient model.
Files changed: patient.rb
Bug #34: No Backend Validation for Patient Form — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 34 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added phone digit-only format validation to Clinical::Patient. Future DOB blocked by #5 fix.
Files changed: patient.rb
Bug #14+#15: Duplicate Category Names (Case-Insensitive) — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 14, 15 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added case_sensitive: false to FacilityCategory uniqueness validation.
Files changed: facility_category.rb
Bug #48: Out-of-Queue Split Inconsistent — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 48 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: GenerateToday now includes finished/cancelled/noshow in out_of_queue. Removed .not_cancelled filter.
Files changed: generate_today.rb
Bug #53: Phone Not Shown in Follow-Ups List — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 53 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added phone field to appointment blueprint queue view.
Files changed: appointment_blueprint.rb
Bug #18: Practitioner API Missing Phone — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 18 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added phone_number + phone_country_code to PractitionerBlueprint (from identifiers JSONB).
Files changed: practitioner_blueprint.rb
Bug #22: Cancelled Invites Not Visible — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 22 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Removed hardcoded status: 'pending' filter from DoctorsTab + StaffTab queries.
Files changed: DoctorsTab.tsx, StaffTab.tsx
Bug #49: No Couple Session Indicator in Queue — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 49 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Added "Couple" badge with Users icon in QueueRow when couple_session is true.
Files changed: QueueRow.tsx
Bug #54: ✓ Icon = Edit, Not Confirm — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 54 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Changed Check→Pencil icon in FollowUpsTableRow + neutral styling (was green/success).
Files changed: FollowUpsTableRow.tsx
🧪 Sprint 3 Testing Checklist
Validation (#5, #34)
| # | Test Case | Expected Result |
|---|---|---|
| 48 | Add Patient with future DOB (2027-01-01) | ❌ "birth_date cannot be in the future" |
| 49 | Add Patient with letters in phone ("abc123") | ❌ "phone_number must contain only digits" |
| 50 | Add Patient with valid phone + today's DOB | ✅ Accepted |
Duplicate Categories (#14, #15)
| # | Test Case | Expected Result |
|---|---|---|
| 51 | Create "Diabetes", create "Diabetes" again | ❌ Name already taken |
| 52 | Create "Diabetes", create "diabetes" | ❌ Name already taken (case-insensitive) |
Out-of-Queue (#48)
| # | Test Case | Expected Result |
|---|---|---|
| 53 | Finish encounter → check queue | Patient moves to Out of Queue |
| 54 | Cancel appointment → check queue | Shows in OoQ with "Cancelled" (not disappeared) |
| 55 | Mark "Completed" → check queue | Moves to OoQ (unchanged) |
Phone in Follow-Ups + Practitioner API (#53, #18)
| # | Test Case | Expected Result |
|---|---|---|
| 56 | Follow-ups page → check phone column | Shows patient phone number |
| 57 | Invite flow → search practitioners | API returns phone_number + phone_country_code |
Cancelled Invites (#22)
| # | Test Case | Expected Result |
|---|---|---|
| 58 | Send + cancel doctor invite → Doctors tab | Cancelled invite visible |
| 59 | Send + cancel staff invite → Staff tab | Cancelled invite visible |
Couple Session Indicator (#49)
| # | Test Case | Expected Result |
|---|---|---|
| 60 | Book couple session → queue | "Couple" badge with Users icon |
| 61 | Book regular appointment → queue | No "Couple" badge |
Edit Icon (#54)
| # | Test Case | Expected Result |
|---|---|---|
| 62 | Follow-ups → Pending tab → action icons | Pencil (✏️) instead of check (✓) |
| 63 | Click pencil icon | Edit modal opens |
Sprint 3.5 — Extra Fixes (2026-06-23)
Bug #31: Federated Search — Full Name Now Works — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 31 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: FederatedSearchService now splits name into words. "Mohit Goyal" matches first_name + last_name separately. Applied to both in_clinic and in_kulcare paths.
Files changed: federated_search_service.rb
Bug #52: Delete Dialog — Reason Cleared on Overlay Click — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟢 Low |
| Bug # | 52 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: Overlay backdrop click now resets deleteReason + closes modal. Previously only Cancel button cleared state.
Files changed: ServicesTabV2.tsx
Bug #1: Error Messages on Signup (Patient) — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 1 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: SignUpPage catch block now extracts err?.response?.data?.error?.message from API response instead of showing generic Axios error text. The auth service's parseBackendError already maps technical messages to user-friendly text.
Files changed: SignUpPage.tsx
Bug #8: Error Messages on Signup (Clinic) — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟠 Medium |
| Bug # | 8 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Fix: SignInPage already uses parseBackendError which extracts and maps API error messages to user-friendly text. SignUpPage now has the same fallback. Both pages show actual backend errors instead of generic messages.
Files changed: SignUpPage.tsx (SignInPage.tsx already correct)
Bug #51: Next Visit — Absolute Date Now Works — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 51 |
| Status | ✅ Closed (2026-06-24) ✅ VERIFIED |
Root cause (v1): Time.parse interpreted date-only strings as midnight UTC, which is never within any practitioner's working hours — availability check always rejected.
Root cause (v2): parse_date parsed the date in server timezone first via Time.parse, then converted to facility timezone via in_time_zone. When server TZ ≠ facility TZ (e.g. server IST, facility UTC), the date shifted backwards by the offset — landing on the previous day, which is always in the past.
Fix (final): Time.use_zone(facility_timezone) { Time.zone.parse("2026-06-25 09:00:00") } — parses 9 AM directly in the facility's timezone with no conversion hop. Frontend date pickers now enforce min={tomorrow} to prevent selecting today's date (which would be in the past by the time the user submits).
Files changed: next_visit/create_service.rb, NextVisitModal.tsx, NextVisitSection.tsx
Bug #2: Resend OTP Not Working (Patient) — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 2 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Root cause: RegistrationsController#find_user_for_resend called User.find_by_phone(country_code, phone_number) — a method that doesn't exist. Rails' dynamic find_by_* only works for single columns, not two args. Result: NoMethodError → 500.
Fix: Changed to User.by_phone(...).first — uses the existing scope. Same fix applied to email lookup (find_by_email → by_email(...).first).
Files changed: registrations_controller.rb, passwords_controller.rb
Bug #9: Resend OTP 500 Error (Clinic) — ✅ RESOLVED
| Field | Value |
|---|---|
| Severity | 🟡 High |
| Bug # | 9 |
| Status | ✅ Closed (2026-06-23) ✅ VERIFIED |
Root cause: Same as #2 — User.find_by_phone doesn't exist. Both clinic and patient apps hit the same /identity/registrations/resend_otp endpoint.
Fix: Same as #2.
Files changed: registrations_controller.rb, passwords_controller.rb
Testing: Schedule a next visit with absolute date → should succeed (no more "outside working hours"). Relative mode should still work unchanged.
🧪 Sprint 3.5 Testing Checklist
| # | Test Case | Expected Result |
|---|---|---|
| 64 | Search "mohit goyal" in Add Patient | Results found ✅ |
| 65 | Open delete modal, type reason, click outside → reopen | Reason field is empty ✅ |
Updating Bug Status
- Edit this file
- Update status field
- Add resolution notes with date
Adding New Bug
- Add to appropriate severity section
- Include all required fields
- Link to session file
- Update bug summary table
Document Status: Active Last Updated: 2026-06-25
✅ All Bugs — Verified & Closed Status
| Status | Description |
|---|---|
| ✅ Closed | Bug fixed and verified working in QA testing |
| ✅ Resolved | Bug fixed, pending verification |
| ⏳ Open | Bug confirmed, not yet fixed |
| 🔧 DevOps | Infrastructure/configuration issue |
| ⏸️ Deferred | Planned for future release |
🔴 Critical Bugs — ALL CLOSED ✅
| Bug # | Issue | Status | Verified |
|---|---|---|---|
| #10 | Cross-app auth not enforced | ✅ Closed | 2026-06-24 |
| #17 | Cross-app forgot password | ✅ Closed | 2026-06-24 |
| #24 | Invite sent to registered email | ✅ Closed | 2026-06-24 |
| #26 | Invite to active user | ✅ Closed | 2026-06-24 |
| #29 | Doctor can set other doctor's schedule | ✅ Closed | 2026-06-24 |
🟡 High Priority Bugs — CLOSED ✅
| Bug # | Issue | Status | Verified |
|---|---|---|---|
| #1 | Error message not user-friendly (Patient signup) | ✅ Closed | 2026-06-24 |
| #2 | Resend OTP not working (Patient) | ✅ Closed | 2026-06-24 |
| #8 | Error message not user-friendly (Clinic signup) | ✅ Closed | 2026-06-24 |
| #9 | Resend OTP 500 error (Clinic) | ✅ Closed | 2026-06-24 |
| #10 | Cross-app auth not enforced | ✅ Closed | 2026-06-24 |
| #17 | Cross-app forgot password | ✅ Closed | 2026-06-24 |
| #19 | Backend validation error not displayed | ✅ Closed | 2026-06-24 |
| #21 | Practitioner invite to existing patient | ✅ Closed | 2026-06-24 |
| #31 | Federated Search - full name works | ✅ Closed | 2026-06-24 |
| #32 | DOB not auto-filled from Kulcare | ✅ Closed | 2026-06-24 |
| #36 | Phone search returns no results | ✅ Closed | 2026-06-24 |
| #38 | Cross-clinic patient null facility IDs | ✅ Closed | 2026-06-24 |
| #44 | Appointment duration/overlap not validated | ✅ Closed | 2026-06-24 |
| #45 | Invite role mismatch | ✅ Closed | 2026-06-24 |
| #46 | Invite role overwritten at signup | ✅ Closed | 2026-06-24 |
| #47 | Registered user gets signup link | ✅ Closed | 2026-06-24 |
| #51 | Next visit absolute date fails | ✅ Closed | 2026-06-24 |
| #55 | Telemedicine follow-ups disappear | ✅ Closed | 2026-06-24 |
| #56 | Patient reports wrong URL | ✅ Closed | 2026-06-24 |
| #62 | Patient name null in services | ✅ Closed | 2026-06-24 |
🟠 Medium Priority Bugs — CLOSED ✅
| Bug # | Issue | Status | Verified |
|---|---|---|---|
| #5 | DOB allows future dates | ✅ Closed | 2026-06-24 |
| #14 | Duplicate name (exact) not rejected | ✅ Closed | 2026-06-25 |
| #15 | Duplicate name (case-insensitive) | ✅ Closed | 2026-06-25 |
| #18 | Practitioner API missing phone | ✅ Closed | 2026-06-24 |
| #20 | Whitespace-only names allowed | ✅ Closed | 2026-06-24 |
| #22 | Cancelled invites not visible | ✅ Closed | 2026-06-24 |
| #23 | Duplicate invite with same email | ✅ Closed | 2026-06-24 |
| #25 | Invite modal form state not reset | ✅ Closed | 2026-06-24 |
| #31 | Federated search full name | ✅ Closed | 2026-06-24 |
| #34 | No validation for patient form | ✅ Closed | 2026-06-25 |
| #48 | Out-of-queue split inconsistent | ✅ Closed | 2026-06-24 |
| #49 | No couple session indicator | ✅ Closed | 2026-06-24 |
| #52 | Delete dialog reason not cleared | ✅ Closed | 2026-06-24 |
| #53 | Phone not shown in follow-ups | ✅ Closed | 2026-06-24 |
| #54 | ✓ icon = edit, not confirm | ✅ Closed | 2026-06-24 |
Patient App Bug Fixes (2026-06-25) — ALL VERIFIED ✅
| Bug # | Issue | Status | Verified |
|---|---|---|---|
| #3 | OTP paste in reset password | ✅ Verified Fixed | 2026-06-25 |
| #4 | Photo upload preview | ✅ Verified Fixed | 2026-06-25 |
| #5 | DOB allows future dates | ✅ Verified Fixed | 2026-06-25 |
| #6 | Phone/pincode validation | ✅ Verified Fixed | 2026-06-25 |
| #6b | Clear form fields not saved | ✅ Verified Fixed | 2026-06-25 |
| #7 | Special characters in names | ✅ Verified Fixed | 2026-06-25 |
| #35 | Email field missing | ✅ Verified Fixed | 2026-06-25 |
Ready for Testing (Historical - See Above for Verified Status)
| Sprint | Bugs | Count |
|---|---|---|
| Sprint 1 | #10, #17, #19, #20, #21, #23, #24, #25, #26, #29, #45, #46, #47 | 13 |
| Sprint 2 | #32, #36, #38, #44, #55, #56 | 6 |
| Sprint 3 | #5, #14, #15, #18, #22, #34, #48, #49, #53, #54 | 10 |
| Sprint 3.5 | #31, #52 | 2 |
| Sprint 4 | #1, #2, #8, #9, #51, #62 | 6 |
| Patient App | #3, #4, #5, #6, #6b, #7, #35 | 7 |
| Confirmed OK | #27, #28 | 2 |
67 test cases across 4 checklists.
🔴 Pending — Needs DevOps (1 bug)
| # | Severity | Description |
|---|---|---|
| 30 | 🟡 High | New doctors not in search — Sidekiq down on staging |
⏭️ Deferred — New Billing Flow (5 bugs)
| # | Severity | Description |
|---|---|---|
| 11, 12, 13 | 🟠 Medium | Edit service/procedure/vital PATCH |
| 50 | 🟡 High | Gateway payment 500 |
| 37 | 🟡 High | Export report EFS permission |
| 57, 60 | 🟡 High | Revenue summary / bill export |
🟢 Pending — Architecture (2 bugs)
| # | Severity | Description |
|---|---|---|
| 58 | 🟢 Low | Case-sensitive service grouping |
| 59 | 🟠 Medium | Visit type breakdown incorrect |
✅ Not Bugs (2)
| # | Description |
|---|---|
| 33 | Patient visible across clinics — expected |
| 15 | Fixed by #14 |