search) { $query->where(function($q) use ($request) { $q->where('code', 'like', "%{$request->search}%") ->orWhereHas('vendor', function($vq) use ($request) { $vq->where('name', 'like', "%{$request->search}%"); }); }); } // Filters if ($request->status && $request->status !== 'all') { $query->where('status', $request->status); } if ($request->warehouse_id && $request->warehouse_id !== 'all') { $query->where('warehouse_id', $request->warehouse_id); } // Sorting $sortField = $request->sort_field ?? 'id'; $sortDirection = $request->sort_direction ?? 'desc'; $allowedSortFields = ['id', 'code', 'status', 'total_amount', 'created_at', 'expected_delivery_date']; if (in_array($sortField, $allowedSortFields)) { $query->orderBy($sortField, $sortDirection); } $orders = $query->paginate(15)->withQueryString(); return Inertia::render('PurchaseOrder/Index', [ 'orders' => $orders, 'filters' => $request->only(['search', 'status', 'warehouse_id', 'sort_field', 'sort_direction']), 'warehouses' => Warehouse::all(['id', 'name']), ]); } public function create() { $vendors = Vendor::with(['products.baseUnit', 'products.largeUnit', 'products.purchaseUnit'])->get()->map(function ($vendor) { return [ 'id' => (string) $vendor->id, 'name' => $vendor->name, 'commonProducts' => $vendor->products->map(function ($product) { return [ 'productId' => (string) $product->id, 'productName' => $product->name, 'base_unit_id' => $product->base_unit_id, 'base_unit_name' => $product->baseUnit?->name, 'large_unit_id' => $product->large_unit_id, 'large_unit_name' => $product->largeUnit?->name, 'purchase_unit_id' => $product->purchase_unit_id, 'conversion_rate' => (float) $product->conversion_rate, 'lastPrice' => (float) ($product->pivot->last_price ?? 0), ]; }) ]; }); $warehouses = Warehouse::all()->map(function ($w) { return [ 'id' => (string) $w->id, 'name' => $w->name, ]; }); return Inertia::render('PurchaseOrder/Create', [ 'suppliers' => $vendors, 'warehouses' => $warehouses, ]); } public function store(Request $request) { $validated = $request->validate([ 'vendor_id' => 'required|exists:vendors,id', 'warehouse_id' => 'required|exists:warehouses,id', 'expected_delivery_date' => 'nullable|date', 'remark' => 'nullable|string', 'items' => 'required|array|min:1', 'items.*.productId' => 'required|exists:products,id', 'items.*.quantity' => 'required|numeric|min:0.01', 'items.*.unitPrice' => 'required|numeric|min:0', 'items.*.unitId' => 'nullable|exists:units,id', // 驗證單位ID ]); try { DB::beginTransaction(); // 生成單號:YYYYMMDD001 $today = now()->format('Ymd'); $lastOrder = PurchaseOrder::where('code', 'like', $today . '%') ->lockForUpdate() // 鎖定以避免並發衝突 ->orderBy('code', 'desc') ->first(); if ($lastOrder) { // 取得最後 3 碼序號並加 1 $lastSequence = intval(substr($lastOrder->code, -3)); $sequence = str_pad($lastSequence + 1, 3, '0', STR_PAD_LEFT); } else { $sequence = '001'; } $code = $today . $sequence; $totalAmount = 0; foreach ($validated['items'] as $item) { $totalAmount += $item['quantity'] * $item['unitPrice']; } // Simple tax calculation (e.g., 5%) $taxAmount = round($totalAmount * 0.05, 2); $grandTotal = $totalAmount + $taxAmount; // 確保有一個有效的使用者 ID $userId = auth()->id(); if (!$userId) { $user = \App\Models\User::first(); if (!$user) { $user = \App\Models\User::create([ 'name' => '系統管理員', 'email' => 'admin@example.com', 'password' => bcrypt('password'), ]); } $userId = $user->id; } $order = PurchaseOrder::create([ 'code' => $code, 'vendor_id' => $validated['vendor_id'], 'warehouse_id' => $validated['warehouse_id'], 'user_id' => $userId, 'status' => 'draft', 'expected_delivery_date' => $validated['expected_delivery_date'], 'total_amount' => $totalAmount, 'tax_amount' => $taxAmount, 'grand_total' => $grandTotal, 'remark' => $validated['remark'], ]); foreach ($validated['items'] as $item) { $order->items()->create([ 'product_id' => $item['productId'], 'quantity' => $item['quantity'], 'unit_id' => $item['unitId'] ?? null, // 儲存單位ID 'unit_price' => $item['unitPrice'], 'subtotal' => $item['quantity'] * $item['unitPrice'], ]); } DB::commit(); return redirect()->route('purchase-orders.index')->with('success', '採購單已成功建立'); } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['error' => '建立失敗:' . $e->getMessage()]); } } public function show($id) { $order = PurchaseOrder::with(['vendor', 'warehouse', 'user', 'items.product.baseUnit', 'items.product.largeUnit'])->findOrFail($id); $order->items->transform(function ($item) use ($order) { $product = $item->product; if ($product) { // 手動附加所有必要的屬性 $item->productId = (string) $product->id; $item->productName = $product->name; $item->base_unit_id = $product->base_unit_id; $item->base_unit_name = $product->baseUnit?->name; $item->large_unit_id = $product->large_unit_id; $item->large_unit_name = $product->largeUnit?->name; $item->purchase_unit_id = $product->purchase_unit_id; $item->conversion_rate = (float) $product->conversion_rate; // Fetch last price $lastPrice = DB::table('product_vendor') ->where('vendor_id', $order->vendor_id) ->where('product_id', $product->id) ->value('last_price'); $item->previousPrice = (float) ($lastPrice ?? 0); // 設定當前選中的單位 ID (from saved item) $item->unitId = $item->unit_id; // 決定 selectedUnit (用於 UI 顯示) if ($item->unitId && $item->large_unit_id && $item->unitId == $item->large_unit_id) { $item->selectedUnit = 'large'; } else { $item->selectedUnit = 'base'; } $item->unitPrice = (float) $item->unit_price; } return $item; }); return Inertia::render('PurchaseOrder/Show', [ 'order' => $order ]); } public function edit($id) { $order = PurchaseOrder::with(['items.product'])->findOrFail($id); $vendors = Vendor::with(['products.baseUnit', 'products.largeUnit', 'products.purchaseUnit'])->get()->map(function ($vendor) { return [ 'id' => (string) $vendor->id, 'name' => $vendor->name, 'commonProducts' => $vendor->products->map(function ($product) { return [ 'productId' => (string) $product->id, 'productName' => $product->name, 'base_unit_id' => $product->base_unit_id, 'base_unit_name' => $product->baseUnit?->name, 'large_unit_id' => $product->large_unit_id, 'large_unit_name' => $product->largeUnit?->name, 'purchase_unit_id' => $product->purchase_unit_id, 'conversion_rate' => (float) $product->conversion_rate, 'lastPrice' => (float) ($product->pivot->last_price ?? 0), ]; }) ]; }); $warehouses = Warehouse::all()->map(function ($w) { return [ 'id' => (string) $w->id, 'name' => $w->name, ]; }); // Transform items for frontend form // Transform items for frontend form $vendorId = $order->vendor_id; $order->items->transform(function ($item) use ($vendorId) { $product = $item->product; if ($product) { // 手動附加所有必要的屬性 $item->productId = (string) $product->id; $item->productName = $product->name; $item->base_unit_id = $product->base_unit_id; $item->base_unit_name = $product->baseUnit?->name; $item->large_unit_id = $product->large_unit_id; $item->large_unit_name = $product->largeUnit?->name; $item->conversion_rate = (float) $product->conversion_rate; // Fetch last price $lastPrice = DB::table('product_vendor') ->where('vendor_id', $vendorId) ->where('product_id', $product->id) ->value('last_price'); $item->previousPrice = (float) ($lastPrice ?? 0); // 設定當前選中的單位 ID $item->unitId = $item->unit_id; // 資料庫中的 unit_id // 決定 selectedUnit (用於 UI 狀態) if ($item->unitId && $item->large_unit_id && $item->unitId == $item->large_unit_id) { $item->selectedUnit = 'large'; } else { $item->selectedUnit = 'base'; } $item->unitPrice = (float) $item->unit_price; } return $item; }); return Inertia::render('PurchaseOrder/Create', [ 'order' => $order, 'suppliers' => $vendors, 'warehouses' => $warehouses, ]); } public function update(Request $request, $id) { $order = PurchaseOrder::findOrFail($id); $validated = $request->validate([ 'vendor_id' => 'required|exists:vendors,id', 'warehouse_id' => 'required|exists:warehouses,id', 'expected_delivery_date' => 'nullable|date', 'remark' => 'nullable|string', 'status' => 'required|string|in:draft,pending,processing,shipping,confirming,completed,cancelled', 'items' => 'required|array|min:1', 'items.*.productId' => 'required|exists:products,id', 'items.*.quantity' => 'required|numeric|min:0.01', 'items.*.unitPrice' => 'required|numeric|min:0', 'items.*.unitId' => 'nullable|exists:units,id', // 驗證單位ID ]); try { DB::beginTransaction(); $totalAmount = 0; foreach ($validated['items'] as $item) { $totalAmount += $item['quantity'] * $item['unitPrice']; } // Simple tax calculation (e.g., 5%) $taxAmount = round($totalAmount * 0.05, 2); $grandTotal = $totalAmount + $taxAmount; $order->update([ 'vendor_id' => $validated['vendor_id'], 'warehouse_id' => $validated['warehouse_id'], 'expected_delivery_date' => $validated['expected_delivery_date'], 'total_amount' => $totalAmount, 'tax_amount' => $taxAmount, 'grand_total' => $grandTotal, 'remark' => $validated['remark'], 'status' => $validated['status'], ]); // Sync items $order->items()->delete(); foreach ($validated['items'] as $item) { $order->items()->create([ 'product_id' => $item['productId'], 'quantity' => $item['quantity'], 'unit_id' => $item['unitId'] ?? null, // 儲存單位ID 'unit_price' => $item['unitPrice'], 'subtotal' => $item['quantity'] * $item['unitPrice'], ]); } DB::commit(); return redirect()->route('purchase-orders.index')->with('success', '採購單已更新'); } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['error' => '更新失敗:' . $e->getMessage()]); } } public function destroy($id) { try { DB::beginTransaction(); $order = PurchaseOrder::findOrFail($id); // Delete associated items first (due to FK constraints if not cascade) $order->items()->delete(); $order->delete(); DB::commit(); return redirect()->route('purchase-orders.index')->with('success', '採購單已刪除'); } catch (\Exception $e) { DB::rollBack(); return back()->withErrors(['error' => '刪除失敗:' . $e->getMessage()]); } } }