Files
hub/app/Http/Controllers/Seller/Processing/MaterialLotController.php
kelly c7d6ee5e21 feat: multiple UI/UX improvements and case-insensitive search
- Refactor New Quote page to enterprise data-entry layout (2-column, dense)
- Add Payment Terms dropdown (COD, NET 15, NET 30, NET 60)
- Fix sidebar menu active states and route names
- Fix brand filter badge visibility on brands page
- Remove company_name references (use business instead)
- Polish Promotions page layout
- Fix double-click issue on sidebar menu collapse
- Make all searches case-insensitive (like -> ilike for PostgreSQL)
2025-12-14 15:36:00 -07:00

167 lines
5.5 KiB
PHP

<?php
namespace App\Http\Controllers\Seller\Processing;
use App\Http\Controllers\Controller;
use App\Models\Business;
use App\Models\Processing\ProcMaterialLot;
use App\Models\Processing\ProcMaterialMovement;
use App\Models\Product;
use Illuminate\Http\Request;
class MaterialLotController extends Controller
{
public function index(Request $request, Business $business)
{
$query = ProcMaterialLot::forBusiness($business->id)
->with('product')
->orderByDesc('created_at');
if ($request->filled('status')) {
$query->where('status', $request->status);
}
if ($request->filled('material_type')) {
$query->where('material_type', $request->material_type);
}
if ($request->filled('search')) {
$query->where('lot_number', 'ilike', '%'.$request->search.'%');
}
$materialLots = $query->paginate(25);
return view('seller.processing.materials.index', compact('business', 'materialLots'));
}
public function create(Request $request, Business $business)
{
$products = Product::where('business_id', $business->id)->get();
return view('seller.processing.materials.create', compact('business', 'products'));
}
public function store(Request $request, Business $business)
{
$validated = $request->validate([
'lot_number' => 'required|string|max:100',
'product_id' => 'nullable|exists:products,id',
'material_type' => 'required|in:biomass,intermediate',
'weight_available' => 'required|numeric|min:0',
'uom' => 'nullable|string|max:10',
]);
$validated['business_id'] = $business->id;
$validated['status'] = 'available';
$validated['manufacture_source'] = 'manual_entry';
$validated['uom'] = $validated['uom'] ?? 'g';
$lot = ProcMaterialLot::create($validated);
// Record initial receive movement
ProcMaterialMovement::create([
'business_id' => $business->id,
'material_lot_id' => $lot->id,
'movement_type' => 'receive',
'quantity' => $validated['weight_available'],
'uom' => $validated['uom'],
'reason' => 'Initial receipt (manual entry)',
]);
return redirect()
->route('seller.processing.materials.index', $business)
->with('success', 'Material lot created successfully.');
}
public function show(Request $request, Business $business, ProcMaterialLot $material)
{
$this->authorizeForBusiness($material, $business);
$material->load(['product', 'movements' => fn ($q) => $q->orderByDesc('created_at')]);
return view('seller.processing.materials.show', compact('business', 'material'));
}
public function edit(Request $request, Business $business, ProcMaterialLot $material)
{
$this->authorizeForBusiness($material, $business);
$products = Product::where('business_id', $business->id)->get();
return view('seller.processing.materials.edit', compact('business', 'material', 'products'));
}
public function update(Request $request, Business $business, ProcMaterialLot $material)
{
$this->authorizeForBusiness($material, $business);
$validated = $request->validate([
'lot_number' => 'required|string|max:100',
'product_id' => 'nullable|exists:products,id',
'material_type' => 'required|in:biomass,intermediate',
'status' => 'required|in:available,allocated,on_hold,quarantined',
]);
$material->update($validated);
return redirect()
->route('seller.processing.materials.show', [$business, $material])
->with('success', 'Material lot updated successfully.');
}
public function destroy(Request $request, Business $business, ProcMaterialLot $material)
{
$this->authorizeForBusiness($material, $business);
$material->delete();
return redirect()
->route('seller.processing.materials.index', $business)
->with('success', 'Material lot deleted successfully.');
}
/**
* Adjust material lot quantity.
*/
public function adjust(Request $request, Business $business, ProcMaterialLot $material)
{
$this->authorizeForBusiness($material, $business);
$validated = $request->validate([
'adjustment_type' => 'required|in:add,subtract',
'quantity' => 'required|numeric|min:0',
'reason' => 'required|string|max:500',
]);
$adjustQty = $validated['adjustment_type'] === 'add'
? $validated['quantity']
: -$validated['quantity'];
$newWeight = $material->weight_available + $adjustQty;
if ($newWeight < 0) {
return back()->with('error', 'Cannot adjust below zero.');
}
$material->update(['weight_available' => $newWeight]);
ProcMaterialMovement::create([
'business_id' => $business->id,
'material_lot_id' => $material->id,
'movement_type' => 'adjust',
'quantity' => $adjustQty,
'uom' => $material->uom,
'reason' => $validated['reason'],
]);
return back()->with('success', 'Adjustment recorded successfully.');
}
protected function authorizeForBusiness(ProcMaterialLot $lot, Business $business): void
{
if ($lot->business_id !== $business->id) {
abort(403, 'Unauthorized access to this material lot.');
}
}
}