feat: Implement category and unit selection for inventory items

- Added a new 'category' field to the InventoryItem entity and updated the database schema.
- Replaced free-text input for 'unit' and 'category' with select dropdowns in the AddItemModal.
- Introduced a new API endpoint to fetch metadata for units and categories.
- Updated inventory filtering logic to use the new 'category' field instead of 'consumable'.
- Enhanced validation for item creation and updates to ensure valid unit and category values.
- Updated tests to cover new functionality and ensure proper validation.
This commit is contained in:
hamed
2026-07-15 14:32:29 +03:30
parent defa0db023
commit 3e5dee0ad5
12 changed files with 513 additions and 36 deletions
+50 -6
View File
@@ -86,18 +86,62 @@ class InventoryApiTest extends ApiTestCase
);
}
public function testCategoriesReturnsDistinctConsumables(): void
public function testCategoriesReturnsDistinctUsedCategories(): void
{
[$user] = $this->doctorUser();
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'a', 'consumable' => 'جراحی']);
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'b', 'consumable' => 'جراحی']);
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'c', 'consumable' => 'دندان']);
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'a', 'category' => 'دارو']);
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'b', 'category' => 'دارو']);
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'c', 'category' => 'لوازم آزمایشگاهی']);
$cats = $this->authJson('GET', '/api/v1/inventory-categories', $user);
self::assertSame(200, $this->responseCode());
self::assertCount(2, $cats['data']);
self::assertContains('جراحی', $cats['data']);
self::assertContains('دندان', $cats['data']);
self::assertContains('دارو', $cats['data']);
self::assertContains('لوازم آزمایشگاهی', $cats['data']);
}
public function testMetaReturnsUnitAndCategoryLists(): void
{
[$user] = $this->doctorUser();
$meta = $this->authJson('GET', '/api/v1/inventory-meta', $user);
self::assertSame(200, $this->responseCode());
self::assertContains('عدد', $meta['data']['units']);
self::assertContains('سی‌سی', $meta['data']['units']);
self::assertContains('دارو', $meta['data']['categories']);
self::assertContains('سایر', $meta['data']['categories']);
}
public function testCreateStoresValidUnitAndCategory(): void
{
[$user] = $this->doctorUser();
$item = $this->authJson('POST', '/api/v1/inventory-item', $user, [
'name' => 'سرنگ', 'unit' => 'بسته', 'category' => 'لوازم مصرفی و تزریقات',
]);
self::assertSame(201, $this->responseCode());
self::assertSame('بسته', $item['data']['unit']);
self::assertSame('لوازم مصرفی و تزریقات', $item['data']['category']);
}
public function testCreateRejectsInvalidUnit(): void
{
[$user] = $this->doctorUser();
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'x', 'unit' => 'واحد‌جعلی']);
self::assertSame(422, $this->responseCode());
}
public function testCreateRejectsInvalidCategory(): void
{
[$user] = $this->doctorUser();
$this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'x', 'category' => 'دسته‌جعلی']);
self::assertSame(422, $this->responseCode());
}
public function testEmptyUnitFallsBackToDefault(): void
{
[$user] = $this->doctorUser();
$item = $this->authJson('POST', '/api/v1/inventory-item', $user, ['name' => 'x', 'unit' => '']);
self::assertSame(201, $this->responseCode());
self::assertSame('عدد', $item['data']['unit']);
}
public function testCannotTouchAnotherTenantsItem(): void