perf(reports): read every resource's calendar in one batch, and close the owed tests

Writing the query-count test that task 14 owed showed the growth was real: one
resource cost 10 queries, six cost 33 — about five per resource, because the
available-minutes figure walked each resource's calendar on its own.

Holidays, tenant overrides and branch hours are identical for every resource in
a report, so they now load once outside the loop; shifts and exceptions load for
all resources in one query each. The batched path is a new method rather than a
change to rawAvailability, which the booking engine also calls. The test pins
the shape of the growth, not an exact count.

Also landed:

- app:segment:seed-templates with beauty, dental and physio presets. Building
  four segments and their requirements by hand is the first thing a new clinic
  must do and the most tedious; this gives them something to edit instead of an
  empty page. It refuses to touch a service that already has segments unless
  --force, and it will not invent resource types the tenant never defined.
- book-all is all-or-nothing, proven rather than asserted: with a calendar open
  one day a week and a 1-2 day protocol gap, session one finds a slot and
  session two cannot, and every session must come back planned.
- credit_refundable: false takes the credit back with a negative adjustment and
  deletes nothing — the ledger stays append-only.
- the segments editor has frontend tests, including that it sends back what the
  user sees and renders read-only without the permission.

useBranches now returns [] for a non-array payload instead of throwing
"branches.map is not a function" and taking the page down with it.

BookingLocationsScanTest built a Clinic around a Doctor loaded from a different
manager, which Doctrine treats as a new entity; it flushed fine most runs and
failed on cascade in others. It now loads the doctor from the same manager.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 14:22:22 +03:30
co-authored by Claude Opus 5
parent 92edd175cc
commit b3c331f0cb
20 changed files with 624 additions and 30 deletions
+45
View File
@@ -435,6 +435,51 @@ class AppointmentPlanTest extends ApiTestCase
);
}
/**
* ⭐ الگوی نمونه نقطهٔ شروع است، نه پیکربندی نهایی — و **بازنویسی خاموش نمی‌کند**.
*/
public function testSeedingCreatesAStarterSetAndRefusesToOverwrite(): void
{
[$user, $section, $address] = $this->clinicWithBranch();
$service = $this->service($section, 'لیزر', 20);
$room = $this->resourceType($address, 'room', 'اتاق');
$operator = $this->resourceType($address, 'operator', 'اپراتور');
$this->resource($user, $address, $room, 'اتاق ۱');
$this->resource($user, $address, $operator, 'اپراتور ۱');
$seed = static::getContainer()->get(\App\Appointment\Plan\Command\SeedSegmentTemplatesCommand::class);
$run = static function (array $input) use ($seed): array {
$tester = new \Symfony\Component\Console\Tester\CommandTester($seed);
$tester->execute($input);
return [$tester->getStatusCode(), $tester->getDisplay()];
};
[$code] = $run(['--service' => $service->getUuid(), '--preset' => 'beauty']);
self::assertSame(0, $code);
$plan = $this->preview($user, $service, $address);
self::assertCount(5, $plan['data']['segments']);
self::assertSame('آماده‌سازی', $plan['data']['segments'][0]['name']);
// بخشِ تمیزکاری بدون حضور بیمار است — همان چیزی که گزارش بهره‌وری با آن کار می‌کند.
$cleanup = end($plan['data']['segments']);
self::assertFalse($cleanup['patient_present']);
// اجرای دوباره بدون `--force` دست به چیزی نمی‌زند.
[, $display] = $run(['--service' => $service->getUuid(), '--preset' => 'dental']);
self::assertStringContainsString('--force', $display);
$unchanged = $this->preview($user, $service, $address);
self::assertCount(5, $unchanged['data']['segments'], 'بدون --force بازنویسی نمی‌شود');
[, $forced] = $run(['--service' => $service->getUuid(), '--preset' => 'dental', '--force' => true]);
self::assertStringNotContainsString('--force', $forced);
$replaced = $this->preview($user, $service, $address);
self::assertCount(3, $replaced['data']['segments']);
}
public function testForeignServiceIsNotFound(): void
{
[$user, , $address] = $this->clinicWithBranch();