Commit Graph
100 Commits
Author SHA1 Message Date
hamedandClaude Opus 4.8 131a78343b test(audit): strengthen H5/H7/H8 into true fail-without-fix regressions
Verification pass found three tests only guarded correctness, not the fix's
behavior:
- H7: add repository white-box test asserting likes/replies come back as
  initialised PersistentCollections (lazy without the fetch-join).
- H8: add a query-count test (constant vs coverage-row count) — without the
  batch fetch the count grows ~1 per row.
- H5: add an end-to-end test hitting DELETE /api/v1/doctor and asserting the
  insurance config is purged (the service unit test didn't cover the wiring).

All three now fail when their fix is reverted. Suite: 39 tests / 92 assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:01:12 +03:30
hamed c9ae3882fe docs(audit): mark Critical+High tier complete in backlog 2026-06-28 19:42:39 +03:30
hamedandClaude Opus 4.8 d8f7db0ada perf(billing,settlement): paginate claims and wallet transactions (H9, H10)
GET /billing/claims loaded every tenant claim with no limit. Add
findByTenant(page, limit) + countByTenant (shared query builder), default
limit 50 / max 100, and expose totals as data.meta — kept inside the existing
{ data: { data: [...] } } envelope so current clients are unaffected.

GET /wallet/transactions was already bounded (findByUser defaulted to limit 50)
but page-less; add page/offset + countByUser + the same additive meta.

Regression: tests/Billing/ClaimsListPaginationTest,
tests/Settlement/WalletTransactionsPaginationTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:42:04 +03:30
hamedandClaude Opus 4.8 42d934abc2 perf(rating): fetch-join comment tree to kill N+1 in public list (H7)
findApprovedRootsByDoctor used a plain findBy, so Comment::toArray() lazy-loaded
likes, replies and the author per comment (and recursively per reply). Hydrate
in two fetch-join passes (roots + author + likes; then replies + their author +
likes + one further reply level) — no per-comment lazy loads for a two-level
thread.

Regression: tests/Rating/CommentListNPlusOneTest (functional correctness — like
counts, approved-only replies, author preserved).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:35:34 +03:30
hamedandClaude Opus 4.8 eb1997066e perf(insurance): batch-fetch service items in coverage list (H8)
listServiceCoverage called serviceItemRepo->find() once per coverage row (N+1).
Collect the ids and fetch them in one findBy(['id' => $ids]), then map by id.

Regression: tests/Insurance/ServiceCoverageNPlusOneTest (functional correctness —
every row resolves the right service_item_uuid).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:29:06 +03:30
hamedandClaude Opus 4.8 6b12f3ddb9 perf(doctor): fetch-join specialties in clinic doctor list (H6)
findByClinicWithFilters left-joined specialties only for filtering, so
toListArray() lazy-loaded them per doctor (N+1). addSelect them and switch the
result fetch to Paginator(fetchJoinCollection: true) so LIMIT still paginates by
doctor.

Test infra: ApiTestCase::countQueries() (via doctrine.debug_data_holder).
Regression: tests/Doctor/ClinicDoctorListNPlusOneTest asserts the query count
does not grow with doctor count (4→10 without the fix).

Also relaxed AppointmentExpiryServiceTest's exact-count assertion (it counts all
stale pendings in the shared db_test, which accumulates) — logged test-isolation
debt as E6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:24:49 +03:30
hamedandClaude Opus 4.8 4cf6873900 fix(db): purge insurance config when a doctor/clinic is deleted (H5)
tenant_insurances, entity_insurance_pricing and tenant_service_coverages
reference their owner through a polymorphic (entity_type, entity_id) pair, so no
database FK can cascade their cleanup. Hard-deleting a doctor (DoctorController)
or clinic (AdminApiController) left these rows orphaned.

Add TenantInsuranceCleanupService::purgeForEntity() and call it from both delete
paths — removes coverage (via owning tenant_insurance ids), then tenant
insurances, then pricing.

Residual (separate, lower-freq paths): deleting an insurance category or a
service_item still orphans rows that reference them by id — tracked under the
medium-tier soft-ref findings.

Regression: tests/Insurance/TenantInsuranceCleanupTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:16:10 +03:30
hamedandClaude Opus 4.8 d14ac38da5 fix(db): RESTRICT delete of a Payment that has a FinancialBreakdown (H4)
The ledger FK used ON DELETE CASCADE on a non-nullable column, so deleting a
Payment silently destroyed its immutable financial breakdown rows. Switch to
RESTRICT — a settled payment can no longer be deleted out from under its ledger.

Regression: tests/Settlement/FinancialBreakdownIntegrityTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:10:53 +03:30
hamedandClaude Opus 4.8 7fa4b55d3f fix(security): unique payment reference_id, reject replayed callbacks (H3)
reference_id (the gateway's settled-transaction ref) was not unique, so the
same successful callback — or a RefNum replayed onto another order — could
credit twice. Add a unique index (NULL until success, so pending/failed rows
don't collide) and an application-level pre-check in the callback that fails the
payment if the reference already belongs to another order. The unique index is
the hard backstop behind the check.

Regression: PaymentCallbackAmountTest::testReplayedGatewayReferenceIsRejected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:08:26 +03:30
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
hamedandClaude Opus 4.8 ae06498a96 fix(security): verify gateway-confirmed amount in payment callback (C1)
The callback marked an order success on any verify-ok result without comparing
the gateway-settled amount to the amount charged. SEP returns AffectiveAmount;
an underpayment or a replayed RefNum from a cheaper order would confirm the
expensive order. Now reject (status=failed, no activation) when the gateway
reports an amount that mismatches the stored amount_rials. Gateways that don't
report a settled amount (Mellat binds it server-side) skip the check.

MockGateway now echoes mock_amount so the guard is exercisable in tests.
Regression: tests/Payment/PaymentCallbackAmountTest (underpayment rejected,
matching amount succeeds).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:49:33 +03:30
hamedandClaude Opus 4.8 8e5cd51873 docs(audit): durable backlog — 5-dimension scan, 70 findings prioritized
Persists the audit plan so a session restart no longer loses it (TodoWrite
is volatile). 10 done, 1 critical, 10 high, 21 medium, 12 low, 4 epics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:44:25 +03:30
hamedandClaude Opus 4.8 6bd49c2d3e fix(security): make commission_percent & active admin-only on PATCH representation
A representation editing its own record could raise its own commission or
self-activate (privilege escalation). Restrict both fields to ROLE_ADMIN and
range-check commission (0–100). Owner can still edit name/city/bank.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 17:36:48 +03:30
hamed e456809b55 fix(devops): repair phpstan config so static analysis runs again
- container_xml_path -> containerXmlPath (phpstan-symfony v2 rename); the old
  key made phpstan abort with an invalid-configuration error, so analysis
  silently never ran
- add the missing tests/doctrine_object_manager.php loader
- drop a stale ignoreErrors pattern

phpstan now runs and surfaces 42 pre-existing level-5 errors (tracked separately).
2026-06-28 17:17:46 +03:30
hamed 8b96753df6 perf(db): add missing indexes (appointment expiry, session dates, user status)
- appointments(status, expires_at): the per-minute expiry scheduler filtered on
  these with no covering index
- patient_sessions(created_at): dashboard revenue range scans
- users(status): admin user-list filter

PatientRecord(entity_type,entity_id) already covered by its unique constraint;
UserActiveContext.user_id is the PK — neither needed a new index.
2026-06-28 17:13:44 +03:30
hamed e1740462b5 perf(appointment): batch-fetch pending payments in expiry loop (fix N+1)
AppointmentExpiryService ran one findPendingByAppointment query per expiring
booking. Add PaymentRepository::findPendingByAppointments (one IN query keyed
by appointment id) and use it. Test covers expiry + payment cancellation for
several appointments at once.
2026-06-28 17:10:32 +03:30
hamed 36b87e9817 fix(db): declare onDelete on required FKs that had none
Eight required (nullable:false) FKs had no referential action. Set per the
codebase's existing pattern: CASCADE for owned relations (doctor_insurances,
doctor_secretaries), RESTRICT for owner/reference FKs (Doctor/Clinic.user_id,
invited_by_id, subscription plan/period). Only the two CASCADE FKs need DDL —
RESTRICT is the MySQL default.
2026-06-28 17:04:23 +03:30
hamed 372bea4849 test(orm): regression — every entity with a custom repo declares repositoryClass
Auto-discovers each mapped entity that has a sibling <Entity>Repository class
and asserts getRepository() returns it (not Doctrine's default). Catches the
prod-only opcache.preload bug class that broke /oauth/userinfo.
2026-06-28 16:53:40 +03:30
hamed 093293004a fix(security): enforce ownership on GET date-override (IDOR)
getOverride leaked any doctor's override to any authenticated user; add the
owner-or-admin check (matching the update/delete endpoints) + regression test.
2026-06-28 16:48:41 +03:30
hamed 90736c8149 fix(security): enforce ownership on GET weekly-schedule (IDOR)
getSchedule returned any doctor's schedule to any authenticated user — the
mutation endpoints (update/delete) already checked owner-or-admin but this GET
did not. Add the same check + regression test (fails without the fix).
2026-06-28 16:47:05 +03:30
hamedandClaude Opus 4.8 30c5fbe98c test: functional test infrastructure (ApiTestCase + db_test + JWT helpers)
- .env.test points at db_test (doctrine when@test appends _test suffix)
- ApiTestCase: WebTestCase base with createUser/jwtFor/authJson helpers
- InfraSmokeTest: proves boot + DB + JWT auth pipeline works (/health 200,
  unauth /oauth/userinfo 401, JWT-auth userinfo 200)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:44:05 +03:30
hamedandClaude Opus 4.8 05fde81320 fix(orm): declare repositoryClass on all entities with a custom repository
On prod (opcache.preload + prod container), getRepository(Entity::class)
returned Doctrine's default repository instead of the custom one when the
entity's #[ORM\Entity] had no repositoryClass — so custom finders like
DoctorSecretaryRepository::findAllActiveBySecretary threw BadMethodCallException,
making /oauth/userinfo return 500 after login. Declare repositoryClass explicitly
on all 25 affected entities.

Also add app:create-admin command (create/promote a ROLE_ADMIN user by mobile).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:19:07 +03:30
hamed 5bcc97ceb6 feat: add CreateAdminCommand for admin user creation and promotion 2026-06-28 15:57:30 +03:30
hamedandClaude Opus 4.8 5f557b2c57 perf(docker): parallel ext compile (MAKEFLAGS=-j) to beat Coolify build timeout
Coolify builds with --no-cache, so intl (C++) recompiles from source every
deploy (~180s single-threaded) and overran the build timeout (exit 255).
Compile across all cores via MAKEFLAGS=-j$(nproc) to cut it several-fold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:38:10 +03:30
hamed cb401c6dbf merge: Coolify production hardening (non-root, healthcheck, opcache, graceful shutdown) 2026-06-28 15:28:21 +03:30
hamed 41114aa4cb feat: add comprehensive Docker production hardening documentation for Coolify stack 2026-06-28 15:27:51 +03:30
hamedandClaude Opus 4.8 5150365d2c harden(docker): non-root, dedicated healthcheck, opcache split, graceful shutdown
Coolify-doc-driven production hardening of the deploy stack:
- run the whole stack as non-root www-data; nginx on 8080 (non-privileged),
  pid in /tmp, user directive dropped (Coolify routes to any port)
- docker/healthcheck.sh: hit real /health route via PHP (not just port probe)
- split OPcache config into docker/php/opcache.ini
- graceful shutdown: supervisord stopsignal/stopwaitsecs + worker stop_grace_period
- APCu intentionally not added (Symfony cache uses redis)
- DEPLOY.md: 8080 port, non-root, resource-limit guidance

Verified on linux/amd64: non-root uid=82, /health 200, migrations run,
worker process healthcheck OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:27:34 +03:30
hamed c74b06b3cc fix: correct spelling of 'connexion' to 'connection' in PostgreSQL configuration comments 2026-06-28 14:55:38 +03:30
hamedandClaude Opus 4.8 7af3b5a600 fix: add symfony/redis-messenger + real healthchecks
worker-async crashed in a loop with 'No transport supports Messenger DSN
redis://...' because symfony/redis-messenger was never installed — 10x restart
killed the whole stack. Add the package (v7.4.8).

Also harden healthchecks:
- app: hit the real /health route via PHP get_headers (verifies app boots and
  serves, not just that port 80 is open)
- workers: confirm the messenger:consume process is alive via busybox ps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 14:54:45 +03:30
hamedandClaude Opus 4.8 61ffb2ecd8 fix(docker): retry composer install + source fallback for flaky GitHub on Coolify
Coolify build host intermittently gets HTTP 400 from codeload.github.com on
Composer dist downloads. Bump COMPOSER_HTTP_RETRIES, wrap the install in a
5x retry loop, and on final attempt fall back to --prefer-source (git clone,
different endpoint than dist zips).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 14:25:48 +03:30
hamed f1dd540ca2 fix(docker): add coolify network to services for proper connectivity 2026-06-28 13:42:32 +03:30
hamed 745e2fc7f2 fix(docker): adjust formatting for RUN_INIT and define external coolify network 2026-06-28 13:23:07 +03:30
hamed 42bb723333 refactor: separate MariaDB and Redis into independent Coolify resources 2026-06-28 11:28:29 +03:30
hamedandClaude Opus 4.8 471f43248b fix(docker): generate minimal .env when absent (repo .env is gitignored)
Symfony Dotenv::bootEnv() hard-requires a .env file even in prod. The repo's
.env holds dev secrets and is gitignored, so Coolify's clone ships none and the
app fatals with 'Unable to read /app/.env'. Write a minimal APP_ENV=prod .env
at build if one wasn't copied; all real values still come from the compose
environment (clear_env=no), which Dotenv never overwrites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 10:54:38 +03:30
hamedandClaude Opus 4.8 46e462c6b9 fix(docker): build phpredis from GitHub source (Coolify host can't reach pecl.php.net)
pecl install redis and install-php-extensions both fail on the Coolify build
host with 'No releases available for package pecl.php.net/redis' — that host
cannot reach the pecl registry. Build phpredis 6.1.0 from its GitHub source
tarball instead (github.com is reachable); pdo_mysql/intl/opcache stay as
bundled docker-php-ext-install (no network).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 10:34:18 +03:30
hamed 8ae35b1c8f fix(docker): enhance PHP extension installation with retry logic and use prebuilt binaries 2026-06-28 10:09:32 +03:30
hamed 26fe7c4fc0 fix(docker): implement retry logic for package installation and optimize image usage in docker-compose 2026-06-28 09:57:11 +03:30
hamed 90a908c503 fix(migrations): ensure safe migration steps for doctor_expertise and doctor_cities, drop legacy tables conditionally 2026-06-28 09:32:27 +03:30
hamed e966e11807 feat: update Docker configuration and add deployment documentation for Coolify 2026-06-27 21:27:58 +03:30
hamed 29badf8d68 fix: update references to docker-compose file and remove unused compose files 2026-06-25 21:40:51 +03:30
hamed b56d37f97f feat(docker): add docker-compose configuration for Coolify deployment 2026-06-25 21:40:44 +03:30
hamed cffc88db05 feat: Implement Docker-based deployment for ClinicPro on Coolify
- Added Dockerfile for multi-stage build including PHP, Node.js, and Nginx.
- Created docker-compose.coolify.yaml for service orchestration with app, workers, MariaDB, and Redis.
- Introduced entrypoint.sh for initialization tasks like JWT key generation and database migrations.
- Configured Nginx with default.conf for handling requests and routing to PHP-FPM.
- Added php.ini with production settings and opcache configuration.
- Set up supervisord.conf to manage PHP-FPM and Nginx processes.
- Created frontend-domains.json for managing allowed frontend domains.
- Added gen-cors-env.php script to generate CORS environment variables from frontend domains.
- Updated framework.yaml to configure trusted proxies and headers.
- Created .dockerignore to exclude unnecessary files from the Docker context.
- Added .env.coolify.example for environment variable configuration.
- Documented deployment steps and troubleshooting in coolify.md.
2026-06-25 21:27:28 +03:30
hamed dfda265af4 feat(gitignore): add SQL backup files to .gitignore and remove clinicpromain directory 2026-06-25 21:07:19 +03:30
hamed 174c683813 Remove state data and seed scripts for realistic and test data 2026-06-25 21:07:09 +03:30
hamed 1997b78d11 feat(calendar): add year picker functionality to PersianDatePicker and PersianCalendar 2026-06-25 20:00:13 +03:30
hamed 60b2224ec3 Add AST cache files for representation API and ApiIrService
- Created a new JSON file for the representation API documentation, including nodes and edges that describe the API structure and relationships.
- Added another JSON file for the ApiIrService class, detailing its methods, imports, and relationships with other components.
2026-06-25 19:42:42 +03:30
hamed c2c6ae4d02 feat(migrations): add national_code_verified flag to users and normalize bank_account representation
- Added a new column `national_code_verified` to the `users` table.
- Normalized the `bank_account` field in the `representations` table from a single object to an array of IBANs with a default `verified` status of false.

feat(ApiIrService): implement identity verification client for api.ir

- Created `ApiIrService` to handle identity verification via api.ir.
- Implemented methods for matching national code with mobile and IBAN with national code and birth date.
- Added error handling and logging for external API requests.
2026-06-25 19:38:47 +03:30
hamed 9b608aaeac feat(auth): implement refresh token handling and update login method 2026-06-25 17:37:55 +03:30
hamed 694ee28787 feat(settlement): add receipt handling and detail view for settlements 2026-06-25 17:34:39 +03:30
hamed 71edc772c8 feat(payment): add Jalali date formatting for appointment confirmation SMS 2026-06-25 16:34:54 +03:30
hamed b1a43b7740 feat(sms): add log viewing functionality for SMS messages 2026-06-25 14:20:59 +03:30
hamed 41b435bb88 feat(clinic): allow representation role to view clinic details without edit permissions 2026-06-25 09:42:41 +03:30
hamed e65e506dae feat(representation): add welcome SMS notification for newly added doctors and clinics by representatives 2026-06-25 09:37:20 +03:30
hamed 763ad82b69 feat(settings): add appointment fee configuration and update payment logic 2026-06-24 20:25:58 +03:30
hamed 93fa0fc2c9 feat(representation): enhance finance page with summary cards and improved table styling 2026-06-24 20:16:10 +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 89e4a424f8 feat(tax): implement tax rate history tracking and API endpoints 2026-06-24 13:17:08 +03:30
hamed 148d033114 feat: Implement financial engine for commission and tax calculations
- Added new configuration keys for appointment and upgrade commissions, tax settings, and SMS panel fee in SiteConfigController and SiteConfigRepository.
- Introduced CommissionService to handle commission calculations for appointments and subscriptions, including tax deductions and SMS fees.
- Created FinancialBreakdown entity and repository to log financial transactions.
- Updated PaymentController to process commissions upon successful payments for appointments and subscriptions.
- Developed FinancialReportPage in the admin panel to display financial breakdowns and summaries.
- Added database migration for the new financial_breakdowns table.
2026-06-24 13:06:17 +03:30
hamed e0abaf5c0c feat(appointment): enforce mandatory patient national code and gender with validation 2026-06-24 12:32:58 +03:30
hamed b7df8cf9ee feat(representation): add endpoints for doctor statistics and toggling doctor status 2026-06-24 12:24:53 +03:30
hamed 8b419d0272 feat(profile): enhance national_code uniqueness error message with masked mobile number 2026-06-24 12:16:16 +03:30
hamed 650e36bce2 feat(profile): enforce uniqueness of national_code across user profiles and update related error handling 2026-06-24 11:52:13 +03:30
hamed 3eb82ffa7d feat(claims): replace PersianDateInput with PersianDatePicker and add quick date range buttons 2026-06-24 06:24:22 +03:30
hamed 91fb55258a feat(claims): enhance claims filtering with insurance, date range, and patient search 2026-06-24 06:19:25 +03:30
hamed 69beb7b944 feat(insurance): add insurance coverage flag to selected services and update coverage logic 2026-06-24 06:09:05 +03:30
hamed 17f41117f1 feat(tariffs): automate current year tariff registration and sync service price 2026-06-24 04:54:56 +03:30
hamed c189daa860 feat(claims): enrich claims with insurance details and item specifics 2026-06-24 04:48:11 +03:30
hamed 27840da78d feat: integrate insurance coverage management for clinic services
- Updated NewSessionPage to calculate patient share based on insurance coverage rules.
- Refactored billing calculations to utilize new patientShareOf function for service items.
- Enhanced API documentation to reflect changes in service coverage structure.
- Implemented ServiceInsuranceModal for managing insurance coverage per service.
- Added UI components for displaying and editing insurance coverage details.
- Removed obsolete toggle switch styles and adjusted CSS for new components.
- Ensured backend endpoints support both service_item_id and service_item_uuid for flexibility.
2026-06-24 04:23:50 +03:30
hamed 253414ef3e feat(subscription): add switch toggle component and update plan terminology 2026-06-24 03:32:50 +03:30
hamedandClaude Opus 4.8 15e7d92fc1 feat(subscription): gate insurance pages behind "insurance" plan feature
Wrap InsurancePricingPage and ClaimsPage in <FeatureGate feature="insurance">
so direct-URL access is blocked without an active subscription that enables
the feature. Add feature: "insurance" to the sidebar links (doctor/clinic/
secretary sections) so the menu items hide when the plan lacks it.

Make AdminSubscriptionPage feature controls dynamic: derive the plan feature
checkboxes from FEATURE_LABELS (now including "insurance") and switch the Zod
schema to z.record, so adding a feature only touches the label map.

No backend/migration change: plan.features is free-form JSON; existing plans
default to insurance:false and admins enable it per plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 03:21:52 +03:30
hamed fc87e4a04e feat(schedule): enhance time selection with separate hour and minute dropdowns 2026-06-24 02:43:05 +03:30
hamed fbfe25c3c6 feat(claims): add insurance_name field to claims and update API responses 2026-06-24 02:11:16 +03:30
hamed f7eb1ebd91 feat(claims): implement automatic claim creation for insurance sessions 2026-06-24 02:08:12 +03:30
hamedandClaude Opus 4.8 14c6dd6456 feat(patients): prefill visit price from free-visit price
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:55:19 +03:30
hamedandClaude Opus 4.8 115690d81a feat(insurance): free-visit price field on pricing page
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:21:25 +03:30
hamedandClaude Opus 4.8 43e8822db5 feat(patients): visit insurance options from tenant contracts + auto coverage%
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:17:37 +03:30
hamedandClaude Opus 4.8 0e6111f1be feat(patients): service quantity in visit session
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:13:51 +03:30
hamedandClaude Opus 4.8 f41314a3ad refactor(ui): unify remaining native selects (claims/sms/wallet/secretaries)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 21:06:45 +03:30
hamedandClaude Opus 4.8 0bc1b4a2e9 refactor(ui): unify selects via SearchableSelect (session form + contracts)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 21:00:19 +03:30
hamedandClaude Opus 4.8 c556f3a53d refactor(insurance): merge pricing into contracts (basic+supp groups)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:55:56 +03:30
hamedandClaude Opus 4.8 18ae55ac30 feat(patients): standalone new-session page with sectioned UX
Dedicated route /admin/my-patients/:recordUuid/session/new replacing the
modal: insurance & price section (auto-fill from pricing), services,
payment/notes, sticky live summary. New-visit buttons navigate to it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:41:22 +03:30
hamed 017ea2ae22 chore(caveman): remove outdated README and SKILL documentation 2026-06-23 20:32:37 +03:30
hamedandClaude Opus 4.8 d049e42ddc feat(insurance): edit tenant insurance contracts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:32:34 +03:30
hamedandClaude Opus 4.8 2ef38f082d fix(insurance): pricing section only basic insurances
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:23:35 +03:30
hamedandClaude Opus 4.8 6ff79b9998 feat(ui-kit): patient detail tab bar (info / sessions)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:09:31 +03:30
hamedandClaude Opus 4.8 06d1f1d39b feat(ui-kit): patient card tauri layout + grid/list toggle
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:05:01 +03:30
hamed 3c867d1e09 feat(caveman): add new caveman communication mode with intensity levels 2026-06-23 19:10:00 +03:30
hamedandClaude Opus 4.8 2b00443af7 feat(ui-kit): orange accent avatar on patient cards (tauri match)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:50:43 +03:30
hamedandClaude Opus 4.8 0cef1518d7 feat(ui-kit): tauri pastel stat cards on doctor/clinic dashboards
Exact tauri stat-card palette tokens; doctor and clinic dashboards now
render 4 pastel StatCards (amber/violet/green/pink) matching the
clinic-pro-tauri reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:41:18 +03:30
hamedandClaude Opus 4.8 0bd18d74b7 feat(ui-kit): design tokens + StatCard + button variants (foundation)
Add accent (orange) + pastel stat-card tokens (light/dark), button
accent/outline/lg/block variants, and a pastel StatCard primitive,
aligning the admin design system with the clinic-pro-tauri reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:34:34 +03:30
hamedandClaude Opus 4.8 2206f02396 feat: enrich patient record with full profile
GET /api/v1/patient/{uuid} now returns a `profile` object from UserProfile
(demographics, contact, insurance names resolved). Admin record detail shows
a "patient info" section. Empty fields render as "ثبت نشده".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:21:33 +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
hamedandClaude Opus 4.8 51d7e24e04 feat: create patient record without prior signup
Extend POST /api/v1/patient to resolve user by uuid or mobile, and
create a new ROLE_USER (name + optional national code, no password)
when no user exists. Admin modal shows a new-patient form on lookup miss.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:20:56 +03:30
hamedandClaude Opus 4.8 59fb590d65 feat: redesign new-visit modal with insurance selects and live breakdown
Sectioned layout (insurance & price / services / payment / summary),
base+supplementary insurance selects auto-filling visit price and
discount from insurance-pricing, live cost breakdown, unified تومان unit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:15:12 +03:30
hamedandClaude Opus 4.8 89191eee57 feat: insurance & medical billing system (6 phases)
Multi-tenant insurance contracts, service coverage, versioned tariffs,
invoice calculation, and insurance claims with debt reporting.

- TenantInsurance: per-tenant insurance contracts (coverage/franchise/ceiling,
  versioning, soft-deactivate) + active guard
- ServiceItem.insuranceCovered + TenantServiceCoverage per-service overrides
- Tariff: versioned yearly tariffs with fallback to ServiceItem price
- Billing domain: Money/ShareBreakdown VOs, BillingCalculator (unit-tested),
  Invoice/InvoiceItem aggregate, InvoiceService.createFromSession
- Claim/ClaimItem with state machine (pending->submitted->approved/rejected->paid),
  ClaimService, insurance-debt report
- ClaimSubmitterInterface + ManualClaimSubmitter (future insurance API ready)
- Admin UI: insurance-pricing page, claims page, service tariff modal,
  service insurance toggle; routes + sidebar entries
- Architecture doc + billing/insurance/clinic-services API docs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:05:24 +03:30
hamed 5b1dfe9b40 feat: add national code to patient records and user entity, update related functionality 2026-06-22 19:44:17 +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 8ed119e12a fix: update active doctor appointment logic to use online_booking_enabled field 2026-06-22 09:47:06 +03:30
hamed 447ffc8862 fix: adjust active doctor appointment filter to default to true unless specified 2026-06-21 18:02:03 +03:30