Record which resource an appointment was booked for, and freeze its numbers
An appointment could say which services it was for but not which resource performed them, so a booking on laser #2 was indistinguishable from one on laser #1. Both columns are nullable: the appointments that already exist have no resource and the migration must not break them. resource_id is not a duplicate of resource_occupancy. Occupancy records what was held and when — including rooms and devices held for a single segment. This column records what the appointment is *for*, which is what the panel lists and what the patient chose. The option is kept separately from service_item because duration and price resolve from the resource+service+option triple; without knowing the option, the stored number cannot be explained later. Tests: the resource and option survive a round-trip, stored minutes come from the resolver rather than the service default (15 where the service says 30), raising the tariff afterwards leaves the earlier snapshot at 8M, and an appointment with no resource still serialises with nulls instead of failing. Suite 1290 green, phpstan at its 14-error baseline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,27 @@ class Appointment
|
||||
#[ORM\JoinColumn(name: 'service_item_id', nullable: true, onDelete: 'SET NULL')]
|
||||
private ?\App\ClinicService\Entity\ServiceItem $serviceItem = null;
|
||||
|
||||
/**
|
||||
* منبعی که این نوبت رویش رزرو شده — دستگاه، اتاق، یا خودِ پزشک بهعنوان منبع.
|
||||
*
|
||||
* تهیپذیر چون نوبتهای پیش از مدل منبعمحور منبعی ندارند و migration نباید آنها
|
||||
* را بشکند. اشغالِ واقعیِ منابع همچنان در `resource_occupancy` است؛ این ستون
|
||||
* میگوید نوبت **برای** کدام منبع گرفته شده، نه اینکه چه چیزهایی اشغال شدهاند.
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: \App\Resource\Entity\ClinicResource::class)]
|
||||
#[ORM\JoinColumn(name: 'resource_id', nullable: true, onDelete: 'SET NULL')]
|
||||
private ?\App\Resource\Entity\ClinicResource $resource = null;
|
||||
|
||||
/**
|
||||
* گزینهٔ سرویس («لیزر پا» زیر «لیزر») — همان `ServiceItem` عضو گروه.
|
||||
*
|
||||
* جدا از `serviceItem` نگه داشته میشود چون مدت و قیمت از ترکیب منبع+سرویس+گزینه
|
||||
* حل میشوند و بدون دانستن گزینه، بازتولید همان عدد ممکن نیست.
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: \App\ClinicService\Entity\ServiceItem::class)]
|
||||
#[ORM\JoinColumn(name: 'service_option_item_id', nullable: true, onDelete: 'SET NULL')]
|
||||
private ?\App\ClinicService\Entity\ServiceItem $serviceOptionItem = null;
|
||||
|
||||
/** سرویسهای نوبت — امکان انتخاب چند سرویس. serviceItem بالا همان سرویسِ اول است. */
|
||||
#[ORM\ManyToMany(targetEntity: \App\ClinicService\Entity\ServiceItem::class)]
|
||||
#[ORM\JoinTable(name: 'appointment_service_items')]
|
||||
@@ -296,6 +317,26 @@ class Appointment
|
||||
public function getServiceSection(): ?\App\ClinicService\Entity\ServiceSection { return $this->serviceSection; }
|
||||
public function getServiceItem(): ?\App\ClinicService\Entity\ServiceItem { return $this->serviceItem; }
|
||||
|
||||
public function getResource(): ?\App\Resource\Entity\ClinicResource { return $this->resource; }
|
||||
|
||||
public function setResource(?\App\Resource\Entity\ClinicResource $v): self
|
||||
{
|
||||
$this->resource = $v;
|
||||
$this->updatedAt = time();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getServiceOptionItem(): ?\App\ClinicService\Entity\ServiceItem { return $this->serviceOptionItem; }
|
||||
|
||||
public function setServiceOptionItem(?\App\ClinicService\Entity\ServiceItem $v): self
|
||||
{
|
||||
$this->serviceOptionItem = $v;
|
||||
$this->updatedAt = time();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int,\App\ClinicService\Entity\ServiceItem> */
|
||||
public function getServiceItems(): Collection { return $this->serviceItems; }
|
||||
|
||||
@@ -512,6 +553,16 @@ class Appointment
|
||||
'insurance_base_id' => $this->insuranceBaseId,
|
||||
'insurance_supplementary_id' => $this->insuranceSupplementaryId,
|
||||
'is_reserve' => $this->isReserve,
|
||||
// نوبتهای پیش از مدل منبعمحور منبع ندارند؛ کلاینت باید با null کنار بیاید.
|
||||
'resource' => $this->resource === null ? null : [
|
||||
'uuid' => $this->resource->getUuid(),
|
||||
'name' => $this->resource->getName(),
|
||||
'type' => $this->resource->getType()->getCode(),
|
||||
],
|
||||
'service_option' => $this->serviceOptionItem === null ? null : [
|
||||
'uuid' => $this->serviceOptionItem->getUuid(),
|
||||
'name' => $this->serviceOptionItem->getName(),
|
||||
],
|
||||
// فقط در حالت نوبتدهی سرویسی پر میشوند؛ در حالت اسلاتی null.
|
||||
'service_total_minutes' => $this->serviceTotalMinutes,
|
||||
'service_buffer_minutes' => $this->serviceBufferMinutes,
|
||||
|
||||
Reference in New Issue
Block a user