This commit completes the PR #53 integration by optimizing the test suite: Performance Improvements: - Migrated 25 test files from RefreshDatabase to DatabaseTransactions - Tests now run in 12.69s parallel (previously 30s+) - Increased PostgreSQL max_locks_per_transaction to 256 for parallel testing Test Infrastructure Changes: - Disabled broadcasting in tests (set to null) to avoid Reverb connectivity issues - Reverted 5 integration tests to RefreshDatabase (CheckoutFlowTest + 4 Service tests) that require full schema recreation due to complex fixtures PR #53 Integration Fixes: - Added Product.inStock() scope for inventory queries - Fixed ProductFactory to create InventoryItem records instead of using removed columns - Added Department.products() relationship - Fixed FulfillmentWorkOrderController view variables - Fixed orders migration location_id foreign key constraint - Created seller-layout component wrapper All 146 tests now pass with optimal performance.
123 lines
4.3 KiB
PHP
123 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers\Seller;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Department;
|
|
use App\Models\FulfillmentWorkOrder;
|
|
use App\Models\User;
|
|
use App\Services\FulfillmentWorkOrderService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\View\View;
|
|
|
|
class FulfillmentWorkOrderController extends Controller
|
|
{
|
|
public function __construct(
|
|
private FulfillmentWorkOrderService $workOrderService
|
|
) {
|
|
//
|
|
}
|
|
|
|
/**
|
|
* List work orders for seller's business
|
|
*/
|
|
public function index(Request $request): View
|
|
{
|
|
$business = $request->user()->businesses()->first();
|
|
|
|
$workOrders = FulfillmentWorkOrder::whereHas('order', function ($query) use ($business) {
|
|
$query->where('seller_business_id', $business->id);
|
|
})
|
|
->with(['order', 'pickingTickets'])
|
|
->orderBy('created_at', 'desc')
|
|
->paginate(20);
|
|
|
|
// Calculate stats for the view
|
|
$stats = [
|
|
'total' => FulfillmentWorkOrder::whereHas('order', fn ($q) => $q->where('seller_business_id', $business->id))->count(),
|
|
'pending' => FulfillmentWorkOrder::whereHas('order', fn ($q) => $q->where('seller_business_id', $business->id))->where('status', 'pending')->count(),
|
|
'in_progress' => FulfillmentWorkOrder::whereHas('order', fn ($q) => $q->where('seller_business_id', $business->id))->where('status', 'in_progress')->count(),
|
|
'overdue' => 0, // FulfillmentWorkOrder doesn't have overdue scope yet
|
|
'due_soon' => 0, // FulfillmentWorkOrder doesn't have due_soon scope yet
|
|
];
|
|
|
|
// Get departments for filtering
|
|
$departments = Department::where('business_id', $business->id)->active()->get();
|
|
|
|
// Get users for filtering
|
|
$users = User::whereHas('businesses', fn ($q) => $q->where('businesses.id', $business->id))->get();
|
|
|
|
return view('seller.work-orders.index', compact('workOrders', 'business', 'stats', 'departments', 'users'));
|
|
}
|
|
|
|
/**
|
|
* Show work order details with picking tickets
|
|
*/
|
|
public function show(Request $request, FulfillmentWorkOrder $workOrder): View
|
|
{
|
|
$business = $request->user()->businesses()->first();
|
|
|
|
// Ensure work order belongs to seller's business
|
|
if ($workOrder->order->seller_business_id !== $business->id) {
|
|
abort(403, 'Unauthorized access to work order');
|
|
}
|
|
|
|
$workOrder->load(['order.items.product', 'pickingTickets.department', 'pickingTickets.items']);
|
|
|
|
return view('seller.work-orders.show', compact('workOrder', 'business'));
|
|
}
|
|
|
|
/**
|
|
* Assign picker to a picking ticket
|
|
*/
|
|
public function assignPicker(Request $request, FulfillmentWorkOrder $workOrder)
|
|
{
|
|
$validated = $request->validate([
|
|
'ticket_id' => 'required|exists:picking_tickets,id',
|
|
'picker_id' => 'required|exists:users,id',
|
|
]);
|
|
|
|
$business = $request->user()->businesses()->first();
|
|
|
|
if ($workOrder->order->seller_business_id !== $business->id) {
|
|
abort(403);
|
|
}
|
|
|
|
$ticket = $workOrder->pickingTickets()->findOrFail($validated['ticket_id']);
|
|
$picker = \App\Models\User::findOrFail($validated['picker_id']);
|
|
|
|
$this->workOrderService->assignPicker($ticket, $picker);
|
|
|
|
return redirect()
|
|
->route('seller.work-orders.show', $workOrder)
|
|
->with('success', 'Picker assigned successfully');
|
|
}
|
|
|
|
/**
|
|
* Start picking the order
|
|
*/
|
|
public function startPicking(Request $request, FulfillmentWorkOrder $workOrder)
|
|
{
|
|
$business = $request->user()->businesses()->first();
|
|
|
|
// Verify authorization
|
|
if ($workOrder->order->seller_business_id !== $business->id) {
|
|
abort(403, 'Unauthorized access to work order');
|
|
}
|
|
|
|
try {
|
|
$workOrder->order->startPicking();
|
|
|
|
return redirect()
|
|
->route('seller.business.orders.show', [$business->slug, $workOrder->order])
|
|
->with('success', 'Order started! You can now begin picking items.');
|
|
} catch (\Exception $e) {
|
|
return redirect()
|
|
->route('seller.business.orders.show', [$business->slug, $workOrder->order])
|
|
->with('error', 'Could not start order: '.$e->getMessage());
|
|
}
|
|
}
|
|
}
|