diff --git a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php index c10316c..4de285d 100644 --- a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php +++ b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php @@ -47,6 +47,17 @@ class MachineSettingController extends AdminController $machines = $machineQuery->latest()->paginate($per_page)->withQueryString(); break; + case 'system_settings': + $machineQuery = Machine::query()->with(['company']); + if ($search) { + $machineQuery->where(function ($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('serial_no', 'like', "%{$search}%"); + }); + } + $machines = $machineQuery->latest()->paginate($per_page)->withQueryString(); + break; + case 'models': $modelQuery = MachineModel::query()->withCount('machines'); if ($search) { @@ -70,6 +81,7 @@ class MachineSettingController extends AdminController } $users_list = $userQuery->latest()->paginate($per_page)->withQueryString(); break; + } $companies = \App\Models\System\Company::select('id', 'name', 'code')->get(); @@ -77,6 +89,7 @@ class MachineSettingController extends AdminController $tabView = match($tab) { 'models' => 'admin.basic-settings.machines.partials.tab-models', 'permissions' => 'admin.basic-settings.machines.partials.tab-permissions', + 'system_settings' => 'admin.basic-settings.machines.partials.tab-system-settings', default => 'admin.basic-settings.machines.partials.tab-machines', }; return response()->view($tabView, compact( @@ -194,6 +207,12 @@ class MachineSettingController extends AdminController 'is_spring_slot_41_50' => 'boolean', 'is_spring_slot_51_60' => 'boolean', 'member_system_enabled' => 'boolean', + 'tax_invoice_enabled' => 'boolean', + 'card_terminal_enabled' => 'boolean', + 'scan_pay_esun_enabled' => 'boolean', + 'scan_pay_linepay_enabled' => 'boolean', + 'shopping_cart_enabled' => 'boolean', + 'cash_module_enabled' => 'boolean', 'machine_model_id' => 'required|exists:machine_models,id', 'payment_config_id' => 'nullable|exists:payment_configs,id', 'location' => 'nullable|string|max:255', @@ -287,6 +306,62 @@ class MachineSettingController extends AdminController ]); } + public function updateSystemSettings(Request $request, Machine $machine): \Illuminate\Http\JsonResponse|RedirectResponse + { + \Log::info('Update System Settings Request:', $request->all()); + if ($request->has('settings')) { + $settings = $request->input('settings'); + $allowedFields = [ + 'tax_invoice_enabled', + 'card_terminal_enabled', + 'scan_pay_esun_enabled', + 'scan_pay_linepay_enabled', + 'shopping_cart_enabled', + 'welcome_gift_enabled', + 'cash_module_enabled', + 'member_system_enabled', + ]; + $data = []; + foreach ($allowedFields as $field) { + $data[$field] = isset($settings[$field]) ? (bool)$settings[$field] : false; + } + $machine->update(array_merge($data, ['updater_id' => auth()->id()])); + + if ($request->expectsJson()) { + return response()->json([ + 'success' => true, + 'message' => __('Settings updated successfully.') + ]); + } + return redirect()->back()->with('success', __('Settings updated successfully.')); + } + + $field = $request->input('field'); + $value = $request->boolean('value'); + + $allowedFields = [ + 'tax_invoice_enabled', + 'card_terminal_enabled', + 'scan_pay_esun_enabled', + 'scan_pay_linepay_enabled', + 'shopping_cart_enabled', + 'welcome_gift_enabled', + 'cash_module_enabled', + 'member_system_enabled', + ]; + + if (!in_array($field, $allowedFields)) { + return response()->json(['success' => false, 'message' => 'Invalid field'], 400); + } + + $machine->update([$field => $value, 'updater_id' => auth()->id()]); + + return response()->json([ + 'success' => true, + 'message' => __('Setting updated successfully.') + ]); + } + /** * 公開機台分布地圖 */ diff --git a/app/Http/Controllers/Admin/CompanyController.php b/app/Http/Controllers/Admin/CompanyController.php index 466b558..ab7c24a 100644 --- a/app/Http/Controllers/Admin/CompanyController.php +++ b/app/Http/Controllers/Admin/CompanyController.php @@ -74,13 +74,6 @@ class CompanyController extends Controller if (isset($validated['settings'])) { $validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN); $validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['tax_invoice'] = filter_var($validated['settings']['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['card_terminal'] = filter_var($validated['settings']['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['scan_pay_esun'] = filter_var($validated['settings']['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['scan_pay_linepay'] = filter_var($validated['settings']['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['shopping_cart'] = filter_var($validated['settings']['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['welcome_gift'] = filter_var($validated['settings']['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['cash_module'] = filter_var($validated['settings']['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN); } DB::transaction(function () use ($validated) { @@ -185,13 +178,6 @@ class CompanyController extends Controller if (isset($validated['settings'])) { $validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN); $validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['tax_invoice'] = filter_var($validated['settings']['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['card_terminal'] = filter_var($validated['settings']['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['scan_pay_esun'] = filter_var($validated['settings']['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['scan_pay_linepay'] = filter_var($validated['settings']['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['shopping_cart'] = filter_var($validated['settings']['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['welcome_gift'] = filter_var($validated['settings']['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN); - $validated['settings']['cash_module'] = filter_var($validated['settings']['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN); } DB::transaction(function () use ($validated, $company) { @@ -230,13 +216,6 @@ class CompanyController extends Controller $formattedSettings = [ 'enable_material_code' => filter_var($settings['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN), 'enable_points' => filter_var($settings['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'tax_invoice' => filter_var($settings['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'card_terminal' => filter_var($settings['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'scan_pay_esun' => filter_var($settings['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'scan_pay_linepay' => filter_var($settings['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'shopping_cart' => filter_var($settings['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'welcome_gift' => filter_var($settings['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN), - 'cash_module' => filter_var($settings['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN), ]; // Ensure we force save the JSON cast properly diff --git a/app/Http/Controllers/Admin/WarehouseController.php b/app/Http/Controllers/Admin/WarehouseController.php index 34058ed..4608255 100644 --- a/app/Http/Controllers/Admin/WarehouseController.php +++ b/app/Http/Controllers/Admin/WarehouseController.php @@ -147,20 +147,26 @@ class WarehouseController extends Controller public function inventory(Request $request) { - $tab = $request->input('tab', 'stock-in'); + $tab = $request->input('tab', 'stock'); $warehouses = Warehouse::active()->orderBy('name')->get(); + $isAjax = $request->ajax(); $data = compact('warehouses', 'tab'); - if ($tab === 'stock') { - $inventory_warehouses = Warehouse::active()->orderBy('name')->get(['id', 'name', 'type']); + // 1. Current Stocks (stock) + if (!$isAjax || $tab === 'stock') { + $warehouse_ids = $request->input('warehouse_ids'); + $inventory_warehouses = Warehouse::active() + ->when($warehouse_ids, fn($q) => $q->whereIn('id', (array)$warehouse_ids)) + ->orderBy('name') + ->get(['id', 'name', 'type']); $query = Product::with(['stocks' => function($q) { $q->select('id', 'product_id', 'warehouse_id', 'quantity', 'safety_stock'); }, 'translations']); - if ($request->filled('warehouse_id')) { - $query->whereHas('stocks', fn($q) => $q->where('warehouse_id', $request->input('warehouse_id'))); + if ($warehouse_ids) { + $query->whereHas('stocks', fn($q) => $q->whereIn('warehouse_id', (array)$warehouse_ids)); } if ($search = $request->input('search')) { $query->where('name', 'like', "%{$search}%"); @@ -168,20 +174,30 @@ class WarehouseController extends Controller $data['inventory_warehouses'] = $inventory_warehouses; $data['products'] = $query->orderBy('name') - ->paginate($request->input('per_page', 15)) + ->paginate($request->input('per_page', 15), ['*'], 'stock_page') ->withQueryString(); + } - } elseif ($tab === 'stock-in') { + // 2. Stock-In Orders (stock-in) + if (!$isAjax || $tab === 'stock-in') { $query = StockInOrder::with(['warehouse:id,name', 'creator:id,name']) ->latest(); if ($request->filled('status')) { $query->where('status', $request->input('status')); } - $data['orders'] = $query->paginate($request->input('per_page', 10))->withQueryString(); - $data['products'] = Product::orderBy('name')->get(['id', 'name', 'image_url']); + if ($search = $request->input('search_order')) { + $query->where('order_no', 'like', "%{$search}%"); + } + if ($request->filled('start_date') && $request->filled('end_date')) { + $query->whereBetween('created_at', [$request->start_date, $request->end_date]); + } + $data['orders'] = $query->paginate($request->input('per_page', 10), ['*'], 'order_page')->withQueryString(); + $data['stock_in_products'] = Product::orderBy('name')->get(['id', 'name', 'image_url']); + } - } elseif ($tab === 'movements') { + // 3. Movement History (movements) + if (!$isAjax || $tab === 'movements') { $query = StockMovement::with(['warehouse:id,name', 'product:id,name', 'creator:id,name']) ->orderBy('created_at', 'desc'); @@ -191,10 +207,13 @@ class WarehouseController extends Controller if ($request->filled('type')) { $query->where('type', $request->input('type')); } - $data['movements'] = $query->paginate($request->input('per_page', 15))->withQueryString(); + if ($request->filled('start_date') && $request->filled('end_date')) { + $query->whereBetween('created_at', [$request->start_date, $request->end_date]); + } + $data['movements'] = $query->paginate($request->input('per_page', 15), ['*'], 'movement_page')->withQueryString(); } - if ($request->ajax()) { + if ($isAjax) { return response()->json([ 'success' => true, 'tab' => $tab, diff --git a/app/Models/Machine/Machine.php b/app/Models/Machine/Machine.php index 8b50ed7..bea51a1 100644 --- a/app/Models/Machine/Machine.php +++ b/app/Models/Machine/Machine.php @@ -85,6 +85,12 @@ class Machine extends Model 'images', 'creator_id', 'updater_id', + 'tax_invoice_enabled', + 'card_terminal_enabled', + 'scan_pay_esun_enabled', + 'scan_pay_linepay_enabled', + 'shopping_cart_enabled', + 'cash_module_enabled', ]; protected $appends = ['image_urls', 'calculated_status']; @@ -177,6 +183,12 @@ class Machine extends Model 'is_spring_slot_41_50' => 'boolean', 'is_spring_slot_51_60' => 'boolean', 'member_system_enabled' => 'boolean', + 'tax_invoice_enabled' => 'boolean', + 'card_terminal_enabled' => 'boolean', + 'scan_pay_esun_enabled' => 'boolean', + 'scan_pay_linepay_enabled' => 'boolean', + 'shopping_cart_enabled' => 'boolean', + 'cash_module_enabled' => 'boolean', 'images' => 'array', ]; diff --git a/database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php b/database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php new file mode 100644 index 0000000..909cc53 --- /dev/null +++ b/database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php @@ -0,0 +1,42 @@ +boolean('tax_invoice_enabled')->default(0)->after('invoice_status')->comment('電子發票開關'); + $table->boolean('card_terminal_enabled')->default(0)->after('card_reader_no')->comment('刷卡機系統開關'); + $table->boolean('scan_pay_esun_enabled')->default(0)->after('card_terminal_enabled')->comment('玉山掃碼支付開關'); + $table->boolean('scan_pay_linepay_enabled')->default(0)->after('scan_pay_esun_enabled')->comment('LinePay支付開關'); + $table->boolean('shopping_cart_enabled')->default(0)->after('scan_pay_linepay_enabled')->comment('購物車功能開關'); + $table->boolean('welcome_gift_enabled')->default(0)->after('shopping_cart_enabled')->comment('迎賓禮功能開關'); + $table->boolean('cash_module_enabled')->default(0)->after('welcome_gift_enabled')->comment('現金模組開關'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('machines', function (Blueprint $table) { + $table->dropColumn([ + 'tax_invoice_enabled', + 'card_terminal_enabled', + 'scan_pay_esun_enabled', + 'scan_pay_linepay_enabled', + 'shopping_cart_enabled', + 'welcome_gift_enabled', + 'cash_module_enabled', + ]); + }); + } +}; diff --git a/lang/en.json b/lang/en.json index eb4c89e..e7ca684 100644 --- a/lang/en.json +++ b/lang/en.json @@ -194,6 +194,7 @@ "Click to Open Dashboard": "Click to Open Dashboard", "Click to upload": "Click to upload", "Close Panel": "Close Panel", + "Coin\/Banknote Machine": "Coin\/Banknote Machine", "Coin\/Bill Acceptor": "Coin\/Bill Acceptor", "Command Center": "Command Center", "Command Confirmation": "Command Confirmation", @@ -264,6 +265,7 @@ "Created By": "Created By", "Creation Time": "Creation Time", "Creator": "Creator", + "Credit Card": "Credit Card", "Credit Card \/ Contactless": "Credit Card \/ Contactless", "Critical": "Critical", "Current": "Current", @@ -334,6 +336,8 @@ "Draft": "Draft", "Duration": "Duration", "Duration (Seconds)": "Duration (Seconds)", + "E.SUN Bank Scan": "E.SUN Bank Scan", + "E.SUN Pay": "E.SUN Pay", "E.SUN QR Pay": "E.SUN QR Pay", "E.SUN QR Scan": "E.SUN QR Scan", "E.SUN QR Scan Settings Description": "E.SUN Bank QR Scan Payment Settings", @@ -703,6 +707,7 @@ "No products found matching your criteria.": "No products found matching your criteria.", "No records found": "No records found", "No replenishment orders found": "No replenishment orders found", + "No results found": "No results found", "No roles available": "No roles available", "No roles found.": "No roles found.", "No slot data": "No slot data", @@ -1027,6 +1032,7 @@ "Serial Number": "Serial Number", "Service Periods": "Service Periods", "Service Terms": "Service Periods", + "Settings updated successfully.": "Settings updated successfully.", "Settlement": "Settlement", "Shopping Cart": "Shopping Cart", "Shopping Cart Feature": "Shopping Cart Feature", @@ -1213,6 +1219,7 @@ "Update Product": "Update Product", "Update Settings": "Update Settings", "Update existing role and permissions.": "Update existing role and permissions.", + "Update failed": "Update failed", "Update identification for your asset": "Update identification for your asset", "Update your account's profile information and email address.": "Update your account's profile information and email address.", "Upload Image": "Upload Image", diff --git a/lang/ja.json b/lang/ja.json index bbe4a4a..c3fa709 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -18,6 +18,7 @@ "Card Terminal System": "カード端末システム", "Cash Module": "現金決済モジュール", "Cash Module Feature": "現金モジュール機能", + "Coin\/Banknote Machine": "硬貨\/紙幣モジュール", "Coin\/Bill Acceptor": "コイン\/紙幣機", "Completed": "完了", "Confirm": "確認", @@ -31,10 +32,13 @@ "Create stock transfers between warehouses and machine returns": "倉庫間の転送および機台からの返品伝票を作成", "Created At": "作成日時", "Created By": "作成者", + "Credit Card": "クレジットカード", "Damage": "破損", "Delivering": "配送中", "Display Material Code": "素材コードを表示", "Draft": "下書き", + "E.SUN Bank Scan": "玉山銀行スキャン", + "E.SUN Pay": "E.SUN Pay", "E.SUN QR Pay": "玉山QR決済", "ESUN": "玉山銀行 (ESUN)", "Electronic Invoice": "電子翻訳", @@ -52,10 +56,12 @@ "Includes Credit Card\/Mobile Pay": "クレジットカード\/モバイル決済を含む", "Inventory Management": "在庫管理", "LinePay": "LinePay", - "LinePay Payment": "LinePay決済", + "LinePay Payment": "LinePay 決済", "Loading...": "読み込み中...", "Low Stock": "在庫不足", "Low Stock Alerts": "低在庫アラート", + "Machine Distribution": "機器分布", + "Machine Distribution Map": "機器分布マップ", "Machine Inventory Overview": "機台在庫概要", "Machine Replenishment": "機台補充", "Machine Return": "機台返品", @@ -113,6 +119,7 @@ "Select Product": "商品を選択", "Select Warehouse": "倉庫を選択", "Serial No.": "シリアル番号", + "Settings updated successfully.": "設定が正常に更新されました。", "Shopping Cart": "ショッピングカート", "Shopping Cart Feature": "ショッピングカート機能", "Slot": "貨道", @@ -145,6 +152,7 @@ "Transfers": "転送", "Unpublish": "配信終了", "Update Settings": "設定を更新", + "Update failed": "更新に失敗しました", "Video": "動画", "View Details": "詳細を表示", "View Inventory": "在庫を表示", @@ -161,5 +169,9 @@ "Warehouse to Warehouse": "倉庫間転送", "Warehouse updated successfully.": "倉庫が正常に更新されました。", "Welcome Gift": "登録特典", - "Welcome Gift Feature": "来店特典機能" + "Welcome Gift Feature": "来店特典機能", + "Visual overview of all machine locations": "全機器位置のビジュアル概要", + "Online": "オンライン", + "Offline": "オフライン", + "Error": "エラー" } \ No newline at end of file diff --git a/lang/zh_TW.json b/lang/zh_TW.json index e26bad8..bca9352 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -165,6 +165,7 @@ "Cannot delete model that is currently in use by machines.": "無法刪除目前正在被機台使用的型號。", "Cannot delete role with active users.": "無法刪除已有綁定帳號的角色。", "Cannot delete warehouse with existing stock": "無法刪除仍有庫存的倉庫", + "Card Machine System": "刷卡機系統", "Card Reader": "刷卡機", "Card Reader No": "刷卡機編號", "Card Reader Reboot": "刷卡機重啟", @@ -175,6 +176,7 @@ "Card Terminal System": "刷卡機系統", "Cash Module": "硬幣機 \/ 紙鈔機模組", "Cash Module Feature": "現金模組功能", + "Cash Module Function": "現金模組功能", "Category": "類別", "Category Management": "分類管理", "Category Name": "分類名稱", @@ -198,6 +200,7 @@ "Click to Open Dashboard": "點擊開啟儀表板", "Click to upload": "點擊上傳", "Close Panel": "關閉面板", + "Coin\/Banknote Machine": "硬幣機\/紙鈔機", "Coin\/Bill Acceptor": "硬幣機\/紙鈔機", "Command Center": "指令中心", "Command Confirmation": "指令確認", @@ -229,7 +232,6 @@ "Confirm replenishment completed?": "確定補貨已完成?", "Confirm this stock-in order?": "確定要確認此進貨單嗎?", "Confirm this transfer?": "確定要確認此調撥作業嗎?", - "Current Stocks": "目前庫存", "Connected": "通訊中", "Connecting...": "連線中", "Connection lost (LWT)": "連線中斷", @@ -273,12 +275,14 @@ "Created By": "建立者", "Creation Time": "建立時間", "Creator": "建立者", + "Credit Card": "信用卡", "Credit Card \/ Contactless": "支援信用卡 \/ 感應支付", "Critical": "嚴重", "Current": "當前", "Current Password": "當前密碼", "Current Status": "當前狀態", "Current Stock": "當前庫存", + "Current Stocks": "目前庫存", "Current Type": "當前類型", "Current:": "現:", "Customer Details": "客戶詳情", @@ -350,7 +354,9 @@ "Draft": "草稿", "Duration": "時長", "Duration (Seconds)": "播放秒數", - "E.SUN QR Pay": "玉山掃碼支付", + "E.SUN Bank Scan": "玉山銀行掃碼", + "E.SUN Pay": "玉山 Pay", + "E.SUN QR Pay": "玉山銀行掃碼", "E.SUN QR Scan": "玉山銀行標籤支付", "E.SUN QR Scan Settings Description": "玉山銀行掃碼支付設定", "EASY_MERCHANT_ID": "悠遊付 商店代號", @@ -437,10 +443,11 @@ "Failed to update machine images: ": "更新機台圖片失敗:", "Feature Settings": "功能設定", "Feature Toggles": "功能開關", - "Filter": "篩選", "Fill Rate": "滿載率", "Fill in the device repair or maintenance details": "填寫設備維修或保養詳情", "Fill in the product details below": "請在下方填寫商品的詳細資訊", + "Filter": "篩選", + "Filter by Warehouse Presence": "依據倉庫存貨篩選", "Firmware Version": "韌體版本", "Firmware updated to :version": "韌體版本更新 : :version", "Fleet Avg OEE": "全機台平均 OEE", @@ -454,6 +461,7 @@ "Full Access": "全機台授權", "Full Name": "名稱", "Full Points": "全點數", + "Functional Settings": "功能設定", "Games": "互動遊戲", "General permissions not linked to a specific menu.": "未連結到特定選單的一般權限。", "Gift Definitions": "禮品設定", @@ -483,7 +491,7 @@ "Image": "圖片", "Immediate": "立即", "In Stock": "當前庫存", - "Includes Credit Card\/Mobile Pay": "包括 \/信用卡\/卡片支付\/手機支付", + "Includes Credit Card\/Mobile Pay": "含信用卡\/行動支付", "Indefinite": "無限期", "Info": "一般", "Initial Admin Account": "初始管理帳號", @@ -559,6 +567,7 @@ "Machine Details": "機台詳情", "Machine Distribution": "機台分布", "Machine Distribution Map": "機台分布地圖", + "Visual overview of all machine locations": "所有機台位置的視覺化概覽", "Machine Images": "機台照片", "Machine Info": "機台資訊", "Machine Information": "機台資訊", @@ -583,6 +592,7 @@ "Machine Settings": "機台設定", "Machine Status": "機台狀態", "Machine Status List": "機台運行狀態列表", + "Machine System Settings": "機台系統設定", "Machine Utilization": "機台稼動率", "Machine created successfully.": "機台已成功建立。", "Machine images updated successfully.": "機台圖片已成功更新。", @@ -725,6 +735,7 @@ "No products found matching your criteria.": "找不到符合條件的商品。", "No records found": "未找到相關紀錄", "No replenishment orders found": "查無補貨單紀錄", + "No results found": "查無搜尋結果", "No roles available": "目前沒有角色資料。", "No roles found.": "找不到角色資料。", "No slot data": "找不到貨道資料", @@ -871,7 +882,7 @@ "Price \/ Member": "售價 \/ 會員價", "Pricing Information": "價格資訊", "Product": "商品", - "Product / Stock": "商品 / 庫存", + "Product \/ Stock": "商品 \/ 庫存", "Product Count": "商品數量", "Product Details": "商品細項", "Product ID": "商品編號", @@ -1000,7 +1011,7 @@ "Saved.": "已儲存", "Saving...": "儲存中...", "Scale level and access control": "層級與存取控制", - "Scan Pay": "掃碼支付", + "Scan Pay": "掃碼支付功能", "Scan this code to quickly access the maintenance form for this device.": "掃描此 QR Code 即可快速進入此設備的維修單填寫頁面。", "Schedule": "排程區間", "Search Company Title...": "搜尋公司名稱...", @@ -1062,9 +1073,11 @@ "Service Periods": "服務區間", "Service Terms": "服務期程", "Settings Saved": "設定已儲存", + "Settings updated successfully.": "設定更新成功。", "Settlement": "結帳處理", - "Shopping Cart": "購物車功能", + "Shopping Cart": "購物車", "Shopping Cart Feature": "購物車功能", + "Shopping Cart Function": "購物車功能", "Show": "顯示", "Show material code field in products": "在商品資料中顯示物料編號欄位", "Show points rules in products": "在商品資料中顯示點數規則相關欄位", @@ -1259,6 +1272,7 @@ "Update Product": "更新商品", "Update Settings": "更新設定", "Update existing role and permissions.": "更新現有角色與權限設定。", + "Update failed": "更新失敗", "Update identification for your asset": "更新您的資產識別名稱", "Update your account's profile information and email address.": "更新您的帳號名稱、手機號碼與電子郵件地址。", "Upload Image": "上傳圖片", @@ -1320,10 +1334,9 @@ "Warranty Service": "保固服務", "Warranty Start": "保固起始", "Welcome Gift": "來店禮", - "Welcome Gift Feature": "來店禮功能", + "Welcome Gift Feature": "迎賓禮功能", + "Welcome Gift Function": "迎賓禮功能", "Welcome Gift Status": "來店禮", - "Work Content": "工作內容", - "Yes, regenerate": "確認重新產生", "Yesterday": "昨日", "You cannot assign permissions you do not possess.": "您無法指派您自身不具備的權限。", "You cannot delete your own account.": "您無法刪除自己的帳號。", @@ -1422,6 +1435,5 @@ "visit_gift": "來店禮", "vs Yesterday": "較昨日", "warehouses": "倉庫管理", - "Filter by Warehouse Presence": "依據倉庫存貨篩選", "待填寫": "待填寫" } \ No newline at end of file diff --git a/resources/views/admin/basic-settings/machines/index.blade.php b/resources/views/admin/basic-settings/machines/index.blade.php index 11215d2..7df50d1 100644 --- a/resources/views/admin/basic-settings/machines/index.blade.php +++ b/resources/views/admin/basic-settings/machines/index.blade.php @@ -8,6 +8,61 @@ modelSearch: '', permissionSearch: '', permissionCompanyId: '{{ request('company_id') }}', + isUpdatingSetting: false, + async toggleSystemSetting(machineId, field, value) { + if (this.isUpdatingSetting) return; + this.isUpdatingSetting = true; + try { + const res = await fetch(`/admin/basic-settings/machines/${machineId}/update-system-settings`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, + 'Accept': 'application/json' + }, + body: JSON.stringify({ field, value }) + }); + const data = await res.json(); + if (data.success) { + window.dispatchEvent(new CustomEvent('toast', { detail: { message: data.message, type: 'success' } })); + } else { + throw new Error(data.message || 'Update failed'); + } + } catch (e) { + window.dispatchEvent(new CustomEvent('toast', { detail: { message: e.message, type: 'error' } })); + // Refresh content to reset toggle state on UI + this.searchInTab('system_settings'); + } finally { + this.isUpdatingSetting = false; + } + }, + async submitMachineSettings() { + if (this.isUpdatingSetting || !this.currentMachine) return; + this.isUpdatingSetting = true; + try { + const res = await fetch(`/admin/basic-settings/machines/${this.currentMachine.id}/update-system-settings`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, + 'Accept': 'application/json' + }, + body: JSON.stringify({ settings: this.machineSettings }) + }); + const data = await res.json(); + if (data.success) { + window.dispatchEvent(new CustomEvent('toast', { detail: { message: data.message, type: 'success' } })); + this.showMachineSettingsModal = false; + this.searchInTab('system_settings'); + } else { + throw new Error(data.message || '{{ __('Update failed') }}'); + } + } catch (e) { + window.dispatchEvent(new CustomEvent('toast', { detail: { message: e.message, type: 'error' } })); + } finally { + this.isUpdatingSetting = false; + } + }, showCreateMachineModal: false, showPhotoModal: false, showDetailDrawer: false, @@ -25,6 +80,22 @@ maintenanceQrMachineName: '', maintenanceQrUrl: '', permissionSearchQuery: '', + showMachineSettingsModal: false, + machineSettings: {}, + openMachineSettingsModal(machine) { + this.currentMachine = machine; + this.machineSettings = { + tax_invoice_enabled: machine.tax_invoice_enabled === true || machine.tax_invoice_enabled === 1 || machine.tax_invoice_enabled === '1', + card_terminal_enabled: machine.card_terminal_enabled === true || machine.card_terminal_enabled === 1 || machine.card_terminal_enabled === '1', + scan_pay_esun_enabled: machine.scan_pay_esun_enabled === true || machine.scan_pay_esun_enabled === 1 || machine.scan_pay_esun_enabled === '1', + scan_pay_linepay_enabled: machine.scan_pay_linepay_enabled === true || machine.scan_pay_linepay_enabled === 1 || machine.scan_pay_linepay_enabled === '1', + shopping_cart_enabled: machine.shopping_cart_enabled === true || machine.shopping_cart_enabled === 1 || machine.shopping_cart_enabled === '1', + welcome_gift_enabled: machine.welcome_gift_enabled === true || machine.welcome_gift_enabled === 1 || machine.welcome_gift_enabled === '1', + cash_module_enabled: machine.cash_module_enabled === true || machine.cash_module_enabled === 1 || machine.cash_module_enabled === '1', + member_system_enabled: machine.member_system_enabled === true || machine.member_system_enabled === 1 || machine.member_system_enabled === '1' + }; + this.showMachineSettingsModal = true; + }, openMaintenanceQr(machine) { this.maintenanceQrMachineName = machine.name; const baseUrl = '{{ route('admin.maintenance.create', ['serial_no' => 'SERIAL_NO']) }}'; @@ -197,7 +268,7 @@ // === 搜尋/分頁 AJAX(僅在搜尋或換頁時觸發,Tab 切換不走此路) === async searchInTab(tabName, extraQuery = '') { this.tabLoading = true; - const searchMap = { machines: this.machineSearch, models: this.modelSearch, permissions: this.permissionSearch }; + const searchMap = { machines: this.machineSearch, models: this.modelSearch, permissions: this.permissionSearch, system_settings: this.machineSearch, distribution: '' }; const search = searchMap[tabName] || ''; let qs = `tab=${tabName}&_ajax=1`; if (search) qs += `&search=${encodeURIComponent(search)}`; @@ -286,7 +357,7 @@ }); // 首次載入時綁定每個 Tab 的分頁連結 this.$nextTick(() => { - ['machines', 'models', 'permissions'].forEach(t => { + ['machines', 'models', 'permissions', 'system_settings'].forEach(t => { const ref = this.$refs[t + 'Content']; if (ref) this.bindPaginationLinks(ref, t); }); @@ -294,6 +365,7 @@ // Tab 切換時同步 URL this.$watch('tab', (newTab) => { history.pushState({}, '', `{{ route('admin.basic-settings.machines.index') }}?tab=${newTab}`); + window.dispatchEvent(new CustomEvent('tab-changed', { detail: { tab: newTab } })); }); // 瀏覽器上一頁/下一頁 window.addEventListener('popstate', () => { @@ -302,19 +374,209 @@ }); } }" @execute-regenerate.window="executeRegeneration($event.detail)"> + + + +
-
-

{{ __('Machine Settings') }}

-

{{ __('Management of operational parameters and models') }}

+
+
+

{{ __('Machine Settings') }}

+ + + + + {{ __('Machine Distribution') }} + +
+

{{ __('Management of operational parameters and models') }}

- - - - - {{ __('Machine Distribution') }} - + +
@@ -393,6 +660,12 @@ @include('admin.basic-settings.machines.partials.tab-permissions')
+ +
+
+ @include('admin.basic-settings.machines.partials.tab-system-settings') +
+
diff --git a/resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php b/resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php new file mode 100644 index 0000000..3edf5d1 --- /dev/null +++ b/resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php @@ -0,0 +1,152 @@ +
+
+
+

{{ __('Machine Distribution Map') }}

+

{{ __('Visual overview of all machine locations') }}

+
+
+
+ + {{ __('Online') }} +
+
+ + {{ __('Offline') }} +
+
+ + {{ __('Error') }} +
+
+
+ +
+
+
+
+ + +@once + + +@endonce + + + + diff --git a/resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php b/resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php new file mode 100644 index 0000000..9c134c5 --- /dev/null +++ b/resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php @@ -0,0 +1,127 @@ +
+ +
+
+ +
+ + + + + + + +
+ + +
+ + @foreach ($companies as $company) + + @endforeach + +
+
+
+ + +
+ + + + + + + + + + + @forelse($machines as $machine) + + + + + + + @empty + + + + @endforelse + +
+ {{ __('Machine Information') }} + + {{ __('Company') }} + + {{ __('Enabled Features') }} + + {{ __('Actions') }} +
+
+
+ + + +
+
+
+ {{ $machine->name }} +
+
+ {{ $machine->serial_no }} +
+
+
+
+ {{ $machine->company?->name ?? __('System') }} + +
+ @php + $activeFeatures = []; + if ($machine->tax_invoice_enabled) $activeFeatures[] = __('Electronic Invoice'); + if ($machine->card_terminal_enabled) $activeFeatures[] = __('Includes Credit Card/Mobile Pay'); + if ($machine->scan_pay_esun_enabled) $activeFeatures[] = __('E.SUN Bank Scan'); + if ($machine->scan_pay_linepay_enabled) $activeFeatures[] = __('LinePay Payment'); + if ($machine->shopping_cart_enabled) $activeFeatures[] = __('Shopping Cart'); + if ($machine->welcome_gift_enabled) $activeFeatures[] = __('Welcome Gift'); + if ($machine->cash_module_enabled) $activeFeatures[] = __('Coin/Banknote Machine'); + if ($machine->member_system_enabled) $activeFeatures[] = __('Member System'); + @endphp + + @forelse($activeFeatures as $feature) + {{ $feature }} + @empty + {{ __('None') }} + @endforelse +
+
+ +
+
+
+ + + +
+

{{ __('No machines found') }}

+
+
+
+ + +
+ {{ $machines->appends(['tab' => 'system_settings', 'search' => request('search'), 'company_id' => request('company_id')])->links('vendor.pagination.luxury') }} +
+
diff --git a/resources/views/admin/companies/index.blade.php b/resources/views/admin/companies/index.blade.php index 4d6d015..9d612f3 100644 --- a/resources/views/admin/companies/index.blade.php +++ b/resources/views/admin/companies/index.blade.php @@ -28,14 +28,7 @@ note: '', settings: { enable_material_code: false, - enable_points: false, - tax_invoice: false, - card_terminal: false, - scan_pay_esun: false, - scan_pay_linepay: false, - shopping_cart: false, - welcome_gift: false, - cash_module: false + enable_points: false } }, openCreateModal() { @@ -49,14 +42,7 @@ status: 1, note: '', settings: { enable_material_code: false, - enable_points: false, - tax_invoice: false, - card_terminal: false, - scan_pay_esun: false, - scan_pay_linepay: false, - shopping_cart: false, - welcome_gift: false, - cash_module: false + enable_points: false } }; this.showModal = true; @@ -73,14 +59,7 @@ software_end_date: company.software_end_date ? company.software_end_date.substring(0, 10) : '', settings: { enable_material_code: company.settings?.enable_material_code || false, - enable_points: company.settings?.enable_points || false, - tax_invoice: company.settings?.tax_invoice || false, - card_terminal: company.settings?.card_terminal || false, - scan_pay_esun: company.settings?.scan_pay_esun || false, - scan_pay_linepay: company.settings?.scan_pay_linepay || false, - shopping_cart: company.settings?.shopping_cart || false, - welcome_gift: company.settings?.welcome_gift || false, - cash_module: company.settings?.cash_module || false + enable_points: company.settings?.enable_points || false } }; this.originalStatus = company.status; @@ -109,14 +88,7 @@ ...company, settings: { enable_material_code: company.settings?.enable_material_code || false, - enable_points: company.settings?.enable_points || false, - tax_invoice: company.settings?.tax_invoice || false, - card_terminal: company.settings?.card_terminal || false, - scan_pay_esun: company.settings?.scan_pay_esun || false, - scan_pay_linepay: company.settings?.scan_pay_linepay || false, - shopping_cart: company.settings?.shopping_cart || false, - welcome_gift: company.settings?.welcome_gift || false, - cash_module: company.settings?.cash_module || false + enable_points: company.settings?.enable_points || false } }; this.sidebarView = 'detail'; @@ -127,14 +99,7 @@ ...company, settings: { enable_material_code: company.settings?.enable_material_code || false, - enable_points: company.settings?.enable_points || false, - tax_invoice: company.settings?.tax_invoice || false, - card_terminal: company.settings?.card_terminal || false, - scan_pay_esun: company.settings?.scan_pay_esun || false, - scan_pay_linepay: company.settings?.scan_pay_linepay || false, - shopping_cart: company.settings?.shopping_cart || false, - welcome_gift: company.settings?.welcome_gift || false, - cash_module: company.settings?.cash_module || false + enable_points: company.settings?.enable_points || false } }; this.sidebarView = 'history'; @@ -148,14 +113,7 @@ ...company, settings: { enable_material_code: company.settings?.enable_material_code || false, - enable_points: company.settings?.enable_points || false, - tax_invoice: company.settings?.tax_invoice || false, - card_terminal: company.settings?.card_terminal || false, - scan_pay_esun: company.settings?.scan_pay_esun || false, - scan_pay_linepay: company.settings?.scan_pay_linepay || false, - shopping_cart: company.settings?.shopping_cart || false, - welcome_gift: company.settings?.welcome_gift || false, - cash_module: company.settings?.cash_module || false + enable_points: company.settings?.enable_points || false } }; this.showHistoryModal = true; @@ -165,14 +123,7 @@ ...company, settings: { enable_material_code: company.settings?.enable_material_code === true || company.settings?.enable_material_code === 1 || company.settings?.enable_material_code === '1', - enable_points: company.settings?.enable_points === true || company.settings?.enable_points === 1 || company.settings?.enable_points === '1', - tax_invoice: company.settings?.tax_invoice === true || company.settings?.tax_invoice === 1 || company.settings?.tax_invoice === '1', - card_terminal: company.settings?.card_terminal === true || company.settings?.card_terminal === 1 || company.settings?.card_terminal === '1', - scan_pay_esun: company.settings?.scan_pay_esun === true || company.settings?.scan_pay_esun === 1 || company.settings?.scan_pay_esun === '1', - scan_pay_linepay: company.settings?.scan_pay_linepay === true || company.settings?.scan_pay_linepay === 1 || company.settings?.scan_pay_linepay === '1', - shopping_cart: company.settings?.shopping_cart === true || company.settings?.shopping_cart === 1 || company.settings?.shopping_cart === '1', - welcome_gift: company.settings?.welcome_gift === true || company.settings?.welcome_gift === 1 || company.settings?.welcome_gift === '1', - cash_module: company.settings?.cash_module === true || company.settings?.cash_module === 1 || company.settings?.cash_module === '1', + enable_points: company.settings?.enable_points === true || company.settings?.enable_points === 1 || company.settings?.enable_points === '1' } }; this.showSettingsModal = true; @@ -221,11 +172,12 @@ } } }"> +

- {{ __('Customer List') }} + {{ __('Customer Management') }}

{{ __('Manage all tenant accounts and validity') }} @@ -603,27 +555,6 @@ @if($settings['enable_points'] ?? false) {{ __('Points') }} @endif - @if($settings['tax_invoice'] ?? false) - {{ __('Electronic Invoice') }} - @endif - @if($settings['card_terminal'] ?? false) - {{ __('Card System') }} - @endif - @if($settings['scan_pay_esun'] ?? false) - {{ __('ESUN') }} - @endif - @if($settings['scan_pay_linepay'] ?? false) - {{ __('LinePay') }} - @endif - @if($settings['shopping_cart'] ?? false) - {{ __('Shopping Cart') }} - @endif - @if($settings['welcome_gift'] ?? false) - {{ __('Welcome Gift') }} - @endif - @if($settings['cash_module'] ?? false) - {{ __('Cash Module') }} - @endif @endif

@@ -1012,116 +943,6 @@
- - -
-
-

{{ __('Taxation System') }}

-
-
- -
-
- - -
-
-

{{ __('Card Terminal System') }}

-
-
- -
-
- - -
-
-

{{ __('QR Scan Payment') }}

-
-
- - -
-
- - -
-
-

{{ __('Shopping Cart Feature') }}

-
-
- -
-
- - -
-
-

{{ __('Welcome Gift Feature') }}

-
-
- -
-
- - -
-
-

{{ __('Cash Module Feature') }}

-
-
- -
-
@@ -1305,97 +1126,7 @@
- -
-
-
- -
- {{ __('Electronic Invoice') }} -
-
- -
-
- -
-
-
- -
- {{ __('Card System') }} -
-
- -
-
- -
-
-
- -
- {{ __('ESUN QR Pay') }} -
-
- -
-
- -
-
-
- -
- {{ __('LinePay Payment') }} -
-
- -
-
- -
-
-
- -
- {{ __('Shopping Cart') }} -
-
- -
-
- -
-
-
- -
- {{ __('Welcome Gift') }} -
-
- -
-
- -
-
-
- -
- {{ __('Cash Module') }} -
-
- -
-
+ diff --git a/resources/views/admin/warehouses/inventory.blade.php b/resources/views/admin/warehouses/inventory.blade.php index b9d1e77..d540ca4 100644 --- a/resources/views/admin/warehouses/inventory.blade.php +++ b/resources/views/admin/warehouses/inventory.blade.php @@ -56,16 +56,16 @@ }, init() { - this.$watch('activeTab', (newTab, oldTab) => { - const container = document.getElementById('tab-' + newTab + '-container'); - if (container && container.innerHTML.trim() === '') { - this.fetchTabData(newTab); - } - + this.$watch('activeTab', (newTab) => { // Update URL const url = new URL(window.location.origin + window.location.pathname); url.searchParams.set('tab', newTab); window.history.pushState({}, '', url); + + // Re-init HSSelect if needed when tab changes + this.$nextTick(() => { + if (window.HSStaticMethods) window.HSStaticMethods.autoInit(); + }); }); }, @@ -90,7 +90,7 @@ if (form) { const formData = new FormData(form); formData.forEach((value, key) => { - if (value.trim() !== '') params.set(key, value); + if (value.trim() !== '') params.append(key, value); }); } url = `${window.location.pathname}?${params.toString()}`; @@ -185,6 +185,7 @@ }, handleFilterSubmit(tab) { + console.log('handleFilterSubmit called for tab:', tab); const url = new URL(window.location.href); url.searchParams.delete('page'); this.fetchTabData(tab); @@ -242,7 +243,7 @@

-