diff --git a/app/Http/Requests/App/InventoryStoreRequest.php b/app/Http/Requests/App/InventoryStoreRequest.php index 7b1a88a..b6a5669 100644 --- a/app/Http/Requests/App/InventoryStoreRequest.php +++ b/app/Http/Requests/App/InventoryStoreRequest.php @@ -21,6 +21,7 @@ public function rules(): array return [ // Campos de Inventory 'name' => ['required', 'string', 'max:100'], + 'key_sat' => ['nullable', 'integer', 'max:9'], 'sku' => ['nullable', 'string', 'max:50', 'unique:inventories,sku'], 'barcode' => ['nullable', 'string', 'unique:inventories,barcode'], 'category_id' => ['required', 'exists:categories,id'], diff --git a/app/Http/Requests/App/InventoryUpdateRequest.php b/app/Http/Requests/App/InventoryUpdateRequest.php index 2168aa3..5b87efd 100644 --- a/app/Http/Requests/App/InventoryUpdateRequest.php +++ b/app/Http/Requests/App/InventoryUpdateRequest.php @@ -23,6 +23,7 @@ public function rules(): array return [ // Campos de Inventory 'name' => ['nullable', 'string', 'max:100'], + 'key_sat' => ['nullable', 'string', 'max:9'], 'sku' => ['nullable', 'string', 'max:50'], 'barcode' => ['nullable', 'string', 'unique:inventories,barcode,' . $inventoryId], 'category_id' => ['nullable', 'exists:categories,id'], diff --git a/app/Models/Inventory.php b/app/Models/Inventory.php index a59513b..f66d299 100644 --- a/app/Models/Inventory.php +++ b/app/Models/Inventory.php @@ -22,6 +22,7 @@ class Inventory extends Model 'category_id', 'unit_of_measure_id', 'name', + 'key_sat', 'sku', 'barcode', 'track_serials', diff --git a/app/Models/InventoryWarehouse.php b/app/Models/InventoryWarehouse.php index dee1aa4..c353551 100644 --- a/app/Models/InventoryWarehouse.php +++ b/app/Models/InventoryWarehouse.php @@ -1,7 +1,10 @@ + * + * @version 1.0.0 + */ +class InventoryWarehouseObserver +{ + /** + * Roles que reciben alertas de stock bajo + */ + protected array $notifiableRoles = ['developer', 'admin']; + + /** + * Umbral por defecto cuando min_stock no está configurado + */ + protected int $defaultThreshold = 5; + + /** + * Detectar cuando el stock cruza el umbral mínimo hacia abajo + */ + public function updated(InventoryWarehouse $inventoryWarehouse): void + { + $previousStock = (float) $inventoryWarehouse->getOriginal('stock'); + $currentStock = (float) $inventoryWarehouse->stock; + $threshold = (float) ($inventoryWarehouse->min_stock ?? $this->defaultThreshold); + + // Solo notificar si el stock CRUZÓ el umbral (no en cada update mientras ya esté bajo) + if ($previousStock >= $threshold && $currentStock < $threshold) { + $this->notifyLowStock($inventoryWarehouse, $currentStock); + } + } + + /** + * Enviar notificación de stock bajo a usuarios con rol relevante + */ + protected function notifyLowStock(InventoryWarehouse $inventoryWarehouse, float $currentStock): void + { + $inventoryWarehouse->load('inventory', 'warehouse'); + + $productName = $inventoryWarehouse->inventory?->name ?? 'Producto desconocido'; + $warehouseName = $inventoryWarehouse->warehouse?->name ?? 'Almacén desconocido'; + + $users = User::role($this->notifiableRoles)->get(); + + foreach ($users as $user) { + $user->notify(new UserNotification( + title: 'Stock bajo: ' . $productName, + description: "El producto \"{$productName}\" tiene {$currentStock} unidad(es) disponible(s) en \"{$warehouseName}\".", + type: 'warning', + timeout: 30, + save: true, + )); + } + } +} diff --git a/app/Services/ProductService.php b/app/Services/ProductService.php index 87ae245..d276df3 100644 --- a/app/Services/ProductService.php +++ b/app/Services/ProductService.php @@ -11,6 +11,7 @@ public function createProduct(array $data) return DB::transaction(function () use ($data) { $inventory = Inventory::create([ 'name' => $data['name'], + 'key_sat' => $data['key_sat'] ?? null, 'sku' => $data['sku'], 'barcode' => $data['barcode'] ?? null, 'category_id' => $data['category_id'], @@ -35,6 +36,7 @@ public function updateProduct(Inventory $inventory, array $data) // Actualizar campos de Inventory solo si están presentes $inventoryData = array_filter([ 'name' => $data['name'] ?? null, + 'key_sat' => $data['key_sat'] ?? null, 'sku' => $data['sku'] ?? null, 'barcode' => $data['barcode'] ?? null, 'category_id' => $data['category_id'] ?? null, diff --git a/database/migrations/2026_02_23_131314_add_key_sat_to_inventories_table.php b/database/migrations/2026_02_23_131314_add_key_sat_to_inventories_table.php new file mode 100644 index 0000000..457588d --- /dev/null +++ b/database/migrations/2026_02_23_131314_add_key_sat_to_inventories_table.php @@ -0,0 +1,28 @@ +integer('key_sat')->nullable()->after('name'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('inventories', function (Blueprint $table) { + $table->dropColumn('key_sat'); + }); + } +};