goodsReceiptService = $goodsReceiptService; $this->inventoryService = $inventoryService; $this->procurementService = $procurementService; } public function index(Request $request) { $query = GoodsReceipt::query() ->select(['id', 'code', 'type', 'warehouse_id', 'vendor_id', 'received_date', 'status', 'created_at']) ->with(['warehouse']) ->withSum('items', 'total_amount'); // 關鍵字搜尋(單號) if ($request->filled('search')) { $search = $request->input('search'); $query->where('code', 'like', "%{$search}%"); } // 狀態篩選 if ($request->filled('status') && $request->input('status') !== 'all') { $query->where('status', $request->input('status')); } // 倉庫篩選 if ($request->filled('warehouse_id') && $request->input('warehouse_id') !== 'all') { $query->where('warehouse_id', $request->input('warehouse_id')); } // 日期範圍篩選 if ($request->filled('date_start')) { $query->whereDate('received_date', '>=', $request->input('date_start')); } if ($request->filled('date_end')) { $query->whereDate('received_date', '<=', $request->input('date_end')); } // 每頁筆數 $perPage = $request->input('per_page', 10); $receipts = $query->orderBy('created_at', 'desc') ->paginate($perPage) ->withQueryString(); // Manual Hydration for Vendors (Cross-Module) $vendorIds = collect($receipts->items())->pluck('vendor_id')->unique()->filter()->toArray(); $vendors = $this->procurementService->getVendorsByIds($vendorIds)->keyBy('id'); $receipts->getCollection()->transform(function ($receipt) use ($vendors) { $receipt->vendor = $vendors->get($receipt->vendor_id); return $receipt; }); // 取得倉庫列表用於篩選 $warehouses = $this->inventoryService->getAllWarehouses(); return Inertia::render('Inventory/GoodsReceipt/Index', [ 'receipts' => $receipts, 'filters' => $request->only(['search', 'status', 'warehouse_id', 'date_start', 'date_end', 'per_page']), 'warehouses' => $warehouses, ]); } public function show($id) { $receipt = GoodsReceipt::with([ 'warehouse', 'items.product.category', 'items.product.baseUnit' ])->findOrFail($id); // Manual Hydration for Vendor (Cross-Module) if ($receipt->vendor_id) { $receipt->vendor = $this->procurementService->getVendorsByIds([$receipt->vendor_id])->first(); } // 手動計算統計資訊 (如果 Model 沒有定義對應的 Attribute) $receipt->items_sum_total_amount = $receipt->items->sum('total_amount'); return Inertia::render('Inventory/GoodsReceipt/Show', [ 'receipt' => $receipt ]); } public function create() { // 取得待進貨的採購單列表(用於標準採購類型選擇) $pendingPOs = $this->procurementService->getPendingPurchaseOrders(); // 提取所有產品 ID 以便跨模組水和資料 $productIds = $pendingPOs->flatMap(fn($po) => $po->items->pluck('product_id'))->unique()->filter()->toArray(); $products = $this->inventoryService->getProductsByIds($productIds)->keyBy('id'); // 處理採購單資料,計算剩餘可收貨數量 $formattedPOs = $pendingPOs->map(function ($po) use ($products) { return [ 'id' => $po->id, 'code' => $po->code, 'status' => $po->status, 'vendor_id' => $po->vendor_id, 'vendor_name' => $po->vendor?->name ?? '', 'warehouse_id' => $po->warehouse_id, 'order_date' => $po->order_date, 'items' => $po->items->map(function ($item) use ($products) { $product = $products->get($item->product_id); $remaining = max(0, $item->quantity - ($item->received_quantity ?? 0)); return [ 'id' => $item->id, 'product_id' => $item->product_id, 'product_name' => $product?->name ?? '', 'product_code' => $product?->code ?? '', 'unit' => $product?->baseUnit?->name ?? '個', 'quantity' => $item->quantity, 'received_quantity' => $item->received_quantity ?? 0, 'remaining' => $remaining, 'unit_price' => $item->unit_price, ]; })->filter(fn($item) => $item['remaining'] > 0)->values(), ]; })->filter(fn($po) => $po['items']->count() > 0)->values(); // 取得所有廠商列表(用於雜項入庫/其他類型選擇) $vendors = $this->procurementService->getAllVendors(); return Inertia::render('Inventory/GoodsReceipt/Create', [ 'warehouses' => $this->inventoryService->getAllWarehouses(), 'pendingPurchaseOrders' => $formattedPOs, 'vendors' => $vendors, ]); } public function store(Request $request) { $validated = $request->validate([ 'warehouse_id' => 'required|exists:warehouses,id', 'type' => 'required|in:standard,miscellaneous,other', 'purchase_order_id' => 'nullable|required_if:type,standard|exists:purchase_orders,id', // Vendor ID is required if standard, but optional/nullable for misc/other? // Stick to existing logic: if standard, we infer vendor from PO usually, or frontend sends it. // For now let's make vendor_id optional for misc/other or user must select one? // "雜項入庫" might not have a vendor. Let's make it nullable. 'vendor_id' => 'nullable|integer', 'received_date' => 'required|date', 'remarks' => 'nullable|string', 'items' => 'required|array|min:1', 'items.*.product_id' => 'required|integer|exists:products,id', 'items.*.purchase_order_item_id' => 'nullable|required_if:type,standard|integer', 'items.*.quantity_received' => 'required|numeric|min:0', 'items.*.unit_price' => 'required|numeric|min:0', 'items.*.batch_number' => 'nullable|string', 'items.*.expiry_date' => 'nullable|date', ]); $this->goodsReceiptService->store($validated); return redirect()->route('goods-receipts.index')->with('success', '進貨單已建立'); } // API to search POs public function searchPOs(Request $request) { $search = $request->input('query'); if (!$search) { return response()->json([]); } $pos = $this->procurementService->searchPendingPurchaseOrders($search); return response()->json($pos); } // API to search Products for Manual Entry public function searchProducts(Request $request) { $search = $request->input('query'); if (!$search) { return response()->json([]); } $products = $this->inventoryService->getProductsByName($search); // Format for frontend $mapped = $products->map(function($product) { return [ 'id' => $product->id, 'name' => $product->name, 'code' => $product->code, 'unit' => $product->baseUnit?->name ?? '個', // Ensure unit is included 'price' => $product->purchase_price ?? 0, // Suggest price from product info if available ]; }); return response()->json($mapped); } // API to search Vendors public function searchVendors(Request $request) { $search = $request->input('query'); if (!$search) { return response()->json([]); } $vendors = $this->procurementService->searchVendors($search); return response()->json($vendors); } }