Pick candidate resources by whether they actually offer the service
findEligible matched on address, type and skills, so two devices of the same type were interchangeable even when only one of them performed the service. It now also consults the offering table. The filter is conditional on purpose: it only applies once the clinic has registered at least one resource for that service. Applying it unconditionally would leave every environment that has not filled the links in yet without a single free slot overnight — a silent outage caused by a feature they never opted into. When rows do exist but all are inactive the result is empty, which is the honest answer: nobody performs this right now. The service comes from the segment template rather than the root service. One appointment's plan can carry segments from several items, and "who can do this" is a per-item question. Five tests: the filter picking one of two identical devices, the no-rows passthrough, the all-inactive empty, the no-service-argument path still untouched, and the filter stacking with the skill filter. Suite 1277 green — including the 27 existing plan and availability tests, which is what proves the backward-compatible path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -222,10 +222,6 @@ final class AppointmentPlanBuilder
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* قانونی که نقشِ ناشناخته یا بیمنبع میخواهد **خطاست، نه بیاثر**: در سکوت رد
|
||||
* کردنش یعنی کلینیک فکر کند قانونش اجرا میشود در حالی که هیچوقت نشده.
|
||||
*/
|
||||
/** @return list<PlannedRequirement> */
|
||||
private function planRequirements(
|
||||
SegmentTemplate $template,
|
||||
@@ -278,7 +274,15 @@ final class AppointmentPlanBuilder
|
||||
SegmentTemplate $template,
|
||||
): array {
|
||||
$skillIds = $requirement->getSkill() === null ? [] : [(int) $requirement->getSkill()->getId()];
|
||||
$eligible = $this->resources->findEligible($address, $requirement->getResourceType(), $skillIds);
|
||||
|
||||
// سرویس از خودِ الگو میآید نه از سرویس ریشه: برنامهٔ یک نوبت میتواند بخشهایی
|
||||
// از چند آیتم داشته باشد، و «کدام منبع این را میدهد» برای هر آیتم جداست.
|
||||
$eligible = $this->resources->findEligible(
|
||||
$address,
|
||||
$requirement->getResourceType(),
|
||||
$skillIds,
|
||||
$template->getService(),
|
||||
);
|
||||
|
||||
if (!$requirement->requiresSameGender()) {
|
||||
return array_values($eligible);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Resource\Repository;
|
||||
|
||||
use App\ClinicService\Entity\ServiceItem;
|
||||
use App\Doctor\Entity\Doctor;
|
||||
use App\Doctor\Entity\DoctorAddress;
|
||||
use App\Branch\Entity\Room;
|
||||
@@ -16,8 +17,10 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
*/
|
||||
class ClinicResourceRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
public function __construct(
|
||||
ManagerRegistry $registry,
|
||||
private readonly ResourceServiceOfferingRepository $offerings,
|
||||
) {
|
||||
parent::__construct($registry, ClinicResource::class);
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@ class ClinicResourceRepository extends ServiceEntityRepository
|
||||
* @param int[] $skillIds خالی یعنی بدون شرط مهارت
|
||||
* @return ClinicResource[]
|
||||
*/
|
||||
public function findEligible(DoctorAddress $address, ResourceType $type, array $skillIds = []): array
|
||||
public function findEligible(DoctorAddress $address, ResourceType $type, array $skillIds = [], ?ServiceItem $service = null): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('r')
|
||||
->where('r.address = :address')
|
||||
@@ -114,6 +117,21 @@ class ClinicResourceRepository extends ServiceEntityRepository
|
||||
->setParameter('address', $address)
|
||||
->setParameter('type', $type);
|
||||
|
||||
/**
|
||||
* «کدام منبع این سرویس را میدهد» فقط وقتی فیلتر است که کلینیک جوابش را داده
|
||||
* باشد. اگر برای این سرویس هیچ ردیفی نباشد، فیلتر اعمال نمیشود — وگرنه هر
|
||||
* محیطی که هنوز رابطهها را پر نکرده، یکشبه بدون وقت آزاد میشد.
|
||||
*/
|
||||
if ($service !== null && $this->offerings->hasAnyFor($service)) {
|
||||
$resourceIds = $this->offerings->activeResourceIdsFor($service);
|
||||
|
||||
if ($resourceIds === []) {
|
||||
return []; // همه غیرفعالاند: «هیچکس این سرویس را نمیدهد»، نه «فیلتری نیست»
|
||||
}
|
||||
|
||||
$qb->andWhere('r.id IN (:offered)')->setParameter('offered', $resourceIds);
|
||||
}
|
||||
|
||||
if ($skillIds !== []) {
|
||||
$qb->join('r.skills', 'rs')
|
||||
->andWhere('rs.skill IN (:skills)')
|
||||
|
||||
Reference in New Issue
Block a user