Commit Graph
24 Commits
Author SHA1 Message Date
hamed f00ed23f00 Refactor code structure for improved readability and maintainability 2026-07-23 15:47:17 +03:30
hamed 357adb1d14 feat: enhance payment status handling and summary in MyPaymentsPage
- Added support for 'partial' payment status in MyPaymentsPage and related components.
- Updated API responses to include 'paid_rials' and 'summary' for invoices.
- Introduced InvoicePaymentStatus service to derive payment status based on actual payments.
- Enhanced tests to cover new payment scenarios including partial payments and payment methods.
- Updated documentation to reflect changes in payment status and API responses.
2026-07-19 10:38:29 +03:30
hamed 5c8fe8ece4 feat: add payments summary endpoint and UI redesign for MyPaymentsPage
- Implemented a new API endpoint `/api/v1/my/billing/payments/summary` to provide a financial summary of payments with filters for national code, status, and date range.
- Updated the InvoiceRepository to aggregate totals for paid and unsettled invoices.
- Created a new hook `usePaymentsSummary` to fetch summary data in the frontend.
- Redesigned the MyPaymentsPage to align with the ClaimsPage structure, incorporating a design system, summary statistics, and improved filtering options.
- Added tests for the new payments summary endpoint to ensure correct functionality and filtering behavior.
2026-07-19 10:12:01 +03:30
hamedandClaude Fable 5 7baa4df3d4 fix(booking): aggregate public booking state across all schedules
The public doctor payload built `active`/`free_turn`/`hours_of_work` from the
personal schedule alone, so a doctor bookable only at a clinic was reported as
"نوبت‌دهی غیرفعال". Aggregate over every schedule instead: any schedule with
online booking on and an active day makes the doctor bookable, and the disabled
label only appears when all of them are off.

Three admin-panel fixes for the same class of bug:

- AppointmentsPage took the selected doctor from `dbUuid`, which is the clinic's
  uuid inside a clinic context — the slots request 404'd. Use `doctorUuid`.
- TurnsTimeline rendered any error or unknown empty_reason as "این روز شیفت کاری
  ندارد". Errors now surface as errors and unknown reasons get a neutral message;
  the day-off wording is reserved for an explicit day_off from the backend.
- Admins have no clinic context, so slots fell back to the personal schedule.
  They now pick a location from `appointment-booking-locations` and that choice
  drives the slot, service and create-appointment requests.

Adds `app:schedule:normalize-format` for legacy rows stored as a bare JSON list
covering only Saturday, which read as day-off for the rest of the week.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:25:24 +03:30
hamedandClaude Opus 4.8 7a0654f8ba fix(booking): carry the clinic context through the panel and drop phantom locations
Two faults, one root: the per-context booking work updated ScheduleSection but
left the rest of the panel calling slot endpoints without clinic_uuid. Absent
clinic_uuid means the personal practice, so the panel asked about a schedule the
doctor barely uses and got nothing back.

- useClinicContext() resolves the current environment once and is used by the
  appointments page, useDoctorBookingServices, ServiceSlotPicker and both
  queries in NewAppointmentDrawer (a fifth call site a sweep turned up). It
  returns null in a doctor's personal environment so the mirror-image bug — a
  doctor seeing the clinic's schedule at their own practice — cannot appear.
  clinicUuid is part of every query key; without it the cache leaks across
  environments.
- appointment-slots returns empty_reason (no_schedule | holiday | day_off |
  outside_window). TurnsTimeline rendered «این روز تعطیل است» for any empty day,
  which is what the bug report actually saw; it now says which of the four it is.
- booking-locations lists a location only when the context has an address and an
  active shift points at it. The dev data had three "personal" schedules whose
  shifts referenced the clinic's address, so the public site advertised a
  personal practice that could never be booked.
- ?date= adds available_on_date per location, validated as a real calendar date.
- MyAppointmentsController and AdminApiController resolved the appointment
  address with no context and could store the wrong one. Both now go through the
  new BookingContextResolver, which also replaces AppointmentController's private
  copy of the same membership check.
- app:schedule:audit-locations reports shifts pointing at a missing or foreign
  address; --fix deactivates them rather than deleting.

Verified against the reported doctor: same date, no clinic_uuid -> 0 sessions,
with it -> 1 session; a full week matches the configured Sat/Tue/Wed/Thu.

Suite: 417 tests, 2 failures — both pre-existing and unrelated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 14:35:31 +03:30
hamed e4ddd38f0c feat: add per-doctor permissions management in clinics
- Implement DoctorPermissionsModal for managing doctor permissions in clinics.
- Create usePermissions hook to handle user permissions context.
- Add migration for clinic_doctor_permissions table with default permissions.
- Develop ClinicDoctorPermissionController for handling permissions API.
- Create ClinicDoctorPermission entity to manage permissions data.
- Implement ClinicDoctorPermissionRepository for database interactions.
- Add ClinicDoctorPermissionChecker for permission validation logic.
- Write tests for clinic doctor permissions functionality.
2026-07-18 09:44:13 +03:30
hamedandClaude Opus 4.8 76bfa5d36f fix: issue invoice on demand so view-invoice buttons actually work
The admin SPA never called POST /billing/invoices, so every session had
invoice_uuid = null and the view-invoice buttons on the patient services
tab (and MyPatients visit modal) were permanently disabled. Add a shared
useIssueInvoice hook (idempotent create + finalize when draft) and wire
it into:
- DetailsStep: the wizard's final step now really issues the invoice
- SessionServiceCard / PatientDetailPage: clicking view-invoice on a
  session without an invoice issues it first, then opens the summary
- MyPatientsPage visit modal: same, replacing the dead disabled button

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 15:39:02 +03:30
hamedandClaude Opus 4.8 ef97b2b249 feat: unify wallet with real payment methods, full transaction transparency, pay-session-from-wallet
Address wallet feedback: use the clinic's real payment infrastructure,
redesign the tab to match the admin panel, and make every wallet movement
fully auditable.

Backend:
- WalletTransaction: add createdBy (acting user) + createdByName, payment_method,
  reference, status; toArray exposes them (migration Version20260716083939).
- WalletService (Settlement): balance/charge/withdraw + settleSessionFromWallet,
  records actor/method/reason; insufficient balance throws ERR_WALLET_INSUFFICIENT.
- PatientController: charge/withdraw delegate to WalletService and accept
  payment_method/reference; PATCH /session/{uuid} with payment_method=wallet
  debits the patient's final share from the wallet (reference=session:{uuid}).
- docs/api/patient.md updated.

Frontend:
- Wallet modal redesigned to panel style (no gradient); payment method now uses
  the clinic's real bank accounts + POS devices (usePaymentMethods) plus cash.
- Wallet tab: panel balance card + DataTable ledger with columns مبلغ/نوع/روش/
  دلیل/ثبت‌کننده/تاریخ/ساعت/وضعیت + همه/واریزی/برداشت filters.
- Session card «تکمیل پرداخت» opens a payment-method chooser incl. کیف پول.

Tests: backend transparency + session-from-wallet (success/insufficient/cash);
frontend modal (real methods, toman→rials) + wallet tab + settle chooser.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 12:22:37 +03:30
hamedandClaude Opus 4.8 f028d6841a feat: port wallet charge/withdraw modal from tauri to patient admin page
Add manual wallet withdrawal (debit) endpoint mirroring the offline app's
balance guard, and rebuild the patient کیف پول tab around a single
charge/withdraw toggle modal (quick amounts, تومان→ریال conversion,
transaction filters).

Backend:
- POST /api/v1/patient/{uuid}/wallet/withdraw — creates a debit
  WalletTransaction; 422 ERR_WALLET_INSUFFICIENT when amount exceeds balance.
- ErrorCodes: ERR_WALLET_INSUFFICIENT ('موجودی کیف پول کافی نیست').
- docs/api/patient.md updated.

Frontend:
- usePatientWallet hook (balance + charge/withdraw mutations).
- WalletTransactionModal (toggle, quick amounts, UI-only payment fields).
- WalletTab: charge button, همه/واریزی/برداشت filters.

Tests: backend withdraw success/insufficient/non-positive/ownership;
frontend modal + wallet tab interactions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 11:56:39 +03:30
hamedandClaude Opus 4.8 e00fe997f9 feat: section-based service picker + per-appointment duration override (service mode)
Service-booking mode now selects services by section like slot mode:
appointment-booking-services returns service_section per item; ServiceSlotPicker
groups by section (SearchableSelect), accumulates picks across sections into a
removable 'section -> service' chip list.

Secretaries can override a service's duration for a single appointment without
changing the service default: appointment-service-slots accepts durations[uuid]
and both create endpoints accept service_durations; the override drives total
duration and slot_end. Online (patient) booking is unaffected — it never sends
overrides. Backend + frontend tests and docs updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 10:41:15 +03:30
hamed 4715649fd8 feat(appointment): implement service mode functionality with service slot selection and validation 2026-07-16 00:02:36 +03:30
hamed 3e5dee0ad5 feat: Implement category and unit selection for inventory items
- Added a new 'category' field to the InventoryItem entity and updated the database schema.
- Replaced free-text input for 'unit' and 'category' with select dropdowns in the AddItemModal.
- Introduced a new API endpoint to fetch metadata for units and categories.
- Updated inventory filtering logic to use the new 'category' field instead of 'consumable'.
- Enhanced validation for item creation and updates to ensure valid unit and category values.
- Updated tests to cover new functionality and ensure proper validation.
2026-07-15 14:32:29 +03:30
hamedandClaude Opus 4.8 519bc8d7f6 feat: port inventory (انبارداری) page from tauri to admin dashboard
Add a per-tenant (doctor/clinic) Inventory domain and admin page, ported
from clinic-pro-tauri /inventory (which was static/mock) into a real feature.

Backend (src/Inventory/):
- Entities InventoryItem, InventoryPackage, InventoryPackageItem, scoped via
  entity_type/entity_id like TenantTag. Item status is derived, package total
  and availability derived at read time.
- InventoryService (stats, package assembly, availability), thin
  InventoryController with CRUD for items and packages + categories endpoint.
- Migration + docs/api/inventory.md + functional tests (10 tests, 42 assertions).

Frontend (assets/admin/):
- InventoryPage with two tabs (کالاهای مصرفی / پکیج), stat cards, items table
  (desktop + mobile cards), packages accordion, add/edit item and package
  modals, search + category filter — pixel-matched to the tauri source.
- useInventory hook (TanStack Query), route + sidebar link for doctor/clinic.
- Vitest coverage (real data, empty state, modal, packages tab).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:48:29 +03:30
hamedandClaude Opus 4.8 b459d082a4 feat: port payment management tab from tauri to admin dashboard
Add per-clinic payment methods (bank accounts + POS/card-reader devices)
under the "مدیریت پرداخت" settings tab at /admin/my-financial, ported from
clinic-pro-tauri's mock-only PaymentManagement tab into a real persisted
feature. These records are referenceable (by uuid) from patient invoices to
record which method a service payment was made with.

Backend (new src/PaymentMethod domain):
- BankAccount + Pos entities, repositories, PaymentMethodService (validation,
  ownership scoping, create/update/toggle logic).
- Thin PaymentMethodController exposing /api/v1/my/payment-methods/{bank-accounts,pos}
  (GET/POST/PUT + PATCH .../status), guarded to clinic/doctor/secretary/admin.
- Migration for bank_accounts + pos_devices tables.
- Functional tests (success + validation/404/403 + empty boundaries).
- docs/api/payment-method.md.

Frontend:
- Replace MyFinancialPage content with the payment-management UI (two tabs,
  tables, add/edit modals, status toggle) using the admin design system.
- usePaymentMethods hook (TanStack Query) + presentational components.
- Update page test to cover tabs, data, empty state and the add modal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:39:32 +03:30
hamedandClaude Opus 4.8 818506bf36 refactor(billing): rebuild payments list as flat invoice list (tauri parity)
Align /admin/my-payments with the tauri /payments source (per user): the list
is now a flat, newest-first list of the tenant's recorded invoices — one row
per invoice — instead of the per-patient aggregation built from Figma.

Backend:
- replace InvoiceRepository::patientPaymentSummary aggregation with
  tenantInvoices/countTenantInvoices (flat, joins patient name/national code).
- InvoiceService::patientPaymentList → tenantInvoiceList.
- BillingController: GET /api/v1/my/billing/patient-payments →
  GET /api/v1/my/billing/payments returning
  { invoice_uuid, patient_uuid, patient_name, national_code, issued_at,
    amount_rials, status } rows.
- node-2 patient invoices endpoint unchanged.

Frontend:
- useMyPayments: usePatientPayments → usePayments (flat PaymentRow).
- MyPaymentsPage columns match tauri DetailT: row #, avatar+name, national
  code, date-time, amount paid, مشاهده (no status column); 'اضافه کردن بیمار'
  links to /admin/patients/new. Filters (national code / status / Jalali date
  range) kept.

Tests + docs/api/billing.md updated. Intentionally omitted tauri extras:
mobile Cards view and the advanced ModalFilter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:00:17 +03:30
hamedandClaude Opus 4.8 4c29fa3274 feat(billing): patient payments list + patient invoices detail (doctor/clinic)
Port two nobat724 Figma screens into the admin SPA for the doctor/clinic
tenant panel:

- node 1 — لیست پرداخت‌ها (/admin/my-payments): per-patient payment summary
  (invoice count, paid, remaining, derived status paid/unsettled/unpaid),
  filters by national code / status / Jalali date range, pagination.
- node 2 — پرداخت‌های ثبت‌شده (/admin/my-payments/:patientUuid): a patient's
  recorded invoices with patient header, service title, total, status badge,
  and an expandable per-invoice item breakdown.

Backend (App\Billing):
- InvoiceRepository::patientPaymentSummary/countPatientPaymentSummary — DQL
  aggregation grouped by patient record (arbitrary join Invoice→PatientRecord
  →User), draft/void excluded, derived-status HAVING filters.
- InvoiceRepository::invoicesForPatient/count + InvoiceService methods that
  shape rows and derive status.
- BillingController: GET /api/v1/my/billing/patient-payments and
  GET /api/v1/my/billing/patients/{patientUuid}/invoices (thin, resolveEntity,
  tenant-scoped, 403/404). Invoice::getIssuedAt / InvoiceItem::getTitle added.
- docs/api/billing.md documents both endpoints.

Frontend: useMyPayments hooks, MyPaymentsPage, MyPaymentDetailPage, routes in
App.tsx (doctor/secretary/clinic, blockClinicScope) and a sidebar entry.
Persian strings hardcoded per existing admin convention (no i18n infra).

Tests: tests/Billing/PatientPaymentsTest.php (8), useMyPayments + both page
tests (11). Note: pre-existing LoginPage.test failures are unrelated (proven
by stashing this change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 18:24:33 +03:30
hamedandClaude Opus 4.8 580bc983b3 feat(patient): complete "اطلاعات پرونده" demographic form
Backend:
- Add profile columns field_of_study, province_id, city_id, postal_code,
  referral_source (UserProfile + migration).
- Extend PATCH /api/v1/patient/{uuid} to persist all demographic fields
  and return them in the patient profile payload.
- Support editable mobile (login identifier): validation, uniqueness,
  User.setMobileNumber, new ERR_PROFILE_002.
- Update docs/api/patient.md.

Frontend:
- New reusable Input, Field, and PatientRecordInfoForm (RHF + Zod).
- usePatient/useUpdatePatient hooks and patientForm mapping helpers.
- Extend the existing "info" tab in MyPatientsPage to the full field set
  via the shared form (province/city/insurance options, Jalali date).

Tests: Patient entity + PATCH integration (PHPUnit); form, hooks, and
mapping helpers (Vitest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:51:18 +03:30
hamed 828e3552c0 feat(subscription): implement effective plan logic and update free plan features 2026-07-05 10:46:31 +03:30
hamed dfc86391c4 feat(payment): enhance payment configuration to include active gateways and update CORS settings 2026-07-01 12:45:18 +03:30
hamed 6084dc5d6b feat: add comprehensive tests for UI components, hooks, and API interactions
- Implement tests for Pagination, StatusBadge, ConfirmDialog, and MobileInput components.
- Add tests for useSubscription, usePaymentConfig, and usePwaInstall hooks.
- Create tests for API requests in the api module, including success and error handling.
- Add utility function tests for formatting and validating Iranian mobile numbers.
- Implement tests for BlogFormPage and BlogsPage to validate form submissions and data fetching.
- Add tests for LoginPage to ensure proper validation and state management.
- Create tests for authStore and uiStore to validate state management and functionality.
- Set up Vitest configuration and testing utilities for consistent testing environment.
2026-06-28 22:58:15 +03:30
hamed 3c103bc51e feat: update SMS settings review functionality
- Changed the tab name from 'post-visit-review' to 'post-visit-approved' in SmsPage.
- Introduced a new query for fetching approved post-visit texts.
- Updated the UI to display approved post-visit texts and their details.
- Enhanced the API endpoint to filter SMS settings based on status (pending/approved).
- Added entity name resolution for doctors and clinics in the SMS settings.
- Updated the SmsWalletPage to include new post-visit text variables for SMS templates.
- Improved type definitions in index.ts for better clarity and consistency.
2026-06-22 10:30:46 +03:30
hamed 3763c83471 feat(payment): refactor payment configuration retrieval into a custom hook 2026-06-15 12:56:43 +03:30
hamed fa6656df48 feat(subscription): implement feature gating for subscription-based access in admin panel 2026-06-15 12:00:36 +03:30
hamed d50fb96542 pwa setting 2026-06-13 13:28:39 +03:30