Browse Source

feat(inventory): publish item operation notifications

softword 2 months ago
parent
commit
394c78c7e1
3 changed files with 28 additions and 4 deletions
  1. 3 3
      specs/api-routes.md
  2. 1 1
      specs/role-and-inventory.md
  3. 24 0
      src/Core/Api/InventoryController.php

+ 3 - 3
specs/api-routes.md

@@ -21,9 +21,9 @@ All routes are registered in `src/Core/Api/Api.php`. Responses are JSON with `Ca
 | PUT | `/api/v1/admin/roles/@id` | `RoleController::update` | Updates role metadata without changing its ID. | Role ID `1` + CSRF |
 | DELETE | `/api/v1/admin/roles/@id` | `RoleController::delete` | Deletes a role only when it is not ID `1` and is not assigned to any user. | Role ID `1` + CSRF |
 | GET | `/api/v1/inventory/items` | `InventoryController::index` | Lists Inventory Monitor demo item DTOs. | Role ID `2` |
-| POST | `/api/v1/inventory/items` | `InventoryController::create` | Creates an Inventory Monitor demo item. | Role ID `2` + CSRF |
-| PUT | `/api/v1/inventory/items/@id` | `InventoryController::update` | Updates an Inventory Monitor demo item. | Role ID `2` + CSRF |
-| DELETE | `/api/v1/inventory/items/@id` | `InventoryController::delete` | Deletes an Inventory Monitor demo item. | Role ID `2` + CSRF |
+| POST | `/api/v1/inventory/items` | `InventoryController::create` | Creates an Inventory Monitor demo item and publishes a shared notification. | Role ID `2` + CSRF |
+| PUT | `/api/v1/inventory/items/@id` | `InventoryController::update` | Updates an Inventory Monitor demo item and publishes a shared notification. | Role ID `2` + CSRF |
+| DELETE | `/api/v1/inventory/items/@id` | `InventoryController::delete` | Deletes an Inventory Monitor demo item and publishes a shared notification. | Role ID `2` + CSRF |
 | GET | `/api/v1/notifications` | `NotificationController::index` | Returns notifications grouped by caption. | Authenticated |
 | DELETE | `/api/v1/notifications` | `NotificationController::delete` | Clears every notification. | Authenticated + CSRF |
 | DELETE | `/api/v1/notifications/@code` | `NotificationController::delete` | Deletes notifications matching a non-negative integer code. | Authenticated + CSRF |

+ 1 - 1
specs/role-and-inventory.md

@@ -31,7 +31,7 @@ The `inventory_items` table stores:
 | `quantity` | Non-negative integer quantity. |
 | `reorder_level` | Non-negative integer threshold. |
 
-Inventory endpoints return safe item DTOs only. Role ID `2` is required for reads and mutations; role ID `1` does not implicitly grant inventory access. Mutations require the session CSRF token. Duplicate SKUs and invalid numeric values are rejected with safe JSON errors.
+Inventory endpoints return safe item DTOs only. Role ID `2` is required for reads and mutations; role ID `1` does not implicitly grant inventory access. Mutations require the session CSRF token. Duplicate SKUs and invalid numeric values are rejected with safe JSON errors. Each successful create, update, or delete publishes a shared notification linked to `/inventory`; notification persistence is non-blocking and does not change the successful mutation result.
 
 ## API and Navigation
 

+ 24 - 0
src/Core/Api/InventoryController.php

@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace Core\Api;
 
 use Core\System\InventoryItem;
+use Core\System\Notifications;
 use PDOException;
 use Throwable;
 
@@ -21,6 +22,10 @@ final class InventoryController extends Controller
     private const HTTP_NOT_FOUND = 404;
     private const HTTP_CONFLICT = 409;
     private const HTTP_UNPROCESSABLE_ENTITY = 422;
+    private const INVENTORY_PATH = '/inventory';
+    private const INVENTORY_ITEM_ADDED_NOTIFICATION = 'Inventory item added.';
+    private const INVENTORY_ITEM_UPDATED_NOTIFICATION = 'Inventory item updated.';
+    private const INVENTORY_ITEM_DELETED_NOTIFICATION = 'Inventory item deleted.';
 
     /**
      * List all inventory items as safe DTOs.
@@ -75,6 +80,7 @@ final class InventoryController extends Controller
             return;
         }
 
+        $this->sendInventoryNotification(self::INVENTORY_ITEM_ADDED_NOTIFICATION);
         $this->respond($this->toDto($item), self::HTTP_CREATED);
     }
 
@@ -125,6 +131,7 @@ final class InventoryController extends Controller
             return;
         }
 
+        $this->sendInventoryNotification(self::INVENTORY_ITEM_UPDATED_NOTIFICATION);
         $this->respond($this->toDto($item), self::HTTP_OK);
     }
 
@@ -167,9 +174,26 @@ final class InventoryController extends Controller
             return;
         }
 
+        $this->sendInventoryNotification(self::INVENTORY_ITEM_DELETED_NOTIFICATION);
         $this->sendJson(['deleted' => true], self::HTTP_OK);
     }
 
+    /**
+     * Records an inventory operation in the shared notification feed.
+     *
+     * @param string $caption Notification message describing the completed operation.
+     * @return void
+     * @side-effects Persists a notification and logs a non-blocking persistence failure.
+     */
+    private function sendInventoryNotification(string $caption): void
+    {
+        try {
+            (new Notifications())->notify(Notifications::FB_GENERIC, $caption, self::INVENTORY_PATH);
+        } catch (Throwable $exception) {
+            error_log(sprintf('Inventory notification persistence failed: %s', $exception->getMessage()));
+        }
+    }
+
     /**
      * Sends an inventory API response with the standard JSON and cache headers.
      *