goodsReceiptService = $goodsReceiptService; $this->inventoryService = $inventoryService; $this->procurementService = $procurementService; } public function index(Request $request) { $query = GoodsReceipt::query() ->with(['warehouse']); // Vendor info might need fetching separately or stored as snapshot if cross-module strict if ($request->has('search')) { $search = $request->input('search'); $query->where('code', 'like', "%{$search}%"); } $receipts = $query->orderBy('created_at', 'desc') ->paginate(10) ->withQueryString(); // Hydrate Vendor Names (Manual hydration to avoid cross-module relation) // Or if we stored vendor_name in DB, we could use that. // For now, let's fetch vendors via Service if needed, or just let frontend handle it if we passed IDs? // Let's implement hydration properly. $vendorIds = $receipts->pluck('vendor_id')->unique()->toArray(); if (!empty($vendorIds)) { // Check if ProcurementService has getVendorsByIds? No directly exposed method in interface yet. // Let's assume we can add it or just fetch POs to get vendors? // Actually, for simplicity and performance in Strict Mode, often we just fetch minimal data. // Or we can use `App\Modules\Procurement\Models\Vendor` directly ONLY for reading if allowed, but strict mode says NO. // But we don't have getVendorsByIds in interface. // User requirement: "從採購單帶入". // Let's just pass IDs for now, or use a method if available. // Wait, I can't modify Interface easily without user approval if it's big change. // But I just added updateReceivedQuantity. // Let's skip vendor name hydration for index for a moment and focus on Create first, or use a direct DB query via a DTO service? // Actually, I can use `DB::table('vendors')` as a workaround if needed, but that's dirty. // Let's revisit Service Interface. } // Quick fix: Add `vendor` relation to GoodsReceipt only if we decided to allow it or if we stored snapshot. // Plan said: `vendor_id`: foreignId. // Ideally we should have stored `vendor_name` in `goods_receipts` table for snapshot. // I didn't add it in migration. // Let's rely on `ProcurementServiceInterface` to get vendor info if possible. // I will add a method to get Vendors or POs. return Inertia::render('Inventory/GoodsReceipt/Index', [ 'receipts' => $receipts, 'filters' => $request->only(['search']), ]); } public function create() { return Inertia::render('Inventory/GoodsReceipt/Create', [ 'warehouses' => $this->inventoryService->getAllWarehouses(), // Vendors? We need to select PO, not Vendor directly maybe? // Designing the UI: Select PO -> fills Vendor and Items. // So we need a way to search POs by code or vendor. // We can provide an API for searching POs. ]); } 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->unit, // 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); } }