| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- <?php
- declare(strict_types=1);
- namespace App\Http\Controllers;
- use App\Http\Requests\ReserveStockRequest;
- use App\Models\CommonCatalogItem;
- use App\Models\StockOrder;
- use App\Models\StockReservation;
- use App\Models\User;
- use App\Services\StockInventoryService;
- use DomainException;
- use Illuminate\Contracts\View\View;
- use Illuminate\Database\Eloquent\Builder;
- use Illuminate\Http\RedirectResponse;
- use Illuminate\Http\Request;
- class StockController extends Controller
- {
- public function __construct(private readonly StockInventoryService $inventoryService) {}
- public function index(Request $request): View
- {
- $search = trim((string) $request->query('search', ''));
- $kind = trim((string) $request->query('kind', ''));
- $status = trim((string) $request->query('status', ''));
- $perPage = $this->perPage($request);
- $items = CommonCatalogItem::query()
- ->whereHas('stockOrders')
- ->with(['imageFile', 'latestStockOrder'])
- ->withSum([
- 'stockOrders as physical_quantity' => fn (Builder $query) => $query->where('status', StockOrder::STATUS_IN_STOCK),
- ], 'available_quantity')
- ->withSum([
- 'stockReservations as reserved_quantity' => fn (Builder $query) => $query->active(),
- ], 'quantity')
- ->withSum([
- 'stockOrders as ordered_stock_quantity' => fn (Builder $query) => $query->where('status', StockOrder::STATUS_ORDERED),
- ], 'ordered_quantity')
- ->when($search !== '', function (Builder $query) use ($search): void {
- $query->where(function (Builder $query) use ($search): void {
- $query->where('article', 'like', "%{$search}%")
- ->orWhere('calculator_name', 'like', "%{$search}%")
- ->orWhere('kind', 'like', "%{$search}%");
- });
- })
- ->when($kind !== '', fn (Builder $query) => $query->where('kind', $kind))
- ->when(array_key_exists($status, StockOrder::STATUS_NAMES), fn (Builder $query) => $query
- ->whereHas('stockOrders', fn (Builder $query) => $query->where('status', $status)))
- ->orderBy('article')
- ->paginate($perPage)
- ->withQueryString();
- return view('stock.index', [
- 'active' => 'stock',
- 'items' => $items,
- 'search' => $search,
- 'kind' => $kind,
- 'status' => $status,
- 'kinds' => CommonCatalogItem::query()
- ->whereHas('stockOrders')
- ->whereNotNull('kind')
- ->where('kind', '<>', '')
- ->distinct()
- ->orderBy('kind')
- ->pluck('kind'),
- 'statuses' => StockOrder::STATUS_NAMES,
- 'per_page' => $perPage,
- ]);
- }
- public function show(CommonCatalogItem $item): View
- {
- $item->load([
- 'imageFile',
- 'stockOrders' => fn ($query) => $query
- ->withSum(['reservations as active_reservations_sum_quantity' => fn ($query) => $query->active()], 'quantity')
- ->orderByDesc('created_at'),
- 'stockReservations' => fn ($query) => $query
- ->with(['manager', 'order'])
- ->orderByDesc('created_at'),
- 'stockMovements' => fn ($query) => $query
- ->with(['user', 'order'])
- ->orderByDesc('created_at'),
- ]);
- return view('stock.show', [
- 'active' => 'stock',
- 'item' => $item,
- 'summary' => $this->inventoryService->summary($item),
- 'managers' => User::query()->orderBy('name')->get(),
- ]);
- }
- public function reserve(ReserveStockRequest $request, CommonCatalogItem $item): RedirectResponse
- {
- /** @var User $actor */
- $actor = $request->user();
- $manager = User::query()->findOrFail((int) $request->validated('manager_id'));
- try {
- $this->inventoryService->reserve(
- $item,
- $manager,
- (int) $request->validated('quantity'),
- $request->validated('note'),
- $actor,
- );
- return back()->with('success', 'Позиция забронирована.');
- } catch (DomainException $exception) {
- return back()->withErrors(['quantity' => $exception->getMessage()])->withInput();
- }
- }
- public function issue(Request $request, StockReservation $reservation): RedirectResponse
- {
- /** @var User $actor */
- $actor = $request->user();
- try {
- $this->inventoryService->issue($reservation, $actor, $request->string('note')->toString());
- return back()->with('success', 'Бронь списана.');
- } catch (DomainException $exception) {
- return back()->with('error', $exception->getMessage());
- }
- }
- public function cancel(Request $request, StockReservation $reservation): RedirectResponse
- {
- /** @var User $actor */
- $actor = $request->user();
- try {
- $this->inventoryService->cancel($reservation, $actor, $request->string('note')->toString());
- return back()->with('success', 'Бронь отменена.');
- } catch (DomainException $exception) {
- return back()->with('error', $exception->getMessage());
- }
- }
- private function perPage(Request $request): int
- {
- $allowed = [50, 100, 200, 500, 2000];
- $perPage = (int) $request->query('per_page', session('per_page', 50));
- $perPage = in_array($perPage, $allowed, true) ? $perPage : 50;
- session(['per_page' => $perPage]);
- return $perPage;
- }
- }
|