You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 line
3.0KB

  1. <?php
  2. namespace App\Http\Controllers\Items;
  3. use App\Models\Item;
  4. use App\Models\Location;
  5. use App\Models\Stock;
  6. use App\Models\StockMovement;
  7. use Framework\Routing\Router;
  8. class AdjustStockController
  9. {
  10. public function handle(Router $router)
  11. {
  12. if (!session()->has('user_id')) {
  13. return redirect($router->route('show-register-form'));
  14. }
  15. secure();
  16. $parameters = $router->current()->parameters();
  17. $item = Item::find((int) $parameters['item']);
  18. if (!$item) {
  19. return redirect($router->route('show-items'));
  20. }
  21. $data = validate($_POST, [
  22. 'location_id' => ['required'],
  23. // not ['required'] - PHP's empty("0") is true, which would reject
  24. // a legitimate "set this to zero" adjustment before it reaches
  25. // the ctype_digit check below (which does allow zero).
  26. 'quantity' => [],
  27. 'notes' => [],
  28. ], 'adjust_errors');
  29. if (!isset($data['quantity']) || !ctype_digit((string) $data['quantity'])) {
  30. session()->put('adjust_errors', [
  31. 'quantity' => ['quantity must be a whole number, 0 or greater'],
  32. ]);
  33. return redirect($router->route('show-adjust-stock-form', ['item' => $item->id]));
  34. }
  35. $location = Location::find((int) $data['location_id']);
  36. if (!$location) {
  37. return redirect($router->route('show-adjust-stock-form', ['item' => $item->id]));
  38. }
  39. $stock = Stock::forItemAtLocation($item->id, $location->id);
  40. $newQuantity = (int) $data['quantity'];
  41. $change = $newQuantity - $stock->quantity;
  42. $stock->quantity = $newQuantity;
  43. $stock->save();
  44. $movement = new StockMovement();
  45. $movement->item_id = $item->id;
  46. $movement->location_id = $location->id;
  47. $movement->type = 'adjustment';
  48. $movement->quantity_change = $change;
  49. $movement->quantity_after = $newQuantity;
  50. $movement->notes = $data['notes'] ?? '';
  51. $movement->user_id = session('user_id');
  52. $movement->save();
  53. $this->alertIfLowStock($item);
  54. return redirect($router->route('view-item', ['item' => $item->id]));
  55. }
  56. private function alertIfLowStock(Item $item): void
  57. {
  58. if ($item->reorder_level <= 0) {
  59. return;
  60. }
  61. $total = array_sum(array_map(
  62. fn($row) => $row->quantity,
  63. Stock::where('item_id', $item->id)->all(),
  64. ));
  65. if ($total > $item->reorder_level) {
  66. return;
  67. }
  68. app('queue')->push(function ($itemName, $total, $reorderLevel) {
  69. app('email')
  70. ->to(config('warehouse.alert_email'))
  71. ->subject("Low stock: {$itemName}")
  72. ->text("{$itemName} is at {$total} units, at or below its reorder level of {$reorderLevel}.")
  73. ->send();
  74. }, $item->name, $total, $item->reorder_level);
  75. }
  76. }

Powered by TurnKey Linux.