user(); $query = Product::with(['category', 'translations', 'company']); // 租戶隔離由 Global Scope (TenantScoped) 處理,但系統管理員可額外篩選 if ($user->isSystemAdmin() && $request->filled('company_id')) { $query->where('company_id', $request->company_id); } // 搜尋 if ($request->filled('search')) { $search = $request->search; $query->where(function($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('barcode', 'like', "%{$search}%") ->orWhere('spec', 'like', "%{$search}%"); }); } // 分類篩選 if ($request->filled('category_id')) { $query->where('category_id', $request->category_id); } $per_page = $request->input('per_page', 10); $products = $query->latest()->paginate($per_page)->withQueryString(); $categories = ProductCategory::all(); $companies = $user->isSystemAdmin() ? Company::all() : collect(); $companySettings = $user->company ? ($user->company->settings ?? []) : []; $routeName = 'admin.data-config.products.index'; return view('admin.products.index', [ 'products' => $products, 'categories' => $categories, 'companies' => $companies, 'companySettings' => $companySettings, 'routeName' => $routeName ]); } public function store(Request $request) { $validated = $request->validate([ 'names.zh_TW' => 'required|string|max:255', 'names.en' => 'nullable|string|max:255', 'names.ja' => 'nullable|string|max:255', 'barcode' => 'nullable|string|max:100', 'spec' => 'nullable|string|max:255', 'category_id' => 'nullable|exists:product_categories,id', 'manufacturer' => 'nullable|string|max:255', 'track_limit' => 'required|integer|min:1', 'spring_limit' => 'required|integer|min:1', 'price' => 'required|numeric|min:0', 'cost' => 'required|numeric|min:0', 'member_price' => 'required|numeric|min:0', 'metadata' => 'nullable|array', 'is_active' => 'nullable|boolean', ]); try { DB::beginTransaction(); $dictKey = (string) Str::uuid(); $company_id = auth()->user()->company_id; // Store translations foreach ($request->names as $locale => $name) { if (empty($name)) continue; Translation::create([ 'group' => 'product', 'key' => $dictKey, 'locale' => $locale, 'text' => $name, 'company_id' => $company_id, ]); } $product = Product::create([ 'company_id' => $company_id, 'category_id' => $request->category_id, 'name' => $request->names['zh_TW'], // Default name uses zh_TW 'name_dictionary_key' => $dictKey, 'barcode' => $request->barcode, 'spec' => $request->spec, 'manufacturer' => $request->manufacturer, 'track_limit' => $request->track_limit, 'spring_limit' => $request->spring_limit, 'price' => $request->price, 'cost' => $request->cost, 'member_price' => $request->member_price, 'metadata' => $request->metadata ?? [], 'is_active' => $request->boolean('is_active', true), ]); DB::commit(); if ($request->wantsJson()) { return response()->json([ 'success' => true, 'message' => __('Product created successfully'), 'data' => $product ]); } return redirect()->back()->with('success', __('Product created successfully')); } catch (\Exception $e) { DB::rollBack(); if ($request->wantsJson()) { return response()->json(['success' => false, 'message' => $e->getMessage()], 500); } return redirect()->back()->with('error', $e->getMessage()); } } public function update(Request $request, $id) { $product = Product::findOrFail($id); $validated = $request->validate([ 'names.zh_TW' => 'required|string|max:255', 'names.en' => 'nullable|string|max:255', 'names.ja' => 'nullable|string|max:255', 'barcode' => 'nullable|string|max:100', 'spec' => 'nullable|string|max:255', 'category_id' => 'nullable|exists:product_categories,id', 'manufacturer' => 'nullable|string|max:255', 'track_limit' => 'required|integer|min:1', 'spring_limit' => 'required|integer|min:1', 'price' => 'required|numeric|min:0', 'cost' => 'required|numeric|min:0', 'member_price' => 'required|numeric|min:0', 'metadata' => 'nullable|array', 'is_active' => 'nullable|boolean', ]); try { DB::beginTransaction(); $dictKey = $product->name_dictionary_key; $company_id = auth()->user()->company_id; // Update or Create translations foreach ($request->names as $locale => $name) { if (empty($name)) { Translation::where([ 'group' => 'product', 'key' => $dictKey, 'locale' => $locale ])->delete(); continue; } Translation::updateOrCreate( [ 'group' => 'product', 'key' => $dictKey, 'locale' => $locale, ], [ 'text' => $name, 'company_id' => $company_id, ] ); } $product->update([ 'category_id' => $request->category_id, 'name' => $request->names['zh_TW'], 'barcode' => $request->barcode, 'spec' => $request->spec, 'manufacturer' => $request->manufacturer, 'track_limit' => $request->track_limit, 'spring_limit' => $request->spring_limit, 'price' => $request->price, 'cost' => $request->cost, 'member_price' => $request->member_price, 'metadata' => $request->metadata ?? [], 'is_active' => $request->boolean('is_active', true), ]); DB::commit(); if ($request->wantsJson()) { return response()->json([ 'success' => true, 'message' => __('Product updated successfully'), 'data' => $product ]); } return redirect()->back()->with('success', __('Product updated successfully')); } catch (\Exception $e) { DB::rollBack(); if ($request->wantsJson()) { return response()->json(['success' => false, 'message' => $e->getMessage()], 500); } return redirect()->back()->with('error', $e->getMessage()); } } public function destroy($id) { try { $product = Product::findOrFail($id); // Delete translations associated with this product if ($product->name_dictionary_key) { Translation::where('key', $product->name_dictionary_key)->delete(); } $product->delete(); return redirect()->back()->with('success', __('Product deleted successfully')); } catch (\Exception $e) { return redirect()->back()->with('error', $e->getMessage()); } } }