Commit Graph
16 Commits
Author SHA1 Message Date
hamedandClaude Opus 4.8 aa87b4a9cb fix(db): prevent double-booking a slot via unique active_slot_key (H2)
A non-unique index on (doctor_id, slot_start) plus a count-then-insert check
left a TOCTOU race: two concurrent requests could both pass isSlotTaken and
both insert. wrapInTransaction alone doesn't stop the phantom under InnoDB
REPEATABLE-READ.

Add a nullable, unique active_slot_key on Appointment = "doctorId:slotStart"
while the booking occupies the slot (pending/confirmed — in lockstep with
isSlotTaken); NULL once expired/completed/no_show/cancelled (NULLs don't collide
in a MySQL unique index, so released slots rebook freely). bookAtomically now:
catches the unique violation -> SlotTakenException, and expires lapsed pendings
in-transaction so the ~1-min window before the expiry cron doesn't wrongly block
rebooking. All three booking paths (online / my / admin) routed through it.

Migration backfills one row per (doctor, slot) — the latest id — so the index
builds even on dirty historical data without destructively cancelling bookings.
(Backfill surfaced a real pre-existing double-booked slot in dev data.)

Regression: tests/Appointment/SlotUniquenessTest. Adjusted the expiry-service
test fixture to use distinct slots (one live booking per slot is now enforced).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:04:54 +03:30
hamedandClaude Opus 4.8 c084571bf0 fix(security): enforce doctor scope on POST my/appointment (H1)
createAppointment only checked the caller held an allowed role, then booked
onto whatever doctor_uuid the request named — a doctor could book onto any
other doctor's calendar, a clinic onto doctors outside it, a secretary outside
their scope. Add canBookForDoctor(): doctor→own only, clinic→member doctors,
secretary→active scope + appointments.create permission, admin→any.

Regression: tests/Appointment/BookingScopeTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:52:23 +03:30
hamed 9603b702c1 feat: implement domain guard for commission calculation and enhance representation dashboard
- Added domain guard in CommissionService to ensure commission is calculated only when the appointment is booked under the same representation as the doctor.
- Updated RepresentationController to filter statistics by representation, ensuring accurate data is shown for each representative.
- Introduced new endpoints for the representation dashboard to provide summary statistics, doctor performance, and financial reports.
- Created new pages for RepresentationFinance and RepresentationSettlement to display financial data and allow for settlement requests.
- Added migration to include booking_representation_id in appointments for tracking the representative under which the appointment was booked.
2026-06-24 16:14:41 +03:30
hamed e0abaf5c0c feat(appointment): enforce mandatory patient national code and gender with validation 2026-06-24 12:32:58 +03:30
hamedandClaude Opus 4.8 af881231d0 fix: store appointment address from schedule and auto-add patient to clinic
Appointments now persist address_id resolved from the weekly-schedule
session (location_id) across all booking paths (online, secretary, admin).
On confirm, the patient is added to the clinic owning that address, or to
the doctor's single clinic as fallback. Weekly-schedule create/update now
requires location_id on every active session. PatientSession exposes
doctor_uuid/doctor_name so clinic records show which doctor each visit is for.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:10:36 +03:30
hamed a8d36d7455 feat(user-profile): add avatar upload functionality and update UserProfile entity 2026-06-19 10:19:47 +03:30
hamedandClaude Opus 4.8 28011160a2 feat(appointment): include doctor specialties and clinic address in toArray
The appointment detail view needs the doctor's specialty and the clinic
address/phone/map, which toArray didn't return. Add doctor.specialties[] and
a top-level `address` (the doctor's first address via DoctorAddress::toArray —
address, telephone, map). Additive only; existing keys unchanged. Docs updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 12:01:32 +03:30
hamed daf2bd4b91 Merge branch 'feat/appointment-lock-patient'
# Conflicts:
#	src/Appointment/Controller/AppointmentController.php
2026-06-15 18:29:25 +03:30
hamed 6e7101f113 Merge branch 'fix/past-slots-unavailable'
# Conflicts:
#	docs/api/appointment.md
2026-06-15 18:28:06 +03:30
hamedandClaude Opus 4.8 ff90ffb9ca docs(api): document booking lock, patient fields, and payment confirm
appointment.md: for_self + patient_* request fields, expires_at and
patient_* response fields, the 15-minute lock/expiry behavior, and the
atomic-conflict 409. payment.md: successful appointment payment confirms
the booking and SMSes the patient.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:01:36 +03:30
hamedandClaude Opus 4.8 4216e67d68 fix(appointment): mark past-time slots as unavailable
is_available only checked whether a slot was booked, so on today's date
slots whose start time had already passed (e.g. 10:00 when it is 12:00)
still showed as bookable until the POST /appointment 422. Treat a slot
with start < now as unavailable in getAllSlotsWithAvailability, and drop
past slots in filterBookedSlots so getAvailableSlots agrees. Future days
are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:48:50 +03:30
hamedandClaude Opus 4.8 0f7a8c1162 docs(api): document booking window meta and month-availability endpoint
- appointment-settings.md: weekly-schedule meta (online_booking_enabled,
  booking_window value/unit), defaults, and the slot-gating behavior.
- appointment.md: new public month-availability endpoint (Gregorian
  year/month, disabled/enabled dates) and the empty-slots conditions for
  appointment-slots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:27:10 +03:30
hamed 5a7df22eda feat: enhance appointment scheduling with session management and availability checks 2026-06-11 19:56:27 +03:30
hamed 0b31eb7812 fix: resolve calendar crash and enhance appointment management
- Fixed calendar crash due to invalid array length in PersianCalendar.tsx by changing locale to 'en-u-ca-persian'.
- Added Persian weekday display in DateNavigator with appropriate styling and logic.
- Updated empty slot message to indicate when a day is off.
- Made patient name a required field in appointment creation and implemented find-or-create logic for patients in both admin and user endpoints.
- Corrected mobile number display to show the patient's number instead of the doctor's in appointment listings.
- Ensured booked appointments are displayed correctly in the schedule view.
- Removed unnecessary operations column from the appointments table view.
2026-06-11 19:35:12 +03:30
hamed e7b90a6399 feat(api): add dashboard endpoints for clinic, doctor, and secretary roles
- Implemented GET /api/v1/dashboard/clinic to return clinic stats and today's schedule for clinic owners.
- Implemented GET /api/v1/dashboard/doctor to return doctor's stats and today's schedule for doctors.
- Implemented GET /api/v1/dashboard/secretary to return stats and conditional appointments for secretaries.

feat(migrations): create user_active_context and mobile_verification_otp tables

- Added migration to create user_active_context table for tracking active user sessions.
- Added migration to create mobile_verification_otp table for handling mobile number verification.

feat(migrations): create site_config table for application settings

- Added migration to create site_config table to store various site configuration settings.

feat(appointments): create MyAppointmentsController for user-specific appointments

- Added MyAppointmentsController to handle fetching user-specific appointments with pagination and filtering.

feat(auth): implement NotificationMobileController for mobile number verification

- Added NotificationMobileController to handle OTP requests and verification for mobile number changes.

feat(auth): create MobileVerificationOtp entity for OTP management

- Created MobileVerificationOtp entity to manage OTP records for mobile verification.

feat(auth): create UserActiveContext entity for user session management

- Created UserActiveContext entity to manage user active sessions.

feat(config): implement SiteConfigController for managing site settings

- Added SiteConfigController to handle fetching and updating site configuration settings.

feat(config): create SiteConfig entity and repository for configuration management

- Created SiteConfig entity and repository to manage site configuration data.
2026-06-11 12:20:12 +03:30
hamed cced85456a Add API documentation for Representation, Secretary, Settlement, SMS, Specialty, Tag, and User Profile endpoints 2026-06-11 10:27:27 +03:30