feat: add support for individual consumable items in service items
- Introduced `consumables` field in `ServiceItem` to allow multiple individual items alongside inventory packages. - Created `ServiceItemConsumable` entity to manage individual consumable items linked to a service. - Updated `ServiceItemController` to handle CRUD operations for consumables. - Enhanced `ServiceDetailPage` and `ServiceItemFormModal` to display and manage consumables. - Added tests to ensure functionality for adding, updating, and validating consumables. - Updated API documentation to reflect changes in service item structure and consumables.
This commit is contained in:
@@ -70,6 +70,14 @@ class ServiceItem
|
||||
#[ORM\Column(name: 'inventory_package_id', type: 'integer', nullable: true)]
|
||||
private ?int $inventoryPackageId = null;
|
||||
|
||||
/**
|
||||
* اقلام کالای تکیِ این خدمت — مستقل از پکیج و قابل استفاده همزمان با آن.
|
||||
*
|
||||
* @var Collection<int, ServiceItemConsumable>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'serviceItem', targetEntity: ServiceItemConsumable::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
private Collection $consumables;
|
||||
|
||||
#[ORM\Column(name: 'created_at', type: 'integer')]
|
||||
private int $createdAt;
|
||||
|
||||
@@ -85,6 +93,51 @@ class ServiceItem
|
||||
$this->createdAt = time();
|
||||
$this->updatedAt = time();
|
||||
$this->staffMembers = new ArrayCollection();
|
||||
$this->consumables = new ArrayCollection();
|
||||
}
|
||||
|
||||
/** @return Collection<int, ServiceItemConsumable> */
|
||||
public function getConsumables(): Collection
|
||||
{
|
||||
// Doctrine بدون constructor هیدریت میکند؛ از property تایپشده محافظت کن.
|
||||
return $this->consumables ??= new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* جایگزینی کامل اقلام تکی. کلید تطبیق، خودِ InventoryItem است تا ردیف بدون تغییر
|
||||
* حذف و دوباره ساخته نشود.
|
||||
*
|
||||
* @param array<int, array{item: \App\Inventory\Entity\InventoryItem, amount: int}> $lines
|
||||
*/
|
||||
public function replaceConsumables(array $lines): self
|
||||
{
|
||||
$existing = [];
|
||||
foreach ($this->getConsumables() as $consumable) {
|
||||
$existing[$consumable->getItem()->getId()] = $consumable;
|
||||
}
|
||||
|
||||
$keep = [];
|
||||
foreach ($lines as $line) {
|
||||
$itemId = $line['item']->getId();
|
||||
$keep[] = $itemId;
|
||||
|
||||
if (isset($existing[$itemId])) {
|
||||
$existing[$itemId]->setAmount($line['amount']);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->getConsumables()->add((new ServiceItemConsumable($line['item'], $line['amount']))->setServiceItem($this));
|
||||
}
|
||||
|
||||
foreach ($existing as $itemId => $consumable) {
|
||||
if (!in_array($itemId, $keep, true)) {
|
||||
$this->getConsumables()->removeElement($consumable);
|
||||
}
|
||||
}
|
||||
|
||||
$this->updatedAt = time();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): ?int { return $this->id; }
|
||||
@@ -170,6 +223,10 @@ class ServiceItem
|
||||
'duration_minutes' => $this->durationMinutes,
|
||||
'bookable' => $this->bookable,
|
||||
'inventory_package_id' => $this->inventoryPackageId,
|
||||
'consumables' => array_map(
|
||||
fn(ServiceItemConsumable $c) => $c->toArray(),
|
||||
$this->getConsumables()->toArray()
|
||||
),
|
||||
'created_at' => $this->createdAt,
|
||||
'updated_at' => $this->updatedAt,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user