164 lines
6.7 KiB
PHP
164 lines
6.7 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Modules\Inventory\Controllers;
|
||
|
|
|
||
|
|
use App\Http\Controllers\Controller;
|
||
|
|
use App\Modules\Inventory\Services\GoodsReceiptService;
|
||
|
|
use App\Modules\Inventory\Services\InventoryService;
|
||
|
|
use App\Modules\Procurement\Contracts\ProcurementServiceInterface;
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Inertia\Inertia;
|
||
|
|
use App\Modules\Inventory\Models\GoodsReceipt;
|
||
|
|
|
||
|
|
class GoodsReceiptController extends Controller
|
||
|
|
{
|
||
|
|
protected $goodsReceiptService;
|
||
|
|
protected $inventoryService;
|
||
|
|
protected $procurementService;
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
GoodsReceiptService $goodsReceiptService,
|
||
|
|
InventoryService $inventoryService,
|
||
|
|
ProcurementServiceInterface $procurementService
|
||
|
|
) {
|
||
|
|
$this->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);
|
||
|
|
}
|
||
|
|
}
|