کلاس => دلیل */ public const ENTITIES = [ // دادهٔ مرجع مشترک بین همهٔ محیط‌ها \App\Location\Entity\Province::class => 'تقسیمات کشوری', \App\Location\Entity\City::class => 'تقسیمات کشوری', \App\Specialty\Entity\Specialty::class => 'تاکسونومی سراسری تخصص‌ها', \App\PracticeDomain\Entity\PracticeDomain::class => 'تاکسونومی سراسری حوزهٔ فعالیت؛ محیط آن را انتخاب می‌کند نه مالکش، و کدش لنگرِ پیاده‌سازی‌های TreatmentWorkflow است', \App\DoctorService\Entity\DoctorService::class => 'تاکسونومی سراسری خدمات، وابسته به تخصص نه به محیط', \App\Insurance\Entity\Insurance::class => 'فهرست بیمه‌های کشور', \App\Insurance\Entity\InsuranceCoverageDefault::class => 'پیش‌فرض پوشش بیمه در سطح کشور؛ هر محیط با TenantInsurance بازنویسی‌اش می‌کند', \App\Tag\Entity\Tag::class => 'تاکسونومی سراسری برچسب — قرینهٔ per-tenant آن TenantTag است', \App\Config\Entity\SiteConfig::class => 'تنظیمات کل سامانه', \App\Config\Entity\TaxRateHistory::class => 'نرخ مالیات کشور', \App\Subscription\Entity\SubscriptionPlan::class => 'پلن‌های فروش، مشترک بین همهٔ مشتریان', \App\Subscription\Entity\SubscriptionPeriod::class => 'دوره‌های قیمتی همان پلن‌ها', \App\Sms\Entity\SmsTemplate::class => 'قالب پیامک سامانه', \App\Sms\Entity\SmsMessageTemplate::class => 'متن پیامک سامانه', \App\Sms\Entity\SmsLog::class => 'لاگ ارسال؛ فقط شماره و قالب دارد، مالک ندارد', \App\Shared\Logging\AppLog::class => 'لاگ سراسری برنامه', \App\Blog\Entity\Blog::class => 'محتوای عمومی مارکت‌پلیس', \App\Resource\Entity\NationalHoliday::class => 'تعطیلات رسمی کشور؛ per محیط کردنش معنای «کشوری» را از بین می‌برد — محیطی که خلافش کار می‌کند TenantHolidayOverride می‌زند', // هویت — یک شخص می‌تواند در چند محیط حضور داشته باشد \App\Auth\Entity\User::class => 'هویت سراسری؛ رابطهٔ بیمار با محیط از patient_records می‌آید', \App\UserProfile\Entity\UserProfile::class => 'پروفایل شخص، نه دادهٔ محیط', \App\Auth\Entity\PreRegistration::class => 'پیش‌ثبت‌نام، هنوز به هیچ محیطی وصل نیست', \App\Auth\Entity\UserActiveContext::class => 'خودش تعیین‌کنندهٔ محیط است؛ فیلتر کردنش حلقه می‌سازد', // خودِ محیط‌ها \App\Doctor\Entity\Doctor::class => 'خودش یک محیط است', \App\Clinic\Entity\Clinic::class => 'خودش یک محیط است', // دادهٔ عمومی مارکت‌پلیس دربارهٔ پزشک — بیمار می‌نویسد، نه محیط \App\Rating\Entity\Comment::class => 'نظر عمومی بیمار روی پروفایل پزشک', \App\Rating\Entity\Like::class => 'لایک عمومی روی همان نظرها', \App\Rating\Entity\Rate::class => 'امتیاز عمومی بیمار به پزشک', \App\Representation\Entity\Representation::class => 'نمایندهٔ فروش؛ بالادستِ محیط‌هاست نه داخل یکی', // رابطهٔ بین دو محیط — فیلتر کردن با یک طرف، طرف دیگر را کور می‌کند \App\Clinic\Entity\ClinicDoctorPermission::class => 'مجوز پزشکِ عضو در یک کلینیک؛ هویتش خودِ جفت (کلینیک، پزشک) است', \App\ClinicInvitation\Entity\ClinicDoctorInvitation::class => 'دعوت کلینیک از پزشک؛ پیش از عضویت هر دو طرف باید ببینندش', \App\Doctor\Entity\DoctorClaimRequest::class => 'درخواست تصاحب پروفایل پزشک؛ متقاضی هنوز صاحب محیط نیست', // دادهٔ خودِ پزشک، مستقل از اینکه در کدام کلینیک کار می‌کند \App\Doctor\Entity\DoctorAddress::class => 'آدرس‌های پزشک؛ در همهٔ محیط‌های او یکسان است', \App\Insurance\Entity\DoctorInsurance::class => 'بیمه‌های طرف قرارداد خودِ پزشک', // استثنای مستندشده در فاز ۲ \App\Appointment\Entity\Holiday::class => 'clinic=NULL یعنی «همهٔ محیط‌ها»، نه «مطب شخصی» — جفت tenant این را نمی‌تواند بیان کند', // کیف پولِ شخص — استثنای مستندشده در فاز ۶ \App\Settlement\Entity\WalletTransaction::class => 'کیف پول خودِ شخص است نه محیط: موجودی از مجموع credit−debitِ همان کاربر مشتق می‌شود و payment_id تهی‌پذیر است، پس تفکیک به محیط، موجودی را بی‌معنا می‌کند', \App\Settlement\Entity\Settlement::class => 'برداشت از همان کیف پولِ شخصی (SettlementController موجودی را با getWalletBalance(user) می‌سنجد)؛ محیط ندارد چون کیف پول ندارد', ]; /** * فرزندان aggregate: ستون tenant ندارند و محیط را از ریشه به ارث می‌برند. * ریشه صریح اعلام می‌شود چون بعضی‌شان با FK اسکالر وصل‌اند (نه رابطهٔ Doctrine) * و از metadata قابل استنتاج نیستند. * * ⚠️ TenantFilter روی این‌ها اعمال نمی‌شود. کوئری مستقیم روی این جدول‌ها بدون * JOIN به ریشه، cross-tenant است — همیشه از ریشه شروع کن. * * فرزندی که uuidش از خودِ درخواست می‌آید نباید اینجا بماند: چنین جست‌وجویی * ذاتاً بی‌لنگر است و تور ایمنی ندارد. هشت مورد از این دست جفت محیط خودشان را * گرفتند (فاز ۸)؛ باقی‌مانده‌ها فقط از ریشه پیمایش می‌شوند. * * @var array فرزند => ریشه */ public const AGGREGATE_CHILDREN = [ \App\Appointment\Entity\AppointmentEvent::class => \App\Appointment\Entity\Appointment::class, \App\Appointment\Plan\Entity\SegmentRequirement::class => \App\Appointment\Plan\Entity\SegmentTemplate::class, \App\Appointment\Booking\Entity\AppointmentSegment::class => \App\Appointment\Entity\Appointment::class, // سطل‌ها فقط قیدِ یکتاییِ ردیف اشغال‌اند و هیچ‌وقت مستقیم پرس‌وجو نمی‌شوند. \App\Appointment\Booking\Entity\OccupancyBucket::class => \App\Appointment\Availability\Entity\ResourceOccupancy::class, // ریشه‌هاشان خودشان جفت محیط دارند (برخلاف پروندهٔ branch_working_hours در // تسک ۰۱)، پس ارث‌بری اینجا واقعی است. هیچ‌کدام uuid از request نمی‌گیرند: // تنها راهشان PUT روی /resource/{uuid}/skills و /resource-pool/{uuid}/members است. \App\Resource\Entity\ResourceSkill::class => \App\Resource\Entity\ClinicResource::class, // «این منبع این سرویس را می‌دهد» جزئی از تعریف همان منبع است، نه دادهٔ مستقل. \App\Resource\Entity\ResourceServiceOffering::class => \App\Resource\Entity\ClinicResource::class, \App\Resource\Entity\ResourceCalendar::class => \App\Resource\Entity\ClinicResource::class, \App\Resource\Entity\ResourcePoolMember::class => \App\Resource\Entity\ResourcePool::class, \App\Patient\Entity\SessionAuditLog::class => \App\Patient\Entity\PatientSession::class, \App\Patient\Entity\SessionConsumable::class => \App\Patient\Entity\PatientSession::class, \App\Patient\Entity\SessionService::class => \App\Patient\Entity\PatientSession::class, \App\ClinicService\Entity\ServiceItemAuditLog::class => \App\ClinicService\Entity\ServiceItem::class, \App\ClinicService\Entity\ServiceItemConsumable::class => \App\ClinicService\Entity\ServiceItem::class, \App\ClinicService\Entity\ItemGroupMember::class => \App\ClinicService\Entity\ItemGroup::class, // «طول درمانِ این سرویس» جزئی از تعریف همان سرویس است و uuid خودش هیچ‌جا از // درخواست نمی‌آید — تنها راه رسیدن به آن، uuid سرویس است. \App\Treatment\Entity\TreatmentProtocol::class => \App\ClinicService\Entity\ServiceItem::class, \App\Treatment\Entity\TreatmentProtocolStep::class => \App\Treatment\Entity\TreatmentProtocol::class, \App\Treatment\Entity\TreatmentProtocolStaff::class => \App\Treatment\Entity\TreatmentProtocol::class, // فهرست نواحیِ یک پرونده فقط از خودِ پرونده پیمایش می‌شود و uuidش هیچ‌جا از // درخواست نمی‌آید. جلسه و رکورد ناحیه برعکس‌اند — پنل پرسنل uuidشان را مستقیم // می‌فرستد — پس آن دو جفت محیط خودشان را دارند، نه اینجا. \App\Treatment\Entity\TreatmentCaseArea::class => \App\Treatment\Entity\TreatmentCase::class, // یال «این دسته شامل آن دسته است» جزئی از تعریف دستهٔ والد است؛ هر دو سرِ یال // در یک محیط‌اند و سازندهٔ یال همین را اجبار می‌کند. \App\ClinicService\Entity\CatalogCategoryInclude::class => \App\ClinicService\Entity\CatalogCategory::class, \App\Billing\Entity\ClaimItem::class => \App\Billing\Entity\Claim::class, \App\Billing\Entity\ClaimStatusLog::class => \App\Billing\Entity\Claim::class, \App\Billing\Entity\InvoiceItem::class => \App\Billing\Entity\Invoice::class, \App\Inventory\Entity\InventoryPackageItem::class => \App\Inventory\Entity\InventoryPackage::class, \App\Insurance\Entity\TenantInsuranceCategoryCoverage::class => \App\Insurance\Entity\TenantInsurance::class, \App\Insurance\Entity\TenantServiceCoverage::class => \App\Insurance\Entity\TenantInsurance::class, \App\Sms\Entity\SmsWalletTransaction::class => \App\Sms\Entity\SmsWallet::class, \App\Payment\Entity\PaymentLog::class => \App\Payment\Entity\Payment::class, \App\Settlement\Entity\FinancialBreakdown::class => \App\Payment\Entity\Payment::class, \App\Secretary\Entity\SecretaryEarning::class => \App\Settlement\Entity\FinancialBreakdown::class, ]; /** * بدهیِ طبقه‌بندی: کلاسی که هنوز تصمیمی درباره‌اش گرفته نشده. * * فاز ۶ آخرین هشت موردش را تعیین تکلیف کرد و اکنون خالی است. خالی بماند: * هر افزوده‌ای یعنی جدولی بیرون از هر تضمینی مانده. اگر تصمیم واقعاً به تحلیل * بیشتری نیاز دارد، همین‌جا با دلیل ثبتش کن — ولی TenantSchemaCoverageTest * خالی‌بودن را اجبار می‌کند تا این کار بی‌صدا نگذرد. * * @var array */ public const DEFERRED = []; }