Files
clinicpro/tests/Shared/TenantLookupInventoryTest.php
T
hamedandClaude Opus 5 4fe0c4f9bf refactor(pricing): make the service the only price source
Price lists, annual tariffs and per-branch price overrides each answered
"what does this service cost?" differently, so a single date could carry
several answers and nobody could say which one was right. Price now lives
only on ServiceItem.price_rials, edited from the services page.

- drop PriceList/PriceListItem, their repositories and the seven
  /api/v1/price-list(s) endpoints; PricingController keeps only quote and
  the appointment price snapshot
- drop Tariff, TariffRepository, TariffService and the two
  /service-items/{uuid}/tariffs endpoints; creating or repricing a service
  no longer upserts a current-year tariff
- drop price_rials from ServiceBranchOverride; the entity stays for its
  duration columns, which DurationCalculator and ServiceSelectionValidator
  still read
- InvoiceService reads the item price directly
- PricingEngine collapses to a single source; breakdown.sources always
  reports service_item, keeping the response contract intact
- remove the price-lists admin page, its route and settings-menu entry, the
  tariff modal and the service detail tariffs tab; useAppointmentInvoice
  moves to its own hook file

Migration drops price_lists, price_list_items, service_tariffs and the
override price column.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 18:00:48 +03:30

110 lines
5.4 KiB
PHP

<?php
namespace App\Tests\Shared;
use PHPUnit\Framework\TestCase;
/**
* فهرستِ بازبینی‌شدهٔ جست‌وجوهای «uuid از درخواست → موجودیت محیط‌دار».
*
* سه نشتی این فاز همگی همین شکل را داشتند و هیچ‌کدام در repository نبودند؛ در
* کنترلر و سرویس بودند، جایی که uuid از بدنه/کوئری می‌آید و کسی محیط را نمی‌سنجد.
* پس گارد هم باید همان‌جا باشد، نه روی DQLهای repository.
*
* قاعده: عدد هر فایل = تعداد findByUuid روی repositoryهای محیط‌دار در آن فایل، که
* همگی بازبینی شده‌اند. اگر عدد عوض شود یعنی جست‌وجوی تازه‌ای اضافه شده و باید
* ثابت شود محیطش بررسی می‌شود — با ownsRecord/ownsSection/ownedItem،
* TenantOwnershipChecker، یا لنگرزدن به ریشه‌ای که خودش بررسی شده.
*
* @see \App\Shared\Tenant\TenantOwnershipChecker
* @see docs/architecture/tenancy.md
*/
class TenantLookupInventoryTest extends TestCase
{
/** repositoryهایی که موجودیت محیط‌دار یا فرزند aggregate برمی‌گردانند. */
private const TENANT_REPOSITORY_VARIABLES = [
'itemRepo', 'serviceItemRepo', 'sectionRepo', 'staffRepo',
'noteRepo', 'attachmentRepo', 'callRepo', 'messageRepo', 'medicalRepo',
'sessionRepo', 'sessionPaymentRepo', 'recordRepo',
'tenantInsuranceRepo', 'tenantTagRepo', 'packageRepo', 'ruleRepo',
];
/**
* فایل => تعداد جست‌وجوی بازبینی‌شده.
*
* @var array<string, int>
*/
private const REVIEWED = [
// AppointmentController دیگر جست‌وجوی بازبینی‌نشده ندارد: assertServicesMatchContext
// حذف شد و مسیر ثبت نوبت هم مثل مسیر اسلات از ServiceBookingCalculator رد می‌شود،
// که خودش مالکیت محیط را می‌سنجد و پایین‌تر شمرده می‌شود.
// TenantOwnershipChecker::belongsToPair() روی هر uuid، پیش از هر بررسی دیگر
'src/Appointment/Service/ServiceBookingCalculator.php' => 2,
// TenantOwnershipChecker روی بخش/سرویس/پرسنل و فهرست سرویس‌ها
'src/Appointment/Controller/MyAppointmentsController.php' => 1,
// ownsSession / getEntityType روی صورتحساب، پرونده و مطالبه
'src/Billing/Controller/BillingController.php' => 3,
// ownsSection ×۹ و مقایسهٔ مستقیم جفت ×۳ (پکیج، کالا، پرسنل)
'src/ClinicService/Controller/ClinicServiceController.php' => 8,
'src/Discount/Controller/DiscountController.php' => 1,
// قرارداد بیمه با جفت، و سرویس با getSection()->getEntityType()
'src/Insurance/Controller/InsuranceController.php' => 5,
// ownedItem() / ownedPackage()
'src/Inventory/Controller/InventoryController.php' => 2,
'src/Inventory/Service/InventoryService.php' => 1,
// ownsRecord ×۳۳ و دو پرداختِ لنگرخورده به مراجعهٔ بررسی‌شده
'src/Patient/Controller/PatientController.php' => 35,
// TenantOwnershipChecker در هر سه حلقه (قیمت‌گذاری، ساخت، ویرایش) + کالا
'src/Patient/Service/PatientService.php' => 5,
// ownsStaff()
'src/Staff/Controller/StaffController.php' => 2,
];
private function projectDir(): string
{
return dirname(__DIR__, 2);
}
/** @return array<string, int> */
private function countLookups(): array
{
$pattern = '/->(' . implode('|', self::TENANT_REPOSITORY_VARIABLES) . ')->findByUuid\(/';
$counts = [];
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->projectDir() . '/src', \FilesystemIterator::SKIP_DOTS),
);
foreach ($files as $file) {
if ($file->getExtension() !== 'php') {
continue;
}
$found = preg_match_all($pattern, (string) file_get_contents($file->getPathname()));
if ($found > 0) {
$relative = str_replace($this->projectDir() . '/', '', $file->getPathname());
$counts[$relative] = $found;
}
}
ksort($counts);
return $counts;
}
public function testNoUnreviewedTenantLookupExists(): void
{
$actual = $this->countLookups();
$expected = self::REVIEWED;
ksort($expected);
self::assertSame($expected, $actual, <<<'MSG'
جست‌وجوی «uuid از درخواست موجودیت محیط‌دار» تغییر کرده.
برای هر مورد تازه ثابت کن محیطش بررسی می‌شود (ownsRecord/ownsSection/
ownedItem، TenantOwnershipChecker، یا لنگر به ریشهٔ بررسی‌شده) و بعد عدد
همان فایل را در REVIEWED به‌روز کن. اگر عدد کم شده، فقط عدد را کم کن.
MSG);
}
}