Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
StockTransferController
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 2
30
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 store
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Http\Controllers\admin;
4
5use App\Http\Controllers\Controller;
6use App\Models\admin\Product;
7use App\Models\ProductVariation;
8use App\Models\StockTransfer;
9use App\Models\StockTransferItem;
10use App\Services\Location\BranchContext;
11use App\Services\Stock\LocationInventoryService;
12use Illuminate\Http\RedirectResponse;
13use Illuminate\Http\Request;
14use Illuminate\Support\Facades\DB;
15
16class StockTransferController extends Controller
17{
18    public function index()
19    {
20        $stockItems = Product::query()->with('variations.attributeValues.attribute')->orderBy('name')->get()->flatMap(function (Product $product) {
21            if ($product->variations->isEmpty()) {
22                return ['p:' . $product->id => $product->name];
23            }
24
25            return $product->variations->mapWithKeys(function ($variation) use ($product) {
26                $options = $variation->attributeValues->map(fn ($value) => ($value->attribute->name ?? 'Option') . ': ' . $value->value)->implode(' / ');
27
28                return ['v:' . $variation->id => $product->name . ($options ? ' — ' . $options : '')];
29            });
30        });
31
32        return view('admin.locations.transfers', [
33            'transfers' => StockTransfer::with(['fromWarehouse', 'toWarehouse', 'items'])->latest()->paginate(30),
34            'warehouses' => app(BranchContext::class)->warehouses()->active()->pluck('name', 'id'),
35            'stockItems' => $stockItems,
36        ]);
37    }
38
39    public function store(Request $request, LocationInventoryService $inventory): RedirectResponse
40    {
41        $data = $request->validate([
42            'from_warehouse_id' => ['required', 'exists:warehouses,id', 'different:to_warehouse_id'],
43            'to_warehouse_id' => ['required', 'exists:warehouses,id'], 'stock_item' => ['required', 'regex:/^[pv]:[0-9]+$/'],
44            'quantity' => ['required', 'integer', 'min:1'], 'note' => ['nullable', 'string', 'max:1000'],
45        ]);
46        [$kind, $id] = explode(':', $data['stock_item']);
47        app(BranchContext::class)->ensureWarehouse((int) $data['from_warehouse_id']);
48        app(BranchContext::class)->ensureWarehouse((int) $data['to_warehouse_id']);
49        $variation = $kind === 'v' ? ProductVariation::with('product')->findOrFail($id) : null;
50        $product = $variation?->product ?? Product::findOrFail($id);
51
52        DB::transaction(function () use ($data, $inventory, $request, $product, $variation) {
53            $transfer = StockTransfer::create([
54                'reference' => 'TR-' . now()->format('YmdHis') . '-' . strtoupper(substr(uniqid(), -4)),
55                'from_warehouse_id' => $data['from_warehouse_id'], 'to_warehouse_id' => $data['to_warehouse_id'],
56                'status' => 'completed', 'note' => $data['note'] ?? null, 'created_by' => $request->user()->id,
57            ]);
58            StockTransferItem::create(['stock_transfer_id' => $transfer->id, 'product_id' => $product->id, 'variation_id' => $variation?->id, 'quantity' => $data['quantity']]);
59            $inventory->transfer($data['from_warehouse_id'], $data['to_warehouse_id'], $product, $variation, $data['quantity'], $transfer);
60        });
61
62        return back()->with('success', 'Stock transferred.');
63    }
64}