Add AST JSON files for NotificationMobileController and manifest.json; create SmsServiceLookupOnlyTest for SMS service functionality

This commit is contained in:
hamed
2026-07-07 14:41:22 +03:30
parent 57d081246f
commit 4ee1524f31
50 changed files with 7945 additions and 2676 deletions
+169
View File
@@ -0,0 +1,169 @@
# اجبار ارسال همه پیامک‌ها از طریق Kavenegar VerifyLookup (حذف مسیر send.json خام)
## پروژه
`clinicpro` (Backend — دامنه `src/Sms`)
## زمینه
کاوه‌نگار در ایران ارسال پیامک خدماتی (سیستمی/غیرشخصی) را فقط از طریق **الگوهای تأییدشده** با
اندپوینت `verify/lookup.json` مجاز می‌داند. ارسال متن آزاد با `sms/send.json` برای این نوع پیام‌ها
**فیلتر/رد** می‌شود (اغلب بدون خطای صریح — پیام در صف می‌رود ولی تحویل نمی‌شود).
هدف: همه‌ی مسیرهای ارسال پیامک باید از `sendTemplate()` (یعنی `verify/lookup.json`) عبور کنند و
هیچ مسیری نباید به `send()` خام (`sms/send.json`) سقوط کند.
## مشکل / هدف
الان اکثر پیام‌های سیستمی درست از الگو استفاده می‌کنند، اما **سه مسیر** هنوز می‌توانند به
`send.json` خام سقوط کنند و پیام‌شان تحویل نشود:
1. **fallback در `SmsService::dispatchTemplate()`** — وقتی تگ، `kavenegar_template` یا `token_map`
نداشته باشد، به `dispatchAsync()` بدون `templateCode` می‌رود → `send()` خام.
2. **fallback در `SmsService::sendNow()`** — هر `SendSmsMessage` که `templateCode === null` باشد
با `send()` خام ارسال می‌شود.
3. **ارسال دستی/کاربر** (`SmsController`) — دو اکشن که مستقیم `dispatchAsync()` بدون الگو صدا می‌زنند:
- `sendCustom` (خط ~75) با `TAG_USER_TEMPLATE` → همیشه `send()` خام.
- `sendViaTemplate` (خط ~488) اگر `SmsTemplate.providerCode` خالی باشد → `templateCode=null``send()` خام.
نکته: همه‌ی تگ‌های سیستمی (`OTP, PAYMENT, CLINIC_INVITATION, PRE_REGISTRATION, NOTIFICATION_MOBILE,
SECRETARY, DOCTOR_APPOINTMENT, WELCOME`) در `SmsMessageTemplate::DEFAULTS` الگو و `token_map` دارند و
درست کار می‌کنند — تمرکز اصلی روی **بستن مسیر سقوط به send.json** است، نه بازنویسی الگوها.
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `src/Sms/Service/SmsService.php` | نقطه‌ی تصمیم `sendTemplate` در برابر `send` (خطوط ۴۹–۵۲ و ۷۸–۹۱) |
| `src/Sms/Provider/KavehNegarProvider.php` | `send()` = `sms/send.json`، `sendTemplate()` = `verify/lookup.json` |
| `src/Sms/Provider/SmsProviderInterface.php` | قرارداد دو متد `send` / `sendTemplate` |
| `src/Sms/Controller/SmsController.php` | اکشن‌های ارسال دستی (`sendCustom` ~۷۵، `sendViaTemplate` ~۴۸۸) |
| `src/Sms/Entity/SmsMessageTemplate.php` | `DEFAULTS` تگ‌های سیستمی + نگاشت token |
| `src/Sms/Entity/SmsLog.php` | ثابت‌های `TAG_*` |
| `docs/api/sms.md` | مستند API — طبق قانون پروژه باید هم‌زمان به‌روز شود |
## وضعیت فعلی
`src/Sms/Service/SmsService.php` — سقوط به مسیر خام:
```php
public function dispatchTemplate(string $tag, string $mobile, array $vars = [], string $provider = 'kavenegar'): void
{
$tpl = $this->messageTemplateRepo->findByTag($tag);
$kaveTemplate = $tpl?->getKavenegarTemplate()
?? (SmsMessageTemplate::DEFAULTS[$tag]['kavenegar_template'] ?? null);
$tokenMap = $tpl?->getTokenMap()
?: (SmsMessageTemplate::DEFAULTS[$tag]['token_map'] ?? []);
$message = $this->textResolver->resolve($tag, $vars);
if ($kaveTemplate === null || $tokenMap === []) {
$this->dispatchAsync($mobile, $message, $provider, tag: $tag); // ← send.json خام
return;
}
// ...
}
public function sendNow(SendSmsMessage $msg): bool
{
$provider = $this->resolveProvider($msg->provider);
$success = ($msg->templateCode !== null)
? $provider->sendTemplate($msg->mobile, $msg->templateCode, $msg->templateVars)
: $provider->send($msg->mobile, $msg->message); // ← send.json خام
// ...
}
```
`src/Sms/Controller/SmsController.php` — ارسال دستی بدون الگو (خط ~۷۵):
```php
$this->smsService->dispatchAsync($mobile, $message, $provider, tag: \App\Sms\Entity\SmsLog::TAG_USER_TEMPLATE);
```
## وظایف
### ۱. بستن سقوط در `dispatchTemplate()` — نبود الگو باید خطای صریح باشد، نه ارسال خام
وقتی تگی `kavenegar_template`/`token_map` ندارد، به‌جای ارسال با `send.json`، باید:
- یک خطای لاگ‌شده‌ی واضح ثبت شود (کدام تگ الگو ندارد) و
- پیام ارسال **نشود** (یا در صورت نیاز، یک `SmsLog` ناموفق با دلیل ثبت شود تا در پنل دیده شود).
```php
if ($kaveTemplate === null || $tokenMap === []) {
$this->logger->error(sprintf('SMS tag "%s" has no Kavenegar template/token_map; refusing raw send', $tag), [
'tag' => $tag, 'mobile' => $mobile,
]);
// اختیاری: ثبت SmsLog ناموفق برای مشاهده در پنل به‌جای سکوت کامل
return;
}
```
> `LoggerInterface` را به `SmsService` تزریق کن (constructor) اگر موجود نیست.
### ۲. بستن سقوط در `sendNow()` — `templateCode` نال یعنی خطا، نه send خام
پیامی که بدون `templateCode` به صف رسیده نباید با `send.json` برود. رفتار پیشنهادی: اگر
`templateCode === null` بود، ارسال را ناموفق در نظر بگیر و لاگ کن (به‌جای `send()`), و `SmsLog`
با `success=false` ثبت شود تا در گزارش‌ها دیده شود:
```php
public function sendNow(SendSmsMessage $msg): bool
{
$provider = $this->resolveProvider($msg->provider);
if ($msg->templateCode === null) {
$this->logger->error('SMS refused: no templateCode (lookup-only policy)', [
'mobile' => $msg->mobile, 'tag' => $msg->tag,
]);
$success = false;
} else {
$success = $provider->sendTemplate($msg->mobile, $msg->templateCode, $msg->templateVars);
}
$log = new SmsLog($msg->mobile, $msg->message, $provider->getName(), $success, $msg->tag);
if ($msg->templateUuid) $log->setTemplateUuid($msg->templateUuid);
$this->logRepo->save($log);
return $success;
}
```
> تصمیم معماری: اگر بخواهی «تک مسیر»، می‌توانی `send()` را از `SmsProviderInterface` و
> `KavehNegarProvider` کلاً حذف کنی. اگر می‌خواهی احتیاطی نگه‌ داری، حداقل هیچ فراخوان‌کننده‌ای
> نباید به آن برسد. در پرامپت اجرا تصمیم را صریح ثبت کن.
### ۳. اکشن‌های ارسال دستی `SmsController` — فقط با الگوی تأییدشده
- **`sendViaTemplate` (~خط ۴۸۸):** پیش از `dispatchAsync`، اگر `$template->getProviderCode()` خالی بود،
خطای ۴۲۲ برگردان (الگو کد کاوه‌نگار ندارد) و ارسال نکن — تا `templateCode=null` به صف نرود.
```php
if (!$template->getProviderCode()) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این تمپلیت کد VerifyLookup کاوه‌نگار ندارد و قابل ارسال نیست', 422);
}
```
- **`sendCustom` (~خط ۷۵، `TAG_USER_TEMPLATE`):** ارسال متن آزاد با `send.json` طبق سیاست جدید مجاز نیست.
دو گزینه (در پرامپت اجرا یکی را انتخاب کن):
1. این اکشن را به الگو-محور تبدیل کن (فقط از `sendViaTemplate` با الگوی تأییدشده استفاده شود) و
مسیر متن آزاد را حذف/۴۲۲ کن.
2. اگر کسب‌وکار به متن آزاد نیاز دارد، آن را پشت یک الگوی «آزاد» تأییدشده‌ی کاوه‌نگار ببر
(نیازمند الگوی مصوب) — نه `send.json`.
### ۴. تست‌ها + مستندات
- تست‌های `tests/` مرتبط با SMS را اجرا/به‌روز کن؛ یک تست اضافه کن که ثابت کند:
«تگ بدون الگو → هیچ فراخوان `send()` انجام نمی‌شود و `SmsLog` ناموفق ثبت می‌شود».
- `docs/api/sms.md` را به‌روز کن: قانون «فقط VerifyLookup»، رفتار جدید `sendCustom`/`sendViaTemplate`
(شرط `providerCode`)، و کد خطای ۴۲۲ جدید.
## نکات مهم
- الگوهای سیستمی موجود دست‌نخورده‌اند؛ فقط **مسیر سقوط** بسته می‌شود. رگرسیون OTP/پرداخت/دعوت را چک کن
(این‌ها الگو دارند و باید هنوز کار کنند).
- `token/token2/token3` فاصله را رد می‌کنند؛ مقدار دارای فاصله باید در `token10/token20` بنشیند —
این منطق در `KavehNegarProvider::forSlot()` هست، تغییرش نده.
- همه‌ی خروجی‌ها باید در `SmsLog` ثبت شوند (چه موفق چه ناموفق) تا در پنل SMS قابل رصد باشد؛ سکوت کامل ممنوع.
- `KAVENEGAR_API_KEY` فقط از env خوانده می‌شود؛ در محیط لوکال ممکن است خالی باشد — تست‌ها نباید به شبکه‌ی واقعی وابسته باشند (provider را mock کن).
- طبق الگوی پروژه: پاسخ‌ها با `$this->error()/success()`، کدهای خطا از `ErrorCodes`، تاریخ‌ها Unix timestamp.
+17 -21
View File
@@ -4,6 +4,11 @@
> **Provider:** همیشه `kavenegar` (پیش‌فرض و تنها گزینه فعال).
> All send operations are dispatched **asynchronously** via Symfony Messenger → Redis queue.
> **سیاست lookup-only (مهم):** همه‌ی پیامک‌ها فقط از طریق الگوی تأییدشده‌ی کاوه‌نگار
> (`verify/lookup.json`) ارسال می‌شوند. ارسال متن آزاد با `sms/send.json` **غیرفعال** است
> (در ایران برای پیام خدماتی فیلتر می‌شود). هر پیامی که الگو/کد VerifyLookup نداشته باشد
> ارسال **نمی‌شود**؛ در `SmsLog` با `success=false` ثبت و در لاگ برنامه خطا می‌خورد.
## Configuration
- **کلید API کاوه‌نگار فقط از متغیر محیطی `KAVENEGAR_API_KEY` خوانده می‌شود** — نه از دیتابیس و نه از پنل. در پنل ادمین فقط وضعیت read-only «تنظیم‌شده/نشده» نمایش داده می‌شود.
@@ -14,32 +19,22 @@
---
## POST `/api/v1/sms/send`
## POST `/api/v1/sms/send` — ⛔ غیرفعال (Deprecated)
Send a direct SMS message (free text).
ارسال متن آزاد **دیگر مجاز نیست** (سیاست lookup-only). این endpoint اکنون همیشه `422`
برمی‌گرداند و هیچ پیامکی ارسال نمی‌کند. برای ارسال دستی از
[`POST /api/v1/sms/send-template`](#post-apiv1smssend-template) با یک تمپلیت تأییدشده
که کد VerifyLookup کاوه‌نگار دارد استفاده کنید.
**Permission:** `ROLE_ADMIN`
### Request Body (`application/json`)
### Response `422` (همیشه)
```json
{
"mobile": "09123456789",
"message": "سلام، پیام آزمایشی",
"provider": "kavenegar"
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `mobile` | string | ✅ | Recipient mobile (`09XXXXXXXXX`) |
| `message` | string | ✅ | Message text |
| `provider` | string | ❌ | نادیده گرفته می‌شود؛ همیشه `kavenegar` استفاده می‌شود |
### Response `200`
```json
{
"success": true,
"data": { "message": "پیامک با موفقیت ارسال شد" }
"success": false,
"errors": [
{ "code": "ERR_VALIDATION_001", "message": "ارسال متن آزاد مجاز نیست؛ از تمپلیت تأییدشده (VerifyLookup) استفاده کنید" }
]
}
```
@@ -48,7 +43,7 @@ Send a direct SMS message (free text).
|------|------|-------------|
| `ERR_AUTH_001` | 401 | Missing token |
| `ERR_AUTH_006` | 403 | Not admin |
| `ERR_VALIDATION_001` | 422 | Invalid mobile format |
| `ERR_VALIDATION_001` | 422 | ارسال متن آزاد غیرفعال است (همیشه) |
---
@@ -93,6 +88,7 @@ Send an SMS using an approved template.
| `ERR_AUTH_006` | 403 | Not admin |
| `ERR_NOT_FOUND_001` | 404 | Template not found |
| `ERR_VALIDATION_001` | 422 | Template not approved |
| `ERR_VALIDATION_001` | 422 | تمپلیت کد VerifyLookup کاوه‌نگار ندارد و قابل ارسال نیست (`provider_code` خالی) |
---
+6 -1
View File
@@ -690,5 +690,10 @@
"688": "Community 688",
"689": "Community 689",
"690": "Community 690",
"691": "Community 691"
"691": "Community 691",
"692": "Community 692",
"693": "Community 693",
"694": "Community 694",
"695": "Community 695",
"696": "Community 696"
}
+171 -162
View File
@@ -1,16 +1,16 @@
# Graph Report - clinicpro (2026-07-02)
# Graph Report - clinicpro (2026-07-07)
## Corpus Check
- 692 files · ~493,676 words
- 708 files · ~520,035 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 8775 nodes · 12110 edges · 692 communities (559 shown, 133 thin omitted)
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 269 edges (avg confidence: 0.8)
- 8966 nodes · 12391 edges · 697 communities (560 shown, 137 thin omitted)
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 275 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `1804b421`
- Built from commit: `57d08124`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -692,6 +692,11 @@
- [[_COMMUNITY_Community 689|Community 689]]
- [[_COMMUNITY_Community 690|Community 690]]
- [[_COMMUNITY_Community 691|Community 691]]
- [[_COMMUNITY_Community 692|Community 692]]
- [[_COMMUNITY_Community 693|Community 693]]
- [[_COMMUNITY_Community 694|Community 694]]
- [[_COMMUNITY_Community 695|Community 695]]
- [[_COMMUNITY_Community 696|Community 696]]
## God Nodes (most connected - your core abstractions)
1. `BaseController` - 76 edges
@@ -710,33 +715,33 @@
assets/admin/components/ServiceTariffModal.tsx → assets/admin/lib/utils.ts
- `Pagination()` --calls--> `formatNumber()` [EXTRACTED]
assets/admin/components/ui/Pagination.tsx → assets/admin/lib/utils.ts
- `RepresentationSettlementPage()` --calls--> `formatRial()` [EXTRACTED]
assets/admin/pages/RepresentationSettlementPage.tsx → assets/admin/lib/utils.ts
- `SettlementsPage()` --calls--> `formatRial()` [EXTRACTED]
assets/admin/pages/SettlementsPage.tsx → assets/admin/lib/utils.ts
- `LogsPage()` --calls--> `formatDateTime()` [EXTRACTED]
assets/admin/pages/LogsPage.tsx → assets/admin/lib/utils.ts
- `SmsPage()` --calls--> `formatDateTime()` [EXTRACTED]
assets/admin/pages/SmsPage.tsx → assets/admin/lib/utils.ts
- `NewAppointmentModal()` --calls--> `useAuthStore` [EXTRACTED]
assets/admin/pages/AppointmentsPage.tsx → assets/admin/stores/authStore.ts
## Import Cycles
- None detected.
## Communities (692 total, 133 thin omitted)
## Communities (697 total, 137 thin omitted)
### Community 0 - "Community 0"
Cohesion: 0.05
Nodes (41): ALLOWED_ROLES, PrivateRoute(), PublicRoute(), RoleRoute(), queryClient, toastStyle, AddForm, addSchema (+33 more)
Nodes (38): ALLOWED_ROLES, PrivateRoute(), PublicRoute(), RoleRoute(), queryClient, toastStyle, DEGREE_OPTIONS, DoctorFormPage() (+30 more)
### Community 1 - "Community 1"
Cohesion: 0.03
Nodes (41): AddressData, AddrForm, addrSchema, AVATAR_COLORS, BookingMeta, CityOpt, DateOverrideData, DEFAULT_BOOKING_META (+33 more)
### Community 2 - "Community 2"
Cohesion: 0.11
Nodes (19): PaymentConfig, PaymentGatewayInfo, usePaymentConfig(), ApiResponse, ChargeForm, chargeSchema, EMPTY_LOGS, POST_VISIT_VARS (+11 more)
Cohesion: 0.10
Nodes (20): Ending — Logo Reveal (۷۵–۹۰s), Master Prompt — موشن‌گرافی تبلیغاتی Clinic Pro, OBJECTIVE, ROLE, Scene 1 — آشوب (۰–۸s), Scene 2 — تحول (۸–۱۴s), Scene 3 — داشبورد و ماژول‌ها (۱۴–۳۰s), Scene 4 — سفر بیمار (۳۰–۴۵s) (+12 more)
### Community 3 - "Community 3"
Cohesion: 0.06
Nodes (35): PaginatedResponse, STATUS_FILTERS, FILTERS, Breakdown, SOURCE_LABEL, Summary, LEVEL_FILTER_OPTIONS, LEVEL_META (+27 more)
Cohesion: 0.10
Nodes (20): `RepresentationActionController` — welcome به‌صورت inline (بدون تمپلت), `SmsMessageTemplate::DEFAULTS` (فاقد نام تمپلت و نگاشت token), `SmsService::sendNow` — انتخاب بین lookup و متن‌آزاد, الگوی فعلی همه‌ی call-siteها (به‌جز OTP) — متن‌آزاد، بدون `templateCode`, تبدیل همه‌ی پیامک‌های سیستمی به VerifyLookup کاوه‌نگار (تمپلت نام‌دار), تنها جای درست (OTP) — که باید الگوی بقیه شود, زمینه, فایل‌های مرتبط (+12 more)
### Community 4 - "Community 4"
Cohesion: 0.05
@@ -747,8 +752,8 @@ Cohesion: 0.07
Nodes (3): UserProfile, self, User
### Community 6 - "Community 6"
Cohesion: 0.12
Nodes (12): SettlementController, SettlementRepository, WalletTransactionRepository, Settlement, JsonResponse, Request, User, ManagerRegistry (+4 more)
Cohesion: 0.09
Nodes (15): SettlementController, SettlementRepository, WalletTransactionRepository, CommissionService, Settlement, JsonResponse, Request, User (+7 more)
### Community 7 - "Community 7"
Cohesion: 0.07
@@ -772,15 +777,15 @@ Nodes (4): UserActiveContextRepository, ManagerRegistry, User, UserActiveContext
### Community 12 - "Community 12"
Cohesion: 0.05
Nodes (44): Clinic Doctor Invitation API, DELETE `/api/v1/admin/clinic/invitation/{invUuid}`, Errors, Errors, Errors, Errors, Errors, Errors (+36 more)
Nodes (39): Clinic Doctor Invitation API, DELETE `/api/v1/admin/clinic/invitation/{invUuid}`, Errors, Errors, Errors, Errors, Errors, Errors (+31 more)
### Community 13 - "Community 13"
Cohesion: 0.06
Nodes (26): get, api, ApiError, getToken(), refreshOnce(), request(), { refreshMock, logoutMock }, replaceMock (+18 more)
Cohesion: 0.07
Nodes (25): get, api, ApiError, getToken(), refreshOnce(), request(), { refreshMock, logoutMock }, replaceMock (+17 more)
### Community 14 - "Community 14"
Cohesion: 0.15
Nodes (8): AppointmentSettingsController, Holiday, HolidayRepository, JsonResponse, Request, User, Doctor, ManagerRegistry
Cohesion: 0.10
Nodes (12): AppointmentSettingsController, DateOverride, Holiday, DateOverrideRepository, HolidayRepository, JsonResponse, Request, User (+4 more)
### Community 15 - "Community 15"
Cohesion: 0.25
@@ -800,7 +805,7 @@ Nodes (38): API, API, API, API, API, Route, Route, Route (+30 more)
### Community 19 - "Community 19"
Cohesion: 0.03
Nodes (56): ALL_STATUSES, CityForm, citySchema, ImportError, InsuranceForm, insuranceSchema, LogoUploadField(), ProvinceForm (+48 more)
Nodes (81): PaginatedResponse, CityForm, citySchema, ImportError, InsuranceForm, insuranceSchema, LogoUploadField(), ProvinceForm (+73 more)
### Community 20 - "Community 20"
Cohesion: 0.13
@@ -808,11 +813,11 @@ Nodes (3): AdminApiController, JsonResponse, Request
### Community 21 - "Community 21"
Cohesion: 0.04
Nodes (45): formatNumber(), ClinicAddress, ClinicDetailPage(), ClinicDoctorItem, ClinicInvitation, EditForm, editSchema, HUES_LIST (+37 more)
Nodes (59): PaymentConfig, PaymentGatewayInfo, usePaymentConfig(), ApiResponse, formatNumber(), formatRial(), ClinicDetailPage(), AdminCharts (+51 more)
### Community 22 - "Community 22"
Cohesion: 0.15
Nodes (9): RatingController, Like, LikeRepository, JsonResponse, Request, User, Comment, ManagerRegistry (+1 more)
Cohesion: 0.12
Nodes (10): RatingController, Like, CommentListNPlusOneTest, LikeRepository, JsonResponse, Request, User, Comment (+2 more)
### Community 23 - "Community 23"
Cohesion: 0.05
@@ -827,15 +832,15 @@ Cohesion: 0.05
Nodes (39): Appointment API, Error Responses, Errors, Errors, Errors, Errors, Errors, Errors (+31 more)
### Community 26 - "Community 26"
Cohesion: 0.13
Nodes (8): InsuranceController, EntityInsurancePricing, EntityInsurancePricingRepository, TenantInsuranceCleanupService, JsonResponse, Request, User, ManagerRegistry
Cohesion: 0.24
Nodes (4): InsuranceController, JsonResponse, Request, User
### Community 27 - "Community 27"
Cohesion: 0.05
Nodes (40): 55. 🟢 `GET` all tag, 56. 🟢 `GET` supplementary_insurance, 57. 🟢 `GET` categories list, 58. 🟢 `GET` all state, 59. 🟢 `GET` all city, 60. 🟢 `GET` all specially doctor, 61. 🔵 `POST` post, 62. 🟡 `PATCH` patch (+32 more)
### Community 28 - "Community 28"
Cohesion: 0.09
Cohesion: 0.08
Nodes (4): Appointment, Doctor, self, User
### Community 29 - "Community 29"
@@ -855,8 +860,8 @@ Cohesion: 0.06
Nodes (32): 10. Modal / Dialog, 11. Toast Notifications, 12. Empty States & Loading, 13. Page Header (هر صفحه), 14. تکنولوژی Stack, 15. Responsive Breakpoints, 16. Dark Mode (اختیاری — فاز دوم), 17. نمونه رنگ‌بندی صفحه داشبورد (+24 more)
### Community 33 - "Community 33"
Cohesion: 0.06
Nodes (27): CoverageRow, Draft, KIND, TenantInsurance, ServiceTariffModal(), TariffResponse, TariffRow, EMPTY_ITEMS (+19 more)
Cohesion: 0.07
Nodes (25): CoverageRow, Draft, KIND, TenantInsurance, ServiceTariffModal(), TariffResponse, TariffRow, EMPTY_ITEMS (+17 more)
### Community 34 - "Community 34"
Cohesion: 0.06
@@ -886,10 +891,6 @@ Nodes (5): DoctorAddress, City, Doctor, Province, self
Cohesion: 0.09
Nodes (4): User, PasswordAuthenticatedUserInterface, self, UserInterface
### Community 41 - "Community 41"
Cohesion: 0.21
Nodes (5): DateOverrideOwnershipTest, DateOverride, DateOverrideRepository, Doctor, ManagerRegistry
### Community 42 - "Community 42"
Cohesion: 0.07
Nodes (29): API calls (با `useMutation`):, API موجود:, Frontend موجود:, `PATCH /api/v1/clinic/{clinicUuid}/doctor/{doctorUuid}/deactivate`, `PATCH /api/v1/clinic/{clinicUuid}/doctor/{doctorUuid}/reactivate`, UX نکات:, آپدیت `GET /api/v1/clinic/doctor-list/{clinicUuid}` (فیلدهای جدید), آپدیت interface: (+21 more)
@@ -935,8 +936,8 @@ Cohesion: 0.07
Nodes (26): الزامات UI, باگ‌فیکس صفحه نوبت‌ها, باگ ۱ — کرش تقویم, باگ ۲ — روز هفته در DateNavigator, باگ ۳ — پیام «slot نیست», باگ ۴ — نوبت جدید: نام اجباری + find-or-create patient, باگ ۵ — patient_mobile نشان می‌دهد موبایل پزشک, باگ ۶ — نوبت‌های رزرو شده در نمایش زمانبندی (+18 more)
### Community 53 - "Community 53"
Cohesion: 0.10
Nodes (13): AppLogRepository, ClaimItemRepository, ClinicStaffRepository, PreRegistrationRepository, SessionServiceRepository, ServiceEntityRepository, ManagerRegistry, ManagerRegistry (+5 more)
Cohesion: 0.08
Nodes (15): AppLogRepository, PaymentLog, ClaimItemRepository, PaymentLogRepository, PreRegistrationRepository, SiteConfigRepository, SmsTemplateRepository, ServiceEntityRepository (+7 more)
### Community 54 - "Community 54"
Cohesion: 0.10
@@ -979,8 +980,8 @@ Cohesion: 0.22
Nodes (5): AuthController, RateLimiterFactory, JsonResponse, Request, User
### Community 64 - "Community 64"
Cohesion: 0.19
Nodes (7): SmsWalletController, SmsSettingsRepository, SmsSettings, JsonResponse, Request, User, ManagerRegistry
Cohesion: 0.29
Nodes (4): SmsWalletController, JsonResponse, Request, User
### Community 65 - "Community 65"
Cohesion: 0.08
@@ -1007,8 +1008,8 @@ Cohesion: 0.15
Nodes (12): رفع بهم‌ریختگی کامل پنل ادمین روی iPhone 8 (Safari/Chrome iOS), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+4 more)
### Community 71 - "Community 71"
Cohesion: 0.07
Nodes (21): Contract, InsuranceOption, KIND_LABEL, Claim, ClaimItem, DebtRow, InsuranceOption, KIND_LABEL (+13 more)
Cohesion: 0.09
Nodes (21): AddForm, addSchema, ClinicsPage(), HUES_LIST, EMPTY, PreRegistration, STATUS_META, STATUS_TABS (+13 more)
### Community 72 - "Community 72"
Cohesion: 0.09
@@ -1040,7 +1041,7 @@ Nodes (3): SubscriptionPlan, Collection, self
### Community 81 - "Community 81"
Cohesion: 0.09
Nodes (22): dependencies, @ckeditor/ckeditor5-build-classic, @ckeditor/ckeditor5-react, @fontsource/vazirmatn, @heroicons/react, @hookform/resolvers, jalaali-js, leaflet (+14 more)
Nodes (23): dependencies, @ckeditor/ckeditor5-build-classic, @ckeditor/ckeditor5-react, @fontsource/vazirmatn, @heroicons/react, @hookform/resolvers, jalaali-js, leaflet (+15 more)
### Community 83 - "Community 83"
Cohesion: 0.09
@@ -1055,8 +1056,8 @@ Cohesion: 0.07
Nodes (30): devDependencies, @babel/core, @babel/preset-env, @babel/preset-react, @babel/preset-typescript, core-js, @csstools/postcss-oklab-function, @hotwired/stimulus (+22 more)
### Community 86 - "Community 86"
Cohesion: 0.07
Nodes (13): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, ServiceItemStaffOwnershipTest, KernelBrowser, CommentPaginationTest, SettlementListPaginationTest (+5 more)
Cohesion: 0.06
Nodes (15): AppointmentExpiryServiceTest, LowTierFixesTest, SendCodeMobileRateLimitTest, ClaimsListPaginationTest, ServiceItemStaffOwnershipTest, UniqueConstraintsTest, EntityManagerInterface, KernelBrowser (+7 more)
### Community 87 - "Community 87"
Cohesion: 0.10
@@ -1403,8 +1404,8 @@ Cohesion: 0.35
Nodes (5): PatientController, JsonResponse, PatientSession, Request, User
### Community 176 - "Community 176"
Cohesion: 0.09
Nodes (13): SecretaryController, SmsLogRepository, OtpService, PaymentManager, SmsLog, Payment, PaymentInitResult, PaymentRefundResult (+5 more)
Cohesion: 0.25
Nodes (4): PaymentManager, Payment, PaymentInitResult, PaymentRefundResult
### Community 177 - "Community 177"
Cohesion: 0.36
@@ -1511,16 +1512,16 @@ Cohesion: 0.21
Nodes (6): AuthenticationException, ExceptionSubscriber, SecurityHeadersSubscriber, EventSubscriberInterface, ExceptionEvent, ResponseEvent
### Community 205 - "Community 205"
Cohesion: 0.07
Nodes (20): Command, CancelExpiredAppointmentsCommand, CreateAdminCommand, PruneLogsCommand, SeedCategoriesCommand, SeedSmsMessageTemplatesCommand, SmsMessageTemplateRepository, SmsTextResolver (+12 more)
Cohesion: 0.10
Nodes (16): Command, CancelExpiredAppointmentsCommand, CreateAdminCommand, PruneLogsCommand, SeedCategoriesCommand, SeedSmsMessageTemplatesCommand, InputInterface, OutputInterface (+8 more)
### Community 206 - "Community 206"
Cohesion: 0.21
Nodes (5): MellatGateway, SoapClient, PaymentInitResult, PaymentRefundResult, PaymentVerifyResult
Cohesion: 0.08
Nodes (14): MellatGateway, MockGateway, SepGateway, SoapClient, PaymentGatewayInterface, PaymentInitResult, PaymentRefundResult, PaymentVerifyResult (+6 more)
### Community 207 - "Community 207"
Cohesion: 0.15
Nodes (13): ۲. مدل داده و موجودیت‌ها, ۲.۱ کاربر (User), ۲.۱۰ پرداخت (Payment Types), ۲.۱۰.۱ پرداخت نوبت, ۲.۱۰.۲ پرداخت اشتراک, ۲.۱۱ پروفایل بیمار (Profile), ۲.۱۲ وبلاگ (Blog), ۲.۳ دکتر (Doctor) (+5 more)
Cohesion: 0.07
Nodes (30): ۲. مدل داده و موجودیت‌ها, ۲.۱ کاربر (User), ۲.۱۰ پرداخت (Payment Types), ۲.۱۰.۱ پرداخت نوبت, ۲.۱۰.۲ پرداخت اشتراک, ۲.۱۱ پروفایل بیمار (Profile), ۲.۱۲ وبلاگ (Blog), ۲.۱۳ نظرات، لایک و امتیازدهی (+22 more)
### Community 208 - "Community 208"
Cohesion: 0.23
@@ -1583,12 +1584,12 @@ Cohesion: 0.15
Nodes (12): Callback Mellat, frontend_address, Idempotency, درگاه‌های واقعی پروژه (از کد Drupal), شرط ایجاد پرداخت, قیمت از Config (نه Request), مجوزها, نکات پیاده‌سازی — تسک ۱۵: ماژول پرداخت (+4 more)
### Community 227 - "Community 227"
Cohesion: 0.17
Nodes (7): HealthController, SiteConfigController, EntityManagerInterface, JsonResponse, Request, User, JsonResponse
Cohesion: 0.36
Nodes (4): SiteConfigController, JsonResponse, Request, User
### Community 228 - "Community 228"
Cohesion: 0.04
Nodes (47): Bulk import / export, DELETE `/api/v1/admin/insurance/{id}`, DELETE `/api/v1/billing/tenant-insurances/{uuid}`, DELETE `/api/v1/insurance/{id}`, EntityInsurancePricing — قیمت‌گذاری ویزیت بر اساس بیمه, Errors, Errors, Errors (+39 more)
Cohesion: 0.20
Nodes (10): Bulk import / export, DELETE `/api/v1/admin/insurance/{id}`, DELETE `/api/v1/insurance/{id}`, EntityInsurancePricing — قیمت‌گذاری ویزیت بر اساس بیمه, GET `/api/v1/insurance/{id}`, Insurance API, Response `200`, Response `200` (+2 more)
### Community 229 - "Community 229"
Cohesion: 0.15
@@ -1647,12 +1648,12 @@ Cohesion: 0.26
Nodes (4): PatientRecordRepository, ManagerRegistry, PatientRecord, User
### Community 244 - "Community 244"
Cohesion: 0.29
Nodes (3): SubscriptionService, ClinicSubscription, Payment
Cohesion: 0.28
Nodes (4): SubscriptionService, ClinicSubscription, Payment, SubscriptionPlan
### Community 245 - "Community 245"
Cohesion: 0.13
Nodes (12): AdminUserDetail, AVATAR_COLORS, EditForm, editSchema, GENDER_LABELS, getPrimaryRole(), MARITAL_LABELS, MEDICAL_SECTIONS (+4 more)
Cohesion: 0.05
Nodes (37): formatDate(), formatDateTime(), ALL_STATUSES, AppointmentDetailPage(), timeOf(), Claim, ClaimItem, DebtRow (+29 more)
### Community 246 - "Community 246"
Cohesion: 0.17
@@ -1691,8 +1692,8 @@ Cohesion: 0.18
Nodes (11): 1. 🔵 `POST` refresh token, 1. احراز هویت (Authentication), 2. 🟢 `GET` X-CSRF-Token, 3. 🟢 `GET` user info 🆕, Request Body, بخش دوم — مستند کامل API, خطاهای عمومی, فهرست مطالب (+3 more)
### Community 255 - "Community 255"
Cohesion: 0.18
Nodes (11): GET /api/v1/doctors/{id}, GET /api/v1/doctors/{id}/insurances, GET /api/v1/representations/{id}, GET /oauth/userinfo — اطلاعات کاربر (سازگار با دروپال), POST /api/v1/representations/{id}/bank-accounts, POST /oauth/token — تجدید توکن (Refresh), POST /oauth/token — ورود به سیستم, Task-02: احراز هویت (Authentication) (+3 more)
Cohesion: 0.11
Nodes (19): GET /api/v1/appointment-settings/slots — دریافت اسلات‌های خالی, GET /api/v1/categories/cities — شهرها, GET /api/v1/categories/states — استان‌ها, GET /api/v1/doctors/{id}, GET /api/v1/doctors/{id}/insurances, GET /api/v1/representations/{id}, GET /oauth/userinfo — اطلاعات کاربر (سازگار با دروپال), POST /api/v1/appointment-settings/holidays — ثبت تعطیلی (فقط ادمین) (+11 more)
### Community 256 - "Community 256"
Cohesion: 0.29
@@ -1727,8 +1728,8 @@ Cohesion: 0.33
Nodes (4): RepresentationRepository, ManagerRegistry, Representation, User
### Community 265 - "Community 265"
Cohesion: 0.08
Nodes (27): calcFinalPrice(), EMPTY_RECORDS, EMPTY_SESSIONS, fileNumber(), getPatientName(), getPatientPhone(), InsurancePricing, MyPatientsPageInner() (+19 more)
Cohesion: 0.06
Nodes (33): Contract, InsuranceOption, KIND_LABEL, calcFinalPrice(), EMPTY_RECORDS, EMPTY_SESSIONS, fileNumber(), getPatientName() (+25 more)
### Community 266 - "Community 266"
Cohesion: 0.33
@@ -1775,8 +1776,8 @@ Cohesion: 0.20
Nodes (9): Architecture Audit — ClinicPro Symfony 7 Migration, Architecture Score (بعد از اصلاحات), Architecture Violations, Executive Summary, Final Verdict (بعد از اصلاحات), Missing Requirements (کامل), اصلاحات اعمال‌شده (بعد از Audit), دلیل تصمیم (+1 more)
### Community 277 - "Community 277"
Cohesion: 0.20
Nodes (9): بخش اول — مستند محصول (PRD), فهرست کلی, کلینیک پرو — مستند جامع فنی و API, ۵. فهرست مشکلات شناسایی‌شده و اصلاحات لازم, ۶. پیوست, ۶.۱ دیاگرام وضعیت نوبت, ۶.۲ جریان کمیسیون, ۶.۳ بررسی محدودیت منشی (+1 more)
Cohesion: 0.08
Nodes (23): GET /api/v1/sms/balance — موجودی حساب پیامک, POST /api/v1/sms/queue — افزودن به صف, بخش اول — مستند محصول (PRD), ثبت‌نام دکتر — از طریق نماینده, ثبت‌نام دکتر — از طریق کلینیک, ثبت‌نام دکتر — مستقل, فهرست کلی, کلینیک پرو — مستند جامع فنی و API (+15 more)
### Community 279 - "Community 279"
Cohesion: 0.20
@@ -1867,8 +1868,8 @@ Cohesion: 0.42
Nodes (3): PreRegistrationController, JsonResponse, Request
### Community 301 - "Community 301"
Cohesion: 0.17
Nodes (7): FormValues, schema, SectionDef, SectionId, SECTIONS, Settings, TaxHistoryRow
Cohesion: 0.09
Nodes (20): FreeVisitPrice(), Pricing, rialToToman(), tomanToRial(), IbanItem, RepMe, RepresentationSettlementPage(), RepSummary (+12 more)
### Community 302 - "Community 302"
Cohesion: 0.12
@@ -1879,8 +1880,8 @@ Cohesion: 0.12
Nodes (16): آماده‌سازی پروژه ClinicPro برای دیپلوی روی Coolify با Docker Compose, زمینه, فایل‌های مرتبط, نکات مهم (محدودیت‌ها و edge caseها), هدف, وظایف, ۱. ساخت `Dockerfile` چندمرحله‌ای, ۱۰. ساخت راهنمای `docs/deploy/coolify.md` (+8 more)
### Community 304 - "Community 304"
Cohesion: 0.22
Nodes (9): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 36. 🟢 `GET` get my rate, 6. کلینیک (Clinic), هدرهای اضافی, پارامترهای Query, پاسخ‌ها, پاسخ‌ها (+1 more)
Cohesion: 0.04
Nodes (56): 29. 🟢 `GET` clinic list 🆕, 30. 🟢 `GET` get 🆕, 32. 🔵 `POST` post, 33. 🔵 `POST` image_clinic, 34. 🔵 `POST` image logo, 35. 🟡 `PATCH` patch, 36. 🟢 `GET` get my rate, 37. 🔵 `POST` post (+48 more)
### Community 306 - "Community 306"
Cohesion: 0.07
@@ -1923,7 +1924,7 @@ Cohesion: 0.39
Nodes (4): SubscriptionPeriodRepository, ManagerRegistry, SubscriptionPeriod, SubscriptionPlan
### Community 316 - "Community 316"
Cohesion: 0.36
Cohesion: 0.31
Nodes (3): SubscriptionPlanRepository, ManagerRegistry, SubscriptionPlan
### Community 319 - "Community 319"
@@ -2039,12 +2040,12 @@ Cohesion: 0.43
Nodes (3): SmsWalletRepository, ManagerRegistry, SmsWallet
### Community 351 - "Community 351"
Cohesion: 0.18
Nodes (5): MockGateway, PaymentGatewayInterface, PaymentInitResult, PaymentRefundResult, PaymentVerifyResult
Cohesion: 0.11
Nodes (18): `Appointment.php`, `PaymentController::pay` (نوبت را اصلاً چک نمی‌کند), `PaymentController::startOrderPayment` (فقط status، بدون چک زمان), `renderPaymentResult` labels (برچسب `expired` ندارد), الگوی موجود انقضا (`AppointmentExpiryService`) — برای مرجع, زمینه, فایل‌های مرتبط, مشکل / هدف (+10 more)
### Community 352 - "Community 352"
Cohesion: 0.21
Nodes (4): SepGateway, PaymentInitResult, PaymentRefundResult, PaymentVerifyResult
Cohesion: 0.15
Nodes (5): EntityInsurancePricing, TenantInsuranceCleanupTest, EntityInsurancePricingRepository, TenantInsuranceCleanupService, ManagerRegistry
### Community 354 - "Community 354"
Cohesion: 0.36
@@ -2115,8 +2116,8 @@ Cohesion: 0.29
Nodes (7): Authentication, Authorization, Input Validation, Logging Security, Rate Limiting, Secrets Management, ۷. تحلیل امنیت
### Community 371 - "Community 371"
Cohesion: 0.15
Nodes (6): UniqueConstraintsTest, FinancialBreakdown, FinancialBreakdownRepository, FinancialBreakdownIntegrityTest, ManagerRegistry, Payment
Cohesion: 0.39
Nodes (4): FinancialBreakdown, FinancialBreakdownRepository, ManagerRegistry, Payment
### Community 372 - "Community 372"
Cohesion: 0.11
@@ -2220,20 +2221,28 @@ Nodes (7): initiate(), refund(), reverse(), verify(), PaymentInitResult, Payment
### Community 399 - "Community 399"
Cohesion: 0.23
Nodes (5): AbstractMigration, Schema, Version20260609130407, Schema, Version20260614182950
Nodes (5): AbstractMigration, Schema, Version20260609130407, Schema, Version20260611075829
### Community 401 - "Community 401"
Cohesion: 0.12
Nodes (15): `lib/utils.ts`, `SettingsPage.tsx` (ورودی‌ها ریال ذخیره می‌شوند), زمینه, فایل‌های مرتبط, مشکل / هدف, نمونهٔ نمایش (Subscription), نکات مهم, واحد پول = تومان در پنل ادمین (نمایش ÷۱۰ / ورودی ×۱۰) — ذخیره و درگاه ریال می‌ماند (+7 more)
### Community 405 - "Community 405"
Cohesion: 0.11
Nodes (17): بازطراحی معماری پرداخت — سرویس‌محور، امن، توسعه‌پذیر (Backend), تست دستی (ddev، در حالت `payment_test_mode=1`), خروجی نهایی (طبق spec — در گزارش اجرا ارائه شود), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی (+9 more)
### Community 407 - "Community 407"
Cohesion: 0.36
Nodes (3): SmsService, SendSmsMessage, SmsProviderInterface
Cohesion: 0.16
Nodes (11): KavehNegarProvider, MessageBusInterface, MockObject, SmsService, SmsServiceLookupOnlyTest, SmsLogRepository, SmsMessageTemplateRepository, SmsService (+3 more)
### Community 418 - "Community 418"
Cohesion: 0.17
Nodes (11): دیپلوی ClinicPro روی Coolify (Docker Compose), رفع اشکال, مراحل دیپلوی, معماری دیپلوی, نکات عملیاتی, چند دامنه فرانت‌اند (مهم), ۱. ساخت Resource در Coolify, ۲. اختصاص دامنه (+3 more)
### Community 424 - "Community 424"
Cohesion: 0.33
Nodes (5): SecretaryController, DoctorSecretary, JsonResponse, Request, User
### Community 432 - "Community 432"
Cohesion: 0.12
Nodes (16): backend (آماده — فقط annotation/doc ناقص), افزودن فیلد «تاریخ شروع فعالیت» (سال تجربه) در پنل ادمین با تقویم شمسی, زمینه, فایل‌های مرتبط, فرم ساخت پزشک — `DoctorFormPage.tsx` (خط ~282), فرم ویرایش پزشک — `DoctorDetailPage.tsx` (وضعیت فعلی، بدون فیلد تاریخ), مشکل / هدف, نکات مهم (+8 more)
@@ -2248,19 +2257,19 @@ Nodes (18): [F10] راهنمای کهنه در `CLAUDE.md`: endpoint `categorys/
### Community 452 - "Community 452"
Cohesion: 0.06
Nodes (45): FreeVisitPrice(), Pricing, cn(), formatDate(), formatDateTime(), formatRial(), iranMobileOptionalSchema, iranMobileSchema (+37 more)
Nodes (31): cn(), iranMobileOptionalSchema, iranMobileSchema, isValidIranMobile(), maskMobile(), sanitizeMobileInput(), toDate(), toEnglishDigits() (+23 more)
### Community 456 - "Community 456"
Cohesion: 0.40
Nodes (5): 32. 🔵 `POST` post, Request Body, مثال Request, هدرهای اضافی, پاسخ‌ها
Cohesion: 0.15
Nodes (12): رفع افتادن مودال‌ها به پایین صفحه (portal برای همه مودال‌ها), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+4 more)
### Community 458 - "Community 458"
Cohesion: 0.47
Nodes (6): formatPersianDate(), gToJ(), jFirstDayOfWeek(), PersianDateInput(), todayGregorian(), toPersianNums()
### Community 459 - "Community 459"
Cohesion: 0.33
Nodes (6): API موجود (نیاز به تغییر ندارند), اپیک‌ها, اپیک ۳ — منشی (Secretary) — تکمیل, تغییرات مورد نیاز, توضیح, نیازمندی‌های کارکردی
Cohesion: 0.40
Nodes (5): API موجود (نیاز به تغییر ندارند), اپیک ۳ — منشی (Secretary) — تکمیل, تغییرات مورد نیاز, توضیح, نیازمندی‌های کارکردی
### Community 460 - "Community 460"
Cohesion: 0.33
@@ -2395,8 +2404,8 @@ Cohesion: 0.40
Nodes (5): addMinutes(), calcSlotCount(), hasOverlap(), parseMinutes(), SessionEditor()
### Community 500 - "Community 500"
Cohesion: 0.40
Nodes (5): API موجود (نیاز به endpoint جدید ندارد), اپیک ۷ — داشبورد هوشمند (Smart Dashboard), تغییر مورد نیاز, توضیح, نیازمندی‌های کارکردی
Cohesion: 0.33
Nodes (6): API موجود (نیاز به endpoint جدید ندارد), اپیک‌ها, اپیک ۷ — داشبورد هوشمند (Smart Dashboard), تغییر مورد نیاز, توضیح, نیازمندی‌های کارکردی
### Community 501 - "Community 501"
Cohesion: 0.40
@@ -2471,13 +2480,17 @@ Cohesion: 0.40
Nodes (4): ساختار فایل‌ها, معماری — تسک ۱۶: ماژول داشبورد دکتر, نمودار جریان, کوئری درآمد سالانه (بر اساس ماه‌های شمسی)
### Community 523 - "Community 523"
Cohesion: 0.43
Nodes (3): SmsTemplateRepository, SmsTemplate, ManagerRegistry
Cohesion: 0.15
Nodes (12): افزودن اسم سایت شهر به پیامک کد تأیید (OTP) — به‌صورت اختیاری, زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+4 more)
### Community 524 - "Community 524"
Cohesion: 0.12
Nodes (16): دیپلوی ClinicPro (Symfony) روی لیارا با پلتفرم PHP (بدون داکر), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی (کد واقعی), وظایف, پروژه (+8 more)
### Community 525 - "Community 525"
Cohesion: 0.15
Nodes (12): ارسال پیامک OTP فقط از طریق Kavenegar VerifyLookup (پترن), اسپک Kavenegar VerifyLookup (از داکیومنت رسمی), زمینه, فایل‌های مرتبط, نکات مهم, وضعیت فعلی, وظایف, پروژه (+4 more)
### Community 526 - "Community 526"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/user/register`, Request Body, Response `201`
@@ -2519,8 +2532,8 @@ Cohesion: 0.12
Nodes (15): `MellatGateway.php` — ثابت‌ها و مسیر prod فعلی (POST خام), آنچه نباید تغییر کند, بردن درگاه ملت روی محیط واقعی (production) با SOAP native, زمینه, فایل‌های مرتبط, نکات مهم (چک‌لیست عملیاتی prod), وضعیت فعلی, وظایف (+7 more)
### Community 537 - "Community 537"
Cohesion: 0.40
Nodes (3): Props, StatTone, TONE
Cohesion: 0.15
Nodes (12): اجبار ارسال همه پیامک‌ها از طریق Kavenegar VerifyLookup (حذف مسیر send.json خام), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+4 more)
### Community 539 - "Community 539"
Cohesion: 0.67
@@ -2580,7 +2593,7 @@ Nodes (4): Errors, GET `/api/v1/representation/clinics`, Query Parameters, Respo
### Community 555 - "Community 555"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/sms/send`, Request Body (`application/json`), Response `200`
Nodes (4): Errors, POST `/api/v1/sms/send-template`, Request Body (`application/json`), Response `200`
### Community 556 - "Community 556"
Cohesion: 0.50
@@ -2595,8 +2608,8 @@ Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/admin/sms/template/{uuid}/reject`, Request Body, Response `200`
### Community 559 - "Community 559"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/sms/send-template`, Request Body (`application/json`), Response `200`
Cohesion: 0.25
Nodes (4): SmsMessageTemplateRepository, SmsTextResolver, SmsMessageTemplate, ManagerRegistry
### Community 560 - "Community 560"
Cohesion: 0.50
@@ -2683,8 +2696,8 @@ Cohesion: 0.40
Nodes (5): Errors, PATCH `/api/v1/clinic-pro/doctor-address/{id}`, Path Parameters, Request Body, Response `200`
### Community 597 - "Community 597"
Cohesion: 0.20
Nodes (10): Configuration, DELETE `/api/v1/sms/template/{uuid}`, Errors, Errors, GET `/api/v1/admin/sms/templates`, GET `/api/v1/sms/template/{uuid}`, Response `200`, Response `200` (+2 more)
Cohesion: 0.29
Nodes (7): Configuration, Errors, GET `/api/v1/admin/sms/templates`, GET `/api/v1/sms/template/{uuid}`, Response `200`, Response `200`, SMS API
### Community 599 - "Community 599"
Cohesion: 0.67
@@ -2694,10 +2707,6 @@ Nodes (3): Errors, POST `/api/v1/sms/template/{uuid}/submit`, Response `200`
Cohesion: 0.67
Nodes (3): POST `/api/v1/admin/sms/template/{uuid}/approve`, Request Body (`application/json`), Response `200`
### Community 602 - "Community 602"
Cohesion: 0.50
Nodes (4): 44. 🟢 `GET` list comment, هدرهای اضافی, پارامترهای Query, پاسخ‌ها
### Community 603 - "Community 603"
Cohesion: 0.67
Nodes (3): Anti-Pattern هایی که مشاهده می‌شوند, Pattern هایی که استفاده شده‌اند, ۳. تحلیل Design Patterns
@@ -2711,24 +2720,28 @@ Cohesion: 0.67
Nodes (3): نقاط ضعف, نقاط قوت, ۶. تحلیل API Design
### Community 606 - "Community 606"
Cohesion: 0.25
Nodes (8): ثبت‌نام دکتر — از طریق نماینده, ثبت‌نام دکتر — از طریق کلینیک, ثبت‌نام دکتر — مستقل, ۱. معرفی محصول, ۱.۱ نقش‌های سیستم, ۱.۲ فلوهای عملیاتی اصلی, ۱.۳ پلن‌های اشتراک, ۱.۴ سیستم پیامک
Cohesion: 0.39
Nodes (3): ClinicStaffRepository, ClinicStaff, ManagerRegistry
### Community 607 - "Community 607"
Cohesion: 0.35
Nodes (5): RepresentationController, JsonResponse, Representation, Request, User
### Community 608 - "Community 608"
Cohesion: 0.43
Nodes (3): SmsLogRepository, SmsLog, ManagerRegistry
### Community 612 - "Community 612"
Cohesion: 0.67
Nodes (3): بک‌اند, فرانت‌اند, وضعیت فعلی کد (مهم — قبل از تغییر بخوان)
### Community 618 - "Community 618"
Cohesion: 0.12
Nodes (6): ServiceItemDeleteCleanupTest, ServiceCoverageNPlusOneTest, TenantInsuranceCleanupTest, TenantServiceCoverageRepository, ManagerRegistry, TenantServiceCoverage
Cohesion: 0.16
Nodes (5): ServiceItemDeleteCleanupTest, ServiceCoverageNPlusOneTest, TenantServiceCoverageRepository, ManagerRegistry, TenantServiceCoverage
### Community 625 - "Community 625"
Cohesion: 0.50
Nodes (4): 45. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخ‌ها
Cohesion: 0.48
Nodes (3): SmsSettingsRepository, SmsSettings, ManagerRegistry
### Community 631 - "Community 631"
Cohesion: 0.50
@@ -2743,12 +2756,8 @@ Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/doctor`, Request Body (`application/json`), Response `201`
### Community 634 - "Community 634"
Cohesion: 0.25
Nodes (8): ۲.۲ انواع دسته‌بندی (Category Types), ۲.۲.۱ تگ (Tag), ۲.۲.۲ استان (State), ۲.۲.۳ شهر (City), ۲.۲.۴ بیمه پایه (Basic Insurance), ۲.۲.۵ بیمه مکمل (Supplementary Insurance), ۲.۲.۶ تخصص دکتر (Doctor Specialty), ۲.۲.۷ خدمات دکتر (Doctor Services)
### Community 635 - "Community 635"
Cohesion: 0.50
Nodes (4): 46. 🟡 `PATCH` patch, مثال Request, هدرهای اضافی, پاسخ‌ها
Cohesion: 0.47
Nodes (3): ImageCropModalProps, createImage(), getCroppedImage()
### Community 636 - "Community 636"
Cohesion: 0.50
@@ -2758,10 +2767,6 @@ Nodes (4): Errors, GET `/api/v1/doctor/{uuid}`, Path Parameters, Response `200`
Cohesion: 0.50
Nodes (4): Errors, POST `/file/upload/clinic_pro/doctor/field_image`, Request, Response `200`
### Community 639 - "Community 639"
Cohesion: 0.33
Nodes (6): GET /api/v1/sms/balance — موجودی حساب پیامک, POST /api/v1/sms/queue — افزودن به صف, ۴. سیستم حساب و صف پیامک, ۴.۱ حساب پیامک (SMS Account), ۴.۲ صف پیامک (SMS Queue), ۴.۳ API پیامک
### Community 640 - "Community 640"
Cohesion: 0.50
Nodes (4): Errors, POST `/api/v1/clinic-pro/doctor-address`, Request Body, Response `201`
@@ -2770,10 +2775,6 @@ Nodes (4): Errors, POST `/api/v1/clinic-pro/doctor-address`, Request Body, Respo
Cohesion: 0.15
Nodes (11): emptyFeatures(), FEATURE_KEYS, FEATURE_LABELS, PeriodForm, periodSchema, PLAN_DISPLAY, PlanForm, planSchema (+3 more)
### Community 646 - "Community 646"
Cohesion: 0.40
Nodes (5): 35. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 647 - "Community 647"
Cohesion: 0.14
Nodes (13): اصلاح فیلتر شهر/استان در لیست عمومی پزشکان (`GET /api/v1/doctors`), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+5 more)
@@ -2786,10 +2787,6 @@ Nodes (5): 41. 🟡 `PATCH` patch, Request Body, مثال Request, هدرهای
Cohesion: 0.14
Nodes (13): زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی (flow جدا در SmsWallet — باید حذف شود), وظایف, پروژه, یکسان‌سازی کامل پرداخت به یک Flow واحد + entry ریدایرکت خالص (Backend + Admin) (+5 more)
### Community 650 - "Community 650"
Cohesion: 0.67
Nodes (3): 34. 🔵 `POST` image logo, هدرهای اضافی, پاسخ‌ها
### Community 651 - "Community 651"
Cohesion: 0.14
Nodes (13): زمینه, فایل‌های مرتبط, قیمت هر پیامک در تنظیمات + رفع تنظیمات ارسال پیامک کیف‌پول, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+5 more)
@@ -2798,10 +2795,6 @@ Nodes (13): زمینه, فایل‌های مرتبط, قیمت هر پیامک
Cohesion: 0.50
Nodes (4): Errors, Path Parameters, POST `/api/v1/clinic-pro/doctor-address/from-clinic/{clinicUuid}`, Response `201`
### Community 653 - "Community 653"
Cohesion: 0.67
Nodes (3): 42. 🔴 `DELETE` delete, هدرهای اضافی, پاسخ‌ها
### Community 654 - "Community 654"
Cohesion: 0.35
Nodes (6): DashboardController, Clinic, DoctorSecretary, JsonResponse, Request, User
@@ -2811,8 +2804,8 @@ Cohesion: 0.38
Nodes (5): MyAppointmentsController, Doctor, JsonResponse, Request, User
### Community 656 - "Community 656"
Cohesion: 0.40
Nodes (5): ۲.۸ تنظیمات نوبت (Appointment Settings), ۲.۸.۱ برنامه هفتگی (Weekly Schedule), ۲.۸.۲ تعطیلات (Holidays), ۲.۸.۳ لغو تعطیل (Date Override), ۲.۸.۴ الگوریتم محاسبه اسلات‌های خالی
Cohesion: 0.53
Nodes (3): SessionServiceRepository, ManagerRegistry, SessionService
### Community 657 - "Community 657"
Cohesion: 0.20
@@ -2826,10 +2819,6 @@ Nodes (3): GET `/api/v1/clinic-pro/doctor-addresses/{doctorId}`, Path Parameters
Cohesion: 0.31
Nodes (4): ClinicInvitationService, Clinic, ClinicDoctorInvitation, User
### Community 661 - "Community 661"
Cohesion: 0.40
Nodes (5): 37. 🔵 `POST` post, Request Body, مثال Request, هدرهای اضافی, پاسخ‌ها
### Community 662 - "Community 662"
Cohesion: 0.29
Nodes (7): Application Logs, DELETE `/api/v1/admin/logs`, GET `/api/v1/admin/logs`, Log Retention, Query Parameters, Response `200`, Response `200`
@@ -2839,32 +2828,32 @@ Cohesion: 0.38
Nodes (5): Rate, RateRepository, Doctor, ManagerRegistry, User
### Community 666 - "Community 666"
Cohesion: 0.50
Nodes (4): GET /api/v1/appointment-settings/slots — دریافت اسلات‌های خالی, POST /api/v1/appointment-settings/holidays — ثبت تعطیلی (فقط ادمین), POST /api/v1/appointment-settings/overrides — ثبت Override توسط دکتر, Task-09: API تنظیمات نوبت
Cohesion: 0.40
Nodes (5): Errors, Path Parameters, POST `/api/v1/doctor/invitation/{invUuid}/respond`, Request Body, Response `200`
### Community 667 - "Community 667"
Cohesion: 0.50
Nodes (4): GET /api/v1/categories/cities — شهرها, GET /api/v1/categories/states — استان‌ها, Task-08: API دسته‌بندی‌ها, سایر Endpoint های دسته‌بندی — الزامی
### Community 671 - "Community 671"
Cohesion: 0.50
Nodes (4): ۲.۱۳ نظرات، لایک و امتیازدهی, ۲.۱۳.۱ نظر (Comment), ۲.۱۳.۲ لایک (Like), ۲.۱۳.۳ امتیاز (Rate)
Cohesion: 0.40
Nodes (5): DELETE `/api/v1/billing/tenant-insurances/{uuid}`, GET `/api/v1/billing/tenant-insurances`, PATCH `/api/v1/billing/tenant-insurances/{uuid}`, POST `/api/v1/billing/tenant-insurances`, TenantInsurance — قراردادهای بیمه‌ی tenant (فاز ۱ سیستم صورتحساب)
### Community 672 - "Community 672"
Cohesion: 0.50
Nodes (4): GET `/api/v1/admin/settlements`, Query Parameters, Response `200`, Settlement Management
### Community 673 - "Community 673"
Cohesion: 0.15
Nodes (3): LoggerInterface, RanginehProvider, ApiIrService
### Community 674 - "Community 674"
Cohesion: 0.47
Nodes (3): CommissionService, Payment, Representation
Cohesion: 0.50
Nodes (4): Errors, GET `/api/v1/admin/insurances`, Query Parameters, Response `200`
### Community 675 - "Community 675"
Cohesion: 0.50
Nodes (4): 39. 🟡 `PATCH` Comment confirmation, مثال Request, هدرهای اضافی, پاسخ‌ها
Nodes (4): Errors, POST `/api/v1/admin/insurance`, Request Body (`application/json`), Response `201`
### Community 676 - "Community 676"
Cohesion: 0.50
Nodes (4): 40. 🔵 `POST` post, مثال Request, هدرهای اضافی, پاسخ‌ها
Nodes (4): Errors, POST `/api/v1/insurance/`, Request Body (`application/json`), Response `201`
### Community 677 - "Community 677"
Cohesion: 0.43
@@ -2879,8 +2868,8 @@ Cohesion: 0.48
Nodes (3): DoctorInsuranceRepository, DoctorInsurance, ManagerRegistry
### Community 684 - "Community 684"
Cohesion: 0.53
Nodes (3): PaymentLog, PaymentLogRepository, ManagerRegistry
Cohesion: 0.50
Nodes (4): Errors, PATCH `/api/v1/insurance/{id}`, Request Body, Response `200`
### Community 686 - "Community 686"
Cohesion: 0.40
@@ -2891,32 +2880,52 @@ Cohesion: 0.50
Nodes (4): Errors, PATCH `/api/v1/admin/insurance/{id}`, Path Parameters, Response `200`
### Community 689 - "Community 689"
Cohesion: 0.67
Nodes (3): 33. 🔵 `POST` image_clinic, هدرهای اضافی, پاسخ‌ها
Cohesion: 0.50
Nodes (4): PUT `/api/v1/insurance-pricing`, Request Body, Response `200`, خطاها
### Community 690 - "Community 690"
Cohesion: 0.67
Nodes (3): 43. 🟢 `GET` get, هدرهای اضافی, پاسخ‌ها
Nodes (3): GET `/api/v1/billing/tenant-insurances/{uuid}/service-coverage`, PUT `/api/v1/billing/tenant-insurances/{uuid}/service-coverage`, TenantServiceCoverage — پوشش خدمت تحت یک قرارداد بیمه (فاز ۲)
### Community 692 - "Community 692"
Cohesion: 0.67
Nodes (3): GET `/api/v1/insurance-pricing`, Response `200`, خطاها
### Community 693 - "Community 693"
Cohesion: 0.67
Nodes (3): GET `/api/v1/insurances`, Query Parameters, Response `200`
### Community 694 - "Community 694"
Cohesion: 0.67
Nodes (3): POST `/api/v1/admin/insurance/{id}/upload-logo`, Request, Response `200`
### Community 695 - "Community 695"
Cohesion: 0.67
Nodes (3): DELETE `/api/v1/sms/template/{uuid}`, Errors, Response `200`
### Community 696 - "Community 696"
Cohesion: 0.67
Nodes (3): Errors, POST `/api/v1/sms/send` — ⛔ غیرفعال (Deprecated), Response `422` (همیشه)
## Knowledge Gaps
- **3812 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `TenantInsurance`, `CoverageRow`, `Draft` (+3807 more)
- **3916 isolated node(s):** `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps`, `TenantInsurance`, `CoverageRow` (+3911 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **133 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **137 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `BaseController` connect `Community 108` to `Community 4`, `Community 6`, `Community 138`, `Community 139`, `Community 14`, `Community 654`, `Community 655`, `Community 15`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 295`, `Community 169`, `Community 300`, `Community 175`, `Community 176`, `Community 177`, `Community 58`, `Community 59`, `Community 63`, `Community 64`, `Community 75`, `Community 77`, `Community 607`, `Community 227`, `Community 230`, `Community 104`, `Community 107`, `Community 109`, `Community 121`, `Community 122`, `Community 252`?**
_High betweenness centrality (0.044) - this node is a cross-community bridge._
- **Why does `BaseController` connect `Community 108` to `Community 4`, `Community 6`, `Community 138`, `Community 139`, `Community 14`, `Community 654`, `Community 655`, `Community 15`, `Community 20`, `Community 22`, `Community 26`, `Community 164`, `Community 295`, `Community 424`, `Community 169`, `Community 300`, `Community 175`, `Community 177`, `Community 58`, `Community 59`, `Community 63`, `Community 64`, `Community 75`, `Community 77`, `Community 607`, `Community 227`, `Community 230`, `Community 104`, `Community 107`, `Community 109`, `Community 121`, `Community 122`, `Community 252`?**
_High betweenness centrality (0.027) - this node is a cross-community bridge._
- **Why does `ApiTestCase` connect `Community 86` to `Community 397`, `Community 661`, `Community 22`, `Community 535`, `Community 541`, `Community 41`, `Community 688`, `Community 562`, `Community 565`, `Community 573`, `Community 574`, `Community 318`, `Community 575`, `Community 594`, `Community 352`, `Community 609`, `Community 484`, `Community 618`, `Community 497`?**
_High betweenness centrality (0.017) - this node is a cross-community bridge._
- **Why does `AppointmentRepository` connect `Community 119` to `Community 53`?**
_High betweenness centrality (0.016) - this node is a cross-community bridge._
- **Why does `Version20260614181657` connect `Community 431` to `Community 399`?**
_High betweenness centrality (0.015) - this node is a cross-community bridge._
- **What connects `ALLOWED_ROLES`, `Pricing`, `TenantInsurance` to the rest of the system?**
_3812 weakly-connected nodes found - possible documentation gaps or missing edges._
_High betweenness centrality (0.017) - this node is a cross-community bridge._
- **What connects `ALLOWED_ROLES`, `Pricing`, `ImageCropModalProps` to the rest of the system?**
_3916 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.049678550555230856 - nodes in this community are weakly interconnected._
_Cohesion score 0.05200501253132832 - nodes in this community are weakly interconnected._
- **Should `Community 1` be split into smaller, more focused modules?**
_Cohesion score 0.028985507246376812 - nodes in this community are weakly interconnected._
- **Should `Community 2` be split into smaller, more focused modules?**
_Cohesion score 0.10507246376811594 - nodes in this community are weakly interconnected._
_Cohesion score 0.09523809523809523 - nodes in this community are weakly interconnected._
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260705070546_php", "label": "Version20260705070546.php", "file_type": "code", "source_file": "migrations/Version20260705070546.php", "source_location": "L1"}, {"id": "migrations_version20260705070546_version20260705070546", "label": "Version20260705070546", "file_type": "code", "source_file": "migrations/Version20260705070546.php", "source_location": "L14"}, {"id": "abstractmigration", "label": "AbstractMigration", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "migrations_version20260705070546_version20260705070546_getdescription", "label": ".getDescription()", "file_type": "code", "source_file": "migrations/Version20260705070546.php", "source_location": "L16"}, {"id": "migrations_version20260705070546_version20260705070546_up", "label": ".up()", "file_type": "code", "source_file": "migrations/Version20260705070546.php", "source_location": "L21"}, {"id": "schema", "label": "Schema", "file_type": "code", "source_file": "migrations/Version20260705070546.php", "source_location": "L21"}, {"id": "migrations_version20260705070546_version20260705070546_down", "label": ".down()", "file_type": "code", "source_file": "migrations/Version20260705070546.php", "source_location": "L33"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260705070546_php", "target": "schema", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L7", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260705070546_php", "target": "abstractmigration", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L8", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260705070546_php", "target": "migrations_version20260705070546_version20260705070546", "relation": "contains", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L14", "weight": 1.0}, {"source": "migrations_version20260705070546_version20260705070546", "target": "abstractmigration", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L14", "weight": 1.0}, {"source": "migrations_version20260705070546_version20260705070546", "target": "migrations_version20260705070546_version20260705070546_getdescription", "relation": "method", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L16", "weight": 1.0}, {"source": "migrations_version20260705070546_version20260705070546", "target": "migrations_version20260705070546_version20260705070546_up", "relation": "method", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L21", "weight": 1.0}, {"source": "migrations_version20260705070546_version20260705070546_up", "target": "schema", "relation": "references", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L21", "weight": 1.0, "context": "parameter_type"}, {"source": "migrations_version20260705070546_version20260705070546", "target": "migrations_version20260705070546_version20260705070546_down", "relation": "method", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L33", "weight": 1.0}, {"source": "migrations_version20260705070546_version20260705070546_down", "target": "schema", "relation": "references", "confidence": "EXTRACTED", "source_file": "migrations/Version20260705070546.php", "source_location": "L33", "weight": 1.0, "context": "parameter_type"}], "raw_calls": [{"caller_nid": "migrations_version20260705070546_version20260705070546_up", "callee": "time", "is_member_call": false, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/migrations/Version20260705070546.php", "source_location": "L23", "receiver": null}, {"caller_nid": "migrations_version20260705070546_version20260705070546_up", "callee": "addSql", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/migrations/Version20260705070546.php", "source_location": "L24", "receiver": null}, {"caller_nid": "migrations_version20260705070546_version20260705070546_down", "callee": "time", "is_member_call": false, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/migrations/Version20260705070546.php", "source_location": "L35", "receiver": null}, {"caller_nid": "migrations_version20260705070546_version20260705070546_down", "callee": "addSql", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/migrations/Version20260705070546.php", "source_location": "L36", "receiver": null}]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"nodes": [], "edges": [], "skipped": "data json (non-object root)"}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260703085828_php", "label": "Version20260703085828.php", "file_type": "code", "source_file": "migrations/Version20260703085828.php", "source_location": "L1"}, {"id": "migrations_version20260703085828_version20260703085828", "label": "Version20260703085828", "file_type": "code", "source_file": "migrations/Version20260703085828.php", "source_location": "L13"}, {"id": "abstractmigration", "label": "AbstractMigration", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "migrations_version20260703085828_version20260703085828_getdescription", "label": ".getDescription()", "file_type": "code", "source_file": "migrations/Version20260703085828.php", "source_location": "L15"}, {"id": "migrations_version20260703085828_version20260703085828_up", "label": ".up()", "file_type": "code", "source_file": "migrations/Version20260703085828.php", "source_location": "L20"}, {"id": "schema", "label": "Schema", "file_type": "code", "source_file": "migrations/Version20260703085828.php", "source_location": "L20"}, {"id": "migrations_version20260703085828_version20260703085828_down", "label": ".down()", "file_type": "code", "source_file": "migrations/Version20260703085828.php", "source_location": "L26"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260703085828_php", "target": "schema", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L7", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260703085828_php", "target": "abstractmigration", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L8", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_migrations_version20260703085828_php", "target": "migrations_version20260703085828_version20260703085828", "relation": "contains", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L13", "weight": 1.0}, {"source": "migrations_version20260703085828_version20260703085828", "target": "abstractmigration", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L13", "weight": 1.0}, {"source": "migrations_version20260703085828_version20260703085828", "target": "migrations_version20260703085828_version20260703085828_getdescription", "relation": "method", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L15", "weight": 1.0}, {"source": "migrations_version20260703085828_version20260703085828", "target": "migrations_version20260703085828_version20260703085828_up", "relation": "method", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L20", "weight": 1.0}, {"source": "migrations_version20260703085828_version20260703085828_up", "target": "schema", "relation": "references", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L20", "weight": 1.0, "context": "parameter_type"}, {"source": "migrations_version20260703085828_version20260703085828", "target": "migrations_version20260703085828_version20260703085828_down", "relation": "method", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L26", "weight": 1.0}, {"source": "migrations_version20260703085828_version20260703085828_down", "target": "schema", "relation": "references", "confidence": "EXTRACTED", "source_file": "migrations/Version20260703085828.php", "source_location": "L26", "weight": 1.0, "context": "parameter_type"}], "raw_calls": [{"caller_nid": "migrations_version20260703085828_version20260703085828_up", "callee": "addSql", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/migrations/Version20260703085828.php", "source_location": "L23", "receiver": null}, {"caller_nid": "migrations_version20260703085828_version20260703085828_down", "callee": "addSql", "is_member_call": true, "source_file": "/Users/hamed/pj/my_pj/clinic_pro/clinicpro/migrations/Version20260703085828.php", "source_location": "L29", "receiver": null}]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"nodes": [{"id": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "label": "manifest.json", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L1"}, {"id": "public_manifest_name", "label": "name", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L2"}, {"id": "public_manifest_short_name", "label": "short_name", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L3"}, {"id": "public_manifest_description", "label": "description", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L4"}, {"id": "public_manifest_start_url", "label": "start_url", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L5"}, {"id": "public_manifest_scope", "label": "scope", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L6"}, {"id": "public_manifest_display", "label": "display", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L7"}, {"id": "public_manifest_orientation", "label": "orientation", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L8"}, {"id": "public_manifest_theme_color", "label": "theme_color", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L9"}, {"id": "public_manifest_background_color", "label": "background_color", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L10"}, {"id": "public_manifest_lang", "label": "lang", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L11"}, {"id": "public_manifest_dir", "label": "dir", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L12"}, {"id": "public_manifest_icons", "label": "icons", "file_type": "code", "source_file": "public/manifest.json", "source_location": "L13"}], "edges": [{"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_name", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L2", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_short_name", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L3", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_description", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L4", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_start_url", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L5", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_scope", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L6", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_display", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L7", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_orientation", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L8", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_theme_color", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L9", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_background_color", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L10", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_lang", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L11", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_dir", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L12", "weight": 1.0}, {"source": "users_hamed_pj_my_pj_clinic_pro_clinicpro_public_manifest_json", "target": "public_manifest_icons", "relation": "contains", "confidence": "EXTRACTED", "source_file": "public/manifest.json", "source_location": "L13", "weight": 1.0}]}
+1 -1
View File
File diff suppressed because one or more lines are too long
+7187 -2361
View File
File diff suppressed because it is too large Load Diff
+214 -114
View File
@@ -5,8 +5,8 @@
"semantic_hash": ""
},
"assets/admin/components/FreeVisitPrice.tsx": {
"mtime": 1782253591.7364018,
"ast_hash": "26aabfbc86b4217eb9db4a8345229c92",
"mtime": 1783060655.245339,
"ast_hash": "33b0b899489f628c56f43f010cad71e3",
"semantic_hash": ""
},
"assets/admin/components/ServiceInsuranceModal.tsx": {
@@ -15,13 +15,13 @@
"semantic_hash": ""
},
"assets/admin/components/ServiceTariffModal.tsx": {
"mtime": 1782264004.3407443,
"ast_hash": "38fd9678b55525228603df77742e341b",
"mtime": 1783060787.3964596,
"ast_hash": "76e4725611298260c8235f1a41b45d0c",
"semantic_hash": ""
},
"assets/admin/components/TenantInsuranceContracts.tsx": {
"mtime": 1782253591.736994,
"ast_hash": "97321cc8db7375d930586e10d184a5ae",
"mtime": 1783060722.7764726,
"ast_hash": "9e766101c7f876b65d3e5ec9b3da4420",
"semantic_hash": ""
},
"assets/admin/components/layout/AdminLayout.tsx": {
@@ -30,13 +30,13 @@
"semantic_hash": ""
},
"assets/admin/components/layout/Sidebar.tsx": {
"mtime": 1782750354.360757,
"ast_hash": "1e0b143a269672d9e611f4e702dd8cac",
"mtime": 1783067421.0219867,
"ast_hash": "61ff8f2939c5c0d3d1aa9c23b75bcdb6",
"semantic_hash": ""
},
"assets/admin/components/layout/Topbar.tsx": {
"mtime": 1781111994.2005057,
"ast_hash": "92d97126e627b9a18f812d30c322c32d",
"mtime": 1783189668.855815,
"ast_hash": "66881dcb193209362927d575aa110922",
"semantic_hash": ""
},
"assets/admin/components/ui/AppointmentStatusDropdown.tsx": {
@@ -45,8 +45,8 @@
"semantic_hash": ""
},
"assets/admin/components/ui/ConfirmDialog.tsx": {
"mtime": 1781109691.353964,
"ast_hash": "803fbae10b30e9d38b09512048fdbd9f",
"mtime": 1783189607.8654194,
"ast_hash": "e22953175e675b3c6aeb5b271cb77206",
"semantic_hash": ""
},
"assets/admin/components/ui/DataTable.tsx": {
@@ -60,8 +60,8 @@
"semantic_hash": ""
},
"assets/admin/components/ui/InviteDoctorModal.tsx": {
"mtime": 1781870026.450072,
"ast_hash": "5910410f6c09b29fc6497cca92b53e8d",
"mtime": 1783189629.4718661,
"ast_hash": "45a914f8e2775468280a5c2a0ad3e01f",
"semantic_hash": ""
},
"assets/admin/components/ui/MobileInput.tsx": {
@@ -145,8 +145,8 @@
"semantic_hash": ""
},
"assets/admin/hooks/useSubscription.ts": {
"mtime": 1781516938.550741,
"ast_hash": "94fa2fa11d78afefe406306d03692e00",
"mtime": 1783235529.002945,
"ast_hash": "9553a236e4931102ebdb500b52b258e3",
"semantic_hash": ""
},
"assets/admin/index.tsx": {
@@ -160,18 +160,18 @@
"semantic_hash": ""
},
"assets/admin/lib/utils.ts": {
"mtime": 1781864093.5509045,
"ast_hash": "4a72e1874b1b86320baaca3e9e691667",
"mtime": 1783060462.649197,
"ast_hash": "dd0cd0483c3515a3048027636466e097",
"semantic_hash": ""
},
"assets/admin/pages/AdminSubscriptionPage.tsx": {
"mtime": 1783014651.0181222,
"ast_hash": "f2502ed4238124e7d9b86857593bebdf",
"mtime": 1783234743.5450752,
"ast_hash": "89172780cda8271d4ad9a759bd80307d",
"semantic_hash": ""
},
"assets/admin/pages/AppointmentDetailPage.tsx": {
"mtime": 1782992372.1432073,
"ast_hash": "9cf58f2636dd09e245edd9e28d1829fe",
"mtime": 1783412110.7500918,
"ast_hash": "d8b3fa65a95adc80cba7c041b64e6aee",
"semantic_hash": ""
},
"assets/admin/pages/AppointmentsPage.tsx": {
@@ -200,8 +200,8 @@
"semantic_hash": ""
},
"assets/admin/pages/ClinicDetailPage.tsx": {
"mtime": 1783014566.3569148,
"ast_hash": "0b9752fdf176101b09568cd96adfab58",
"mtime": 1783189824.9124436,
"ast_hash": "6d8004a5a5e2cf62a9d48efd20b3cf5b",
"semantic_hash": ""
},
"assets/admin/pages/ClinicFormPage.tsx": {
@@ -210,13 +210,13 @@
"semantic_hash": ""
},
"assets/admin/pages/ClinicServicesPage.tsx": {
"mtime": 1782261334.490092,
"ast_hash": "a0d62afa19eec00d6c4649a2936f7a12",
"mtime": 1783061096.3412282,
"ast_hash": "4126cd738d2fffaae14b587ce665cd83",
"semantic_hash": ""
},
"assets/admin/pages/ClinicsPage.tsx": {
"mtime": 1783014650.9975634,
"ast_hash": "9821e090ed254341b6e0a94b1e30da7d",
"mtime": 1783189730.939223,
"ast_hash": "85c4acd6491e578cf1d2d3258abf7626",
"semantic_hash": ""
},
"assets/admin/pages/CommentsPage.tsx": {
@@ -230,8 +230,8 @@
"semantic_hash": ""
},
"assets/admin/pages/DoctorDetailPage.tsx": {
"mtime": 1783014580.2052991,
"ast_hash": "56eb0dfe4c7bf715ac61c245d05ba36f",
"mtime": 1783189217.065477,
"ast_hash": "d10e0ec287b6d42011d384e3e8d8583b",
"semantic_hash": ""
},
"assets/admin/pages/DoctorFormPage.tsx": {
@@ -260,8 +260,8 @@
"semantic_hash": ""
},
"assets/admin/pages/LoginPage.tsx": {
"mtime": 1782396353.6326334,
"ast_hash": "8985cba0bd39fa88c3359a69b60956c2",
"mtime": 1783067422.968726,
"ast_hash": "24f66d4545d352c0c78b9c00645bebb5",
"semantic_hash": ""
},
"assets/admin/pages/MyClinicPage.tsx": {
@@ -295,13 +295,13 @@
"semantic_hash": ""
},
"assets/admin/pages/PaymentsPage.tsx": {
"mtime": 1783014538.78235,
"ast_hash": "0e2cad0f531169ccd722cc7efded481a",
"mtime": 1783061684.7522023,
"ast_hash": "03f3fbcc85d146f53f475aad48226c41",
"semantic_hash": ""
},
"assets/admin/pages/PreRegistrationsPage.tsx": {
"mtime": 1783014651.0085008,
"ast_hash": "54355562c5a5c8b1a5affd1c3427dfed",
"mtime": 1783189748.8708737,
"ast_hash": "f129f32ea163089b80b7efcb1c8d144e",
"semantic_hash": ""
},
"assets/admin/pages/RatingsPage.tsx": {
@@ -325,8 +325,8 @@
"semantic_hash": ""
},
"assets/admin/pages/RepresentationSettlementPage.tsx": {
"mtime": 1783014651.0150015,
"ast_hash": "87c76a7946e4fd7ffd9ce47c4e355287",
"mtime": 1783060990.6327345,
"ast_hash": "239a3515ee1b06600f34aae4b42b74ad",
"semantic_hash": ""
},
"assets/admin/pages/RepresentationsPage.tsx": {
@@ -345,8 +345,8 @@
"semantic_hash": ""
},
"assets/admin/pages/SettingsPage.tsx": {
"mtime": 1783010796.4329321,
"ast_hash": "e70d4f6c19b088d4c8bb6bdd57b32bdb",
"mtime": 1783060574.3517244,
"ast_hash": "342ffb05150e7e73101ab5c0b4581113",
"semantic_hash": ""
},
"assets/admin/pages/SettlementDetailPage.tsx": {
@@ -360,13 +360,13 @@
"semantic_hash": ""
},
"assets/admin/pages/SmsPage.tsx": {
"mtime": 1782384522.9625459,
"ast_hash": "2395be5bafe74eafee0507e6ae270ff6",
"mtime": 1783237919.164836,
"ast_hash": "315c9186b87f6f9fab6d42cc72407fbc",
"semantic_hash": ""
},
"assets/admin/pages/SmsWalletPage.tsx": {
"mtime": 1783014651.0052793,
"ast_hash": "6a10dbd574fe40dd0a7978dffd88db66",
"mtime": 1783060909.263113,
"ast_hash": "54525372c41c826269ad2fdc63ba5466",
"semantic_hash": ""
},
"assets/admin/pages/StaffPage.tsx": {
@@ -385,8 +385,8 @@
"semantic_hash": ""
},
"assets/admin/pages/UsersPage.tsx": {
"mtime": 1781282181.8709655,
"ast_hash": "b37d52528ff9d69795fb684168c9d04d",
"mtime": 1783189698.4759433,
"ast_hash": "73d8276816592940a2d66d013443ca39",
"semantic_hash": ""
},
"assets/admin/stores/authStore.ts": {
@@ -400,8 +400,8 @@
"semantic_hash": ""
},
"assets/admin/types/index.ts": {
"mtime": 1783010796.4332225,
"ast_hash": "e7305bcff435adaa9df6a2c42a992924",
"mtime": 1783236968.6643784,
"ast_hash": "ae716d8c32e4747984bba610dac11fe9",
"semantic_hash": ""
},
"assets/app.js": {
@@ -780,8 +780,8 @@
"semantic_hash": ""
},
"package.json": {
"mtime": 1782976135.2818882,
"ast_hash": "a1d6d0993877d027bf35d702f382e373",
"mtime": 1783189109.134888,
"ast_hash": "f0b61dd4c429f6bceac993e60b933ab4",
"semantic_hash": ""
},
"postcss.config.js": {
@@ -795,8 +795,8 @@
"semantic_hash": ""
},
"public/manifest.json": {
"mtime": 1781344581.73852,
"ast_hash": "cce0d12ed76c2fc053b190c5e27ae73a",
"mtime": 1783067213.2312572,
"ast_hash": "6d4b402f211a53ccfd25050b17542cc1",
"semantic_hash": ""
},
"public/sw.js": {
@@ -840,8 +840,8 @@
"semantic_hash": ""
},
"src/Appointment/Entity/Appointment.php": {
"mtime": 1782728407.1901643,
"ast_hash": "846b7a62fa3dec3970ea3d28331e1bab",
"mtime": 1783059631.6813712,
"ast_hash": "4eb6d63f8461e96ff592963ee14d5dcc",
"semantic_hash": ""
},
"src/Appointment/Entity/DateOverride.php": {
@@ -905,18 +905,18 @@
"semantic_hash": ""
},
"src/Auth/Controller/AuthController.php": {
"mtime": 1782728407.1912344,
"ast_hash": "b7cad6d36b6d0a354a2acb29d6000e1c",
"mtime": 1783071277.1970987,
"ast_hash": "b4778b795ee2d27f5e857a1967c337df",
"semantic_hash": ""
},
"src/Auth/Controller/NotificationMobileController.php": {
"mtime": 1781888558.424223,
"ast_hash": "27f5a5811186297fb5b562d6ea60880b",
"mtime": 1783237168.983222,
"ast_hash": "61b4c4c7f8290cbbf17bbd51db1ffc12",
"semantic_hash": ""
},
"src/Auth/Controller/PreRegistrationController.php": {
"mtime": 1782728407.191858,
"ast_hash": "f67fc43368ba9bbccb55d58f1f67aa64",
"mtime": 1783238186.8425517,
"ast_hash": "69a551f873ff500c47fc880ea088be8b",
"semantic_hash": ""
},
"src/Auth/Entity/MobileVerificationOtp.php": {
@@ -960,8 +960,8 @@
"semantic_hash": ""
},
"src/Auth/Service/OtpService.php": {
"mtime": 1781946485.89198,
"ast_hash": "7a98956de85e879db83c1868c902699a",
"mtime": 1783236682.997204,
"ast_hash": "4a2f2d8b2a06f6521f56f372dd9ec6ac",
"semantic_hash": ""
},
"src/Auth/Service/TokenService.php": {
@@ -1105,8 +1105,8 @@
"semantic_hash": ""
},
"src/ClinicInvitation/Service/ClinicInvitationService.php": {
"mtime": 1781888637.7279549,
"ast_hash": "98947642164eeba491e1fc0d682652fc",
"mtime": 1783237201.6128454,
"ast_hash": "3e70bceb24b5bbefbf9a06013ce64cf3",
"semantic_hash": ""
},
"src/ClinicService/Controller/ClinicServiceController.php": {
@@ -1360,8 +1360,8 @@
"semantic_hash": ""
},
"src/Payment/Controller/PaymentController.php": {
"mtime": 1783011733.758226,
"ast_hash": "a4dbad9ee7aa5df9aed3acf2d7f89ba9",
"mtime": 1783059694.9443185,
"ast_hash": "c55d1d0db3a84fd43dbdfe7bb425dad0",
"semantic_hash": ""
},
"src/Payment/Entity/Payment.php": {
@@ -1445,8 +1445,8 @@
"semantic_hash": ""
},
"src/Representation/Controller/RepresentationActionController.php": {
"mtime": 1782979010.2578533,
"ast_hash": "7468f7a2aaa928ef0150fa745267e765",
"mtime": 1783236899.056081,
"ast_hash": "edeb32fbce8b281548f7a1dc510bbef9",
"semantic_hash": ""
},
"src/Representation/Controller/RepresentationController.php": {
@@ -1475,8 +1475,8 @@
"semantic_hash": ""
},
"src/Secretary/Controller/SecretaryController.php": {
"mtime": 1782929066.3195713,
"ast_hash": "9cbf2e62e58b071b044dda089612dd4a",
"mtime": 1783237220.8926466,
"ast_hash": "5de5217dce5bebb2dc20e42800d06517",
"semantic_hash": ""
},
"src/Secretary/Entity/DoctorSecretary.php": {
@@ -1595,18 +1595,18 @@
"semantic_hash": ""
},
"src/Sms/Command/SeedSmsMessageTemplatesCommand.php": {
"mtime": 1781888665.382464,
"ast_hash": "3a4d7786d2b55e7f7b03e926bfb388f7",
"mtime": 1783237401.025294,
"ast_hash": "c162a05d42dc5abd55491327d81df0e9",
"semantic_hash": ""
},
"src/Sms/Controller/SmsController.php": {
"mtime": 1781889430.823495,
"ast_hash": "afb452b1f9a17fa268e492c635ae3c83",
"mtime": 1783422449.6546745,
"ast_hash": "bc3c230636b0144316d6de31a571de21",
"semantic_hash": ""
},
"src/Sms/Controller/SmsMessageController.php": {
"mtime": 1781888448.780114,
"ast_hash": "9cc390bfbb355c4e099629ef71bf22ec",
"mtime": 1783237134.1834602,
"ast_hash": "a83a980b540da9f080dc2d8cbfdecc7d",
"semantic_hash": ""
},
"src/Sms/Controller/SmsWalletController.php": {
@@ -1620,8 +1620,8 @@
"semantic_hash": ""
},
"src/Sms/Entity/SmsMessageTemplate.php": {
"mtime": 1783013406.3586075,
"ast_hash": "d61835bd817a3c614ac1f7ec4190cff2",
"mtime": 1783239399.6824262,
"ast_hash": "0b700e4754211f3619b19b236b52c980",
"semantic_hash": ""
},
"src/Sms/Entity/SmsSettings.php": {
@@ -1650,8 +1650,8 @@
"semantic_hash": ""
},
"src/Sms/Provider/KavehNegarProvider.php": {
"mtime": 1782893881.0959284,
"ast_hash": "53cc58e643103610cd94b9345b0ffb39",
"mtime": 1783239423.5871642,
"ast_hash": "dc0ecd9474beba57d252f7367e11c6ee",
"semantic_hash": ""
},
"src/Sms/Provider/RanginehProvider.php": {
@@ -1700,8 +1700,8 @@
"semantic_hash": ""
},
"src/Sms/Service/SmsService.php": {
"mtime": 1781888026.6171083,
"ast_hash": "507afd0eb63abb602f7020b08eade663",
"mtime": 1783422449.654906,
"ast_hash": "2ecc3be9f619c38c0b629ea31a5e1d03",
"semantic_hash": ""
},
"src/Sms/Service/SmsTextResolver.php": {
@@ -1745,8 +1745,8 @@
"semantic_hash": ""
},
"src/Subscription/Controller/SubscriptionController.php": {
"mtime": 1781522583.7349539,
"ast_hash": "fdc1092513112a88e3bbdf11fe0cb36f",
"mtime": 1783235518.9559367,
"ast_hash": "cb05c759a1c625db2bc201989b32360f",
"semantic_hash": ""
},
"src/Subscription/Entity/ClinicSubscription.php": {
@@ -1760,8 +1760,8 @@
"semantic_hash": ""
},
"src/Subscription/Entity/SubscriptionPlan.php": {
"mtime": 1781516938.5825694,
"ast_hash": "0fda8d00ecb40b110aa4f526bd2eec53",
"mtime": 1783233488.1702743,
"ast_hash": "2cf4bbcddcfe5a5574135e96ab97c156",
"semantic_hash": ""
},
"src/Subscription/Repository/ClinicSubscriptionRepository.php": {
@@ -1775,13 +1775,13 @@
"semantic_hash": ""
},
"src/Subscription/Repository/SubscriptionPlanRepository.php": {
"mtime": 1781516938.582985,
"ast_hash": "cfbc76bf2f114464f2dca407b15a899e",
"mtime": 1783234723.5048378,
"ast_hash": "7a534d21319170d6dc0d3db6a46aa973",
"semantic_hash": ""
},
"src/Subscription/Service/SubscriptionService.php": {
"mtime": 1783010796.4419346,
"ast_hash": "df1c35a147116e7a52139e23bf0024c5",
"mtime": 1783235508.7354445,
"ast_hash": "b3f054e09e4084b1016f58d619f27cff",
"semantic_hash": ""
},
"src/Tag/Controller/TagController.php": {
@@ -2230,8 +2230,8 @@
"semantic_hash": ""
},
"config/packages/messenger.yaml": {
"mtime": 1782929813.4472997,
"ast_hash": "2bcd86d1b6bfe2890abcb27a71bb7c65",
"mtime": 1783416412.700051,
"ast_hash": "c8ae652937b41c3c33b9a0358465b025",
"semantic_hash": ""
},
"config/packages/nelmio_api_doc.yaml": {
@@ -2300,8 +2300,8 @@
"semantic_hash": ""
},
"config/services.yaml": {
"mtime": 1782993263.0293136,
"ast_hash": "27502355f3182447d5ff05d58ae0f27b",
"mtime": 1783238196.169478,
"ast_hash": "9f71355af38e08d9ebb55a70f3965459",
"semantic_hash": ""
},
"docs/Architecture_Audit.md": {
@@ -2355,8 +2355,8 @@
"semantic_hash": ""
},
"docs/api/auth.md": {
"mtime": 1782728407.1075277,
"ast_hash": "c193f86d49bb42a6198807d0497ff145",
"mtime": 1783072324.1093419,
"ast_hash": "fe76d2c41f7e9996751b1002edb50c6b",
"semantic_hash": ""
},
"docs/api/billing.md": {
@@ -2415,8 +2415,8 @@
"semantic_hash": ""
},
"docs/api/payment.md": {
"mtime": 1783011804.9949813,
"ast_hash": "b4e46fcd33854944ea5b9e80172cb47f",
"mtime": 1783059802.8154833,
"ast_hash": "324965b58abe6317caab5653c982dfa7",
"semantic_hash": ""
},
"docs/api/rating.md": {
@@ -2440,8 +2440,8 @@
"semantic_hash": ""
},
"docs/api/sms.md": {
"mtime": 1783013072.0409873,
"ast_hash": "fa27f7d1bb6c3241447f844133a400f5",
"mtime": 1783422271.8648708,
"ast_hash": "c9074f705b1ecb69fff198b4de732136",
"semantic_hash": ""
},
"docs/api/specialty.md": {
@@ -2455,8 +2455,8 @@
"semantic_hash": ""
},
"docs/api/subscription.md": {
"mtime": 1781516938.5630767,
"ast_hash": "dd89fadf917c707c907188708106a092",
"mtime": 1783235773.210707,
"ast_hash": "fa134000029c8940463e87fbde7bbf96",
"semantic_hash": ""
},
"docs/api/tag.md": {
@@ -2975,13 +2975,13 @@
"semantic_hash": ""
},
"public/icons/icon-192.png": {
"mtime": 1781344567.4313211,
"ast_hash": "bc03c95a7343cf802c7259d1a7dffae3",
"mtime": 1783067149.6622553,
"ast_hash": "b9f4929301ffa52fe18ed5b6085cd90e",
"semantic_hash": ""
},
"public/icons/icon-512.png": {
"mtime": 1781344567.4635944,
"ast_hash": "c05ed2a8f3994262e14b1a68644a12ac",
"mtime": 1783067149.6663008,
"ast_hash": "33de033fde6adfd2fcf8d7389950c2bd",
"semantic_hash": ""
},
"docker/entrypoint.sh": {
@@ -3285,8 +3285,8 @@
"semantic_hash": ""
},
"assets/admin/hooks/hooks.test.tsx": {
"mtime": 1782728407.0925536,
"ast_hash": "211876b92667979d0f1363a73c351183",
"mtime": 1783235583.87458,
"ast_hash": "976013dde8b2af8e6173134261211335",
"semantic_hash": ""
},
"assets/admin/lib/api.test.ts": {
@@ -3295,8 +3295,8 @@
"semantic_hash": ""
},
"assets/admin/lib/utils.test.ts": {
"mtime": 1782728407.0943124,
"ast_hash": "02c5c9fb7810480bb1f449a523eb6e99",
"mtime": 1783061147.064616,
"ast_hash": "18d3b5178eff1022816b0f4c3b07059c",
"semantic_hash": ""
},
"assets/admin/pages/BlogFormPage.test.tsx": {
@@ -3430,8 +3430,8 @@
"semantic_hash": ""
},
"data/seed/cities.json": {
"mtime": 1782974609.7399116,
"ast_hash": "a3210b7dbfa06d151018197534694ded",
"mtime": 1783069922.2119036,
"ast_hash": "24bc6b07dfdf32298ef64a5dba0effbc",
"semantic_hash": ""
},
"data/seed/doctor_services.json": {
@@ -3540,8 +3540,8 @@
"semantic_hash": ""
},
"src/Payment/Service/PaymentManager.php": {
"mtime": 1783012974.905455,
"ast_hash": "c968b32943ddf0363ac01fc8e6c9eb31",
"mtime": 1783239408.129024,
"ast_hash": "51937725073adf1f98391b485befaa79",
"semantic_hash": ""
},
"tests/Payment/MellatGatewayTest.php": {
@@ -3603,5 +3603,105 @@
"mtime": 1782998857.27679,
"ast_hash": "6884dbc77045e3a46508c1ea7b0b8206",
"semantic_hash": ""
},
"assets/admin/components/ImageCropModal.tsx": {
"mtime": 1783189383.5120335,
"ast_hash": "88a20f1dccf54d5c405fe9ad193f6a0a",
"semantic_hash": ""
},
"assets/admin/components/ui/Portal.tsx": {
"mtime": 1783189596.9823797,
"ast_hash": "221fa80f30d491bfd48962c2803c2fb4",
"semantic_hash": ""
},
"assets/admin/lib/cropImage.ts": {
"mtime": 1783189130.6179984,
"ast_hash": "7b234a79f8d50bbda27fc4ca520d71b6",
"semantic_hash": ""
},
"migrations/Version20260703085828.php": {
"mtime": 1783069119.2053103,
"ast_hash": "ba7e6f01fcf7a3978b886947e0b411fd",
"semantic_hash": ""
},
"migrations/Version20260705070546.php": {
"mtime": 1783235160.1264384,
"ast_hash": "0c8199e8a7578078b2c128e4e1403a04",
"semantic_hash": ""
},
"migrations/Version20260705072835.php": {
"mtime": 1783236836.0604913,
"ast_hash": "e7a5baa6a594a564f567ddd0f693a4b6",
"semantic_hash": ""
},
"migrations/Version20260705081716.php": {
"mtime": 1783239466.9926126,
"ast_hash": "a98d2aff5ff87845413d5096b3b69251",
"semantic_hash": ""
},
"tests/Sms/SmsServiceLookupOnlyTest.php": {
"mtime": 1783422346.6194532,
"ast_hash": "9438dc12e73d86d85511144e7cc66a1a",
"semantic_hash": ""
},
".claude/prompt/clinic-pro-promo-video.md": {
"mtime": 1783245223.582404,
"ast_hash": "b4bb49e1103a0d088a693637280a3a5d",
"semantic_hash": ""
},
".claude/prompt/currency-toman-display-admin.md": {
"mtime": 1783060267.9411626,
"ast_hash": "012387127ce9f2f1f4620f754f4b8f5d",
"semantic_hash": ""
},
".claude/prompt/expired-appointment-blocks-payment.md": {
"mtime": 1783059494.3545446,
"ast_hash": "f7169d8b155acf807998a866a2a53aeb",
"semantic_hash": ""
},
".claude/prompt/fix-modal-portal-positioning.md": {
"mtime": 1783189568.4460306,
"ast_hash": "d2cd9fef9829b460547dc461e7a60742",
"semantic_hash": ""
},
".claude/prompt/otp-sms-site-name.md": {
"mtime": 1783070793.0096982,
"ast_hash": "1f4eaf90a69be286ad3c63ebca6aac1e",
"semantic_hash": ""
},
".claude/prompt/otp-via-kavenegar-lookup.md": {
"mtime": 1783151621.4100735,
"ast_hash": "0a583a2b07bdcbf884c5e814b3c4b993",
"semantic_hash": ""
},
".claude/prompt/sms-lookup-only.md": {
"mtime": 1783421842.1666644,
"ast_hash": "6d23d5cb7ceee2759783a957fc99c76e",
"semantic_hash": ""
},
".claude/prompt/sms-verify-lookup-templates.md": {
"mtime": 1783236336.6524806,
"ast_hash": "0916ac7bf37a1c0e9a1d75f4b7b4c4b6",
"semantic_hash": ""
},
"public/_transparency-preview.png": {
"mtime": 1783015134.0,
"ast_hash": "258e00434d9e9d84778d629203f0899d",
"semantic_hash": ""
},
"public/apple-touch-icon.png": {
"mtime": 1783067149.6687918,
"ast_hash": "e47a20da3454186f7be441441f231bf3",
"semantic_hash": ""
},
"public/favicon-32.png": {
"mtime": 1783067149.6695104,
"ast_hash": "b40afe9488167fab007741810396705e",
"semantic_hash": ""
},
"public/logo.svg": {
"mtime": 1783066913.0879276,
"ast_hash": "074cb84109dd212089ba2b5397e9b3ed",
"semantic_hash": ""
}
}
+8 -12
View File
@@ -63,18 +63,9 @@ class SmsController extends BaseController
#[Route('/api/v1/sms/send', methods: ['POST'])]
public function send(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true) ?? [];
$mobile = trim($data['mobile'] ?? '');
$message = trim($data['message'] ?? '');
$provider = $data['provider'] ?? 'kavenegar';
if (empty($mobile) || empty($message)) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'mobile و message الزامی است', 422);
}
$this->smsService->dispatchAsync($mobile, $message, $provider, tag: \App\Sms\Entity\SmsLog::TAG_USER_TEMPLATE);
return $this->success(['message' => 'پیامک در صف ارسال قرار گرفت']);
// سیاست lookup-only: ارسال متن آزاد با sms/send.json مجاز نیست (در ایران فیلتر می‌شود).
// برای ارسال دستی از تمپلیت تأییدشده استفاده کنید: POST /api/v1/sms/send-via-template
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'ارسال متن آزاد مجاز نیست؛ از تمپلیت تأییدشده (VerifyLookup) استفاده کنید', 422);
}
// ── Templates ─────────────────────────────────────────────────────────────
@@ -484,6 +475,11 @@ class SmsController extends BaseController
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'تمپلیت هنوز تأیید نشده است', 422);
}
// سیاست lookup-only: بدون کد VerifyLookup کاوه‌نگار قابل ارسال نیست.
if (!$template->getProviderCode()) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این تمپلیت کد VerifyLookup کاوه‌نگار ندارد و قابل ارسال نیست', 422);
}
$message = $template->renderBody($vars);
$this->smsService->dispatchAsync(
$mobile, $message, $provider, $template->getUuid(),
+16 -4
View File
@@ -10,6 +10,7 @@ use App\Sms\Provider\RanginehProvider;
use App\Sms\Provider\SmsProviderInterface;
use App\Sms\Repository\SmsLogRepository;
use App\Sms\Repository\SmsMessageTemplateRepository;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
class SmsService
@@ -23,6 +24,7 @@ class SmsService
private readonly MessageBusInterface $bus,
private readonly SmsMessageTemplateRepository $messageTemplateRepo,
private readonly SmsTextResolver $textResolver,
private readonly LoggerInterface $logger,
) {
$this->providers = [
'kavenegar' => $kavenegar,
@@ -46,8 +48,12 @@ class SmsService
$message = $this->textResolver->resolve($tag, $vars);
// سیاست lookup-only: بدون الگوی کاوه‌نگار نباید با sms/send.json خام ارسال شود.
if ($kaveTemplate === null || $tokenMap === []) {
$this->dispatchAsync($mobile, $message, $provider, tag: $tag);
$this->logger->error(sprintf('SMS tag "%s" has no Kavenegar template/token_map; refusing raw send', $tag), [
'tag' => $tag, 'mobile' => $mobile,
]);
$this->logRepo->save(new SmsLog($mobile, $message, $provider, false, $tag));
return;
}
@@ -79,9 +85,15 @@ class SmsService
{
$provider = $this->resolveProvider($msg->provider);
$success = ($msg->templateCode !== null)
? $provider->sendTemplate($msg->mobile, $msg->templateCode, $msg->templateVars)
: $provider->send($msg->mobile, $msg->message);
// سیاست lookup-only: پیام بدون templateCode نباید با sms/send.json خام برود.
if ($msg->templateCode === null) {
$this->logger->error('SMS refused: no templateCode (lookup-only policy)', [
'mobile' => $msg->mobile, 'tag' => $msg->tag,
]);
$success = false;
} else {
$success = $provider->sendTemplate($msg->mobile, $msg->templateCode, $msg->templateVars);
}
$log = new SmsLog($msg->mobile, $msg->message, $provider->getName(), $success, $msg->tag);
if ($msg->templateUuid) $log->setTemplateUuid($msg->templateUuid);
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace App\Tests\Sms;
use App\Sms\Entity\SmsLog;
use App\Sms\Message\SendSmsMessage;
use App\Sms\Provider\KavehNegarProvider;
use App\Sms\Provider\RanginehProvider;
use App\Sms\Repository\SmsLogRepository;
use App\Sms\Repository\SmsMessageTemplateRepository;
use App\Sms\Service\SmsService;
use App\Sms\Service\SmsTextResolver;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
/**
* سیاست lookup-only: هیچ مسیری نباید به sms/send.json خام (provider->send) سقوط کند.
*/
class SmsServiceLookupOnlyTest extends TestCase
{
private KavehNegarProvider&MockObject $kavenegar;
private SmsLogRepository&MockObject $logRepo;
private MessageBusInterface&MockObject $bus;
private SmsMessageTemplateRepository&MockObject $messageTemplateRepo;
private SmsTextResolver&MockObject $textResolver;
private SmsService $service;
protected function setUp(): void
{
$this->kavenegar = $this->createMock(KavehNegarProvider::class);
$this->kavenegar->method('getName')->willReturn('kavenegar');
$this->logRepo = $this->createMock(SmsLogRepository::class);
$this->bus = $this->createMock(MessageBusInterface::class);
$this->messageTemplateRepo = $this->createMock(SmsMessageTemplateRepository::class);
$this->textResolver = $this->createMock(SmsTextResolver::class);
$this->service = new SmsService(
$this->kavenegar,
$this->createMock(RanginehProvider::class),
$this->logRepo,
$this->bus,
$this->messageTemplateRepo,
$this->textResolver,
$this->createMock(LoggerInterface::class),
);
}
public function testSendNowWithoutTemplateCodeRefusesRawSendAndLogsFailure(): void
{
$this->kavenegar->expects($this->never())->method('send');
$this->kavenegar->expects($this->never())->method('sendTemplate');
$captured = null;
$this->logRepo->expects($this->once())->method('save')
->willReturnCallback(function (SmsLog $log) use (&$captured) { $captured = $log; });
$msg = new SendSmsMessage('09120000000', 'hello', 'kavenegar', tag: SmsLog::TAG_GLOBAL);
$ok = $this->service->sendNow($msg);
$this->assertFalse($ok);
$this->assertNotNull($captured);
$this->assertFalse($captured->toArray()['success']);
}
public function testSendNowWithTemplateCodeUsesLookup(): void
{
$this->kavenegar->expects($this->never())->method('send');
$this->kavenegar->expects($this->once())->method('sendTemplate')
->with('09120000000', 'clinicpro-otp', ['token' => '1234'])
->willReturn(true);
$this->logRepo->expects($this->once())->method('save');
$msg = new SendSmsMessage(
'09120000000', 'code 1234', 'kavenegar',
templateVars: ['token' => '1234'], templateCode: 'clinicpro-otp', tag: SmsLog::TAG_OTP,
);
$this->assertTrue($this->service->sendNow($msg));
}
public function testDispatchTemplateForTagWithoutTemplateDoesNotEnqueueAndLogsFailure(): void
{
// TAG_GLOBAL در DEFAULTS نیست و در DB هم چیزی نداریم → باید رد شود، نه ارسال خام.
$this->messageTemplateRepo->method('findByTag')->willReturn(null);
$this->textResolver->method('resolve')->willReturn('any body');
$this->bus->expects($this->never())->method('dispatch');
$this->logRepo->expects($this->once())->method('save');
$this->service->dispatchTemplate(SmsLog::TAG_GLOBAL, '09120000000', ['x' => 'y']);
}
public function testDispatchTemplateForKnownTagEnqueuesWithTemplateCode(): void
{
// TAG_OTP در DEFAULTS الگو + token_map دارد → باید با templateCode به صف برود.
$this->messageTemplateRepo->method('findByTag')->willReturn(null);
$this->textResolver->method('resolve')->willReturn('کد شما: 1234');
$captured = null;
$this->bus->expects($this->once())->method('dispatch')
->willReturnCallback(function (SendSmsMessage $m) use (&$captured) {
$captured = $m;
return new Envelope($m);
});
$this->logRepo->expects($this->never())->method('save');
$this->service->dispatchTemplate(SmsLog::TAG_OTP, '09120000000', ['code' => '1234', 'site' => 'یزد']);
$this->assertNotNull($captured);
$this->assertSame('clinicpro-otp', $captured->templateCode);
$this->assertArrayHasKey('token', $captured->templateVars);
$this->assertSame('1234', $captured->templateVars['token']);
}
}