1. 實作通行碼 (Pass Code) 編輯功能,包含後端 Controller 邏輯與前端 Alpine.js 編輯彈窗。 2. 優化 searchable-select 組件的空值處理與樣式。 3. 更新繁體中文語系檔,統一通行碼相關操作之回傳訊息翻譯。 4. 修正通行碼更新邏輯,支援 nullable 狀態與到期日空值處理。 5. 在桌機版與手機版介面加入編輯操作入口。
247 lines
8.0 KiB
PHP
247 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\Request;
|
|
use App\Models\Transaction\PickupCode;
|
|
use App\Models\Transaction\PassCode;
|
|
use App\Models\Machine\Machine;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
class SalesController extends Controller
|
|
{
|
|
// 銷售&金流紀錄
|
|
public function index()
|
|
{
|
|
return view('admin.placeholder', [
|
|
'title' => '銷售&金流紀錄',
|
|
'description' => '銷售交易與金流明細查詢',
|
|
'features' => [
|
|
'銷售記錄查詢',
|
|
'金流對帳',
|
|
'發票管理',
|
|
'退款處理',
|
|
]
|
|
]);
|
|
}
|
|
|
|
// 取貨碼設定
|
|
public function pickupCodes(Request $request)
|
|
{
|
|
$query = PickupCode::with(['machine.slots.product', 'creator'])->latest();
|
|
|
|
if ($request->search) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('code', 'like', "%{$request->search}%")
|
|
->orWhereHas('machine', function ($mq) use ($request) {
|
|
$mq->where('name', 'like', "%{$request->search}%")
|
|
->orWhere('serial_no', 'like', "%{$request->search}%");
|
|
});
|
|
});
|
|
}
|
|
|
|
if ($request->status && trim($request->status) !== '') {
|
|
$query->where('status', trim($request->status));
|
|
}
|
|
|
|
$per_page = $request->input('per_page', 15);
|
|
$pickupCodes = $query->paginate($per_page)->withQueryString();
|
|
|
|
// 供新增彈窗使用的機台清單
|
|
$machines = Machine::all();
|
|
|
|
return view('admin.sales.pickup-codes.index', [
|
|
'title' => '取貨碼設定',
|
|
'description' => '產生與管理商品取貨驗證碼',
|
|
'pickupCodes' => $pickupCodes,
|
|
'machines' => $machines,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 產生取貨碼
|
|
*/
|
|
public function storePickupCode(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'machine_id' => 'required|exists:machines,id',
|
|
'slot_no' => 'required|string',
|
|
'expires_hours' => 'nullable|integer|min:1|max:720', // 最長一個月
|
|
'custom_code' => 'nullable|string|min:4|max:12',
|
|
]);
|
|
|
|
$machine = Machine::findOrFail($validated['machine_id']);
|
|
$expiresAt = now()->addHours((int) ($request->expires_hours ?? 24));
|
|
|
|
$pickupCode = PickupCode::create([
|
|
'machine_id' => $validated['machine_id'],
|
|
'slot_no' => $validated['slot_no'],
|
|
'code' => $request->custom_code ?? PickupCode::generateUniqueCode($validated['machine_id']),
|
|
'expires_at' => $expiresAt,
|
|
'status' => 'active',
|
|
'company_id' => $machine->company_id,
|
|
'created_by' => Auth::id(),
|
|
]);
|
|
|
|
return back()->with('success', __('Pickup code generated: :code', ['code' => $pickupCode->code]));
|
|
}
|
|
|
|
/**
|
|
* 更新取貨碼 (僅限修改時間)
|
|
*/
|
|
public function updatePickupCode(Request $request, PickupCode $pickupCode)
|
|
{
|
|
$validated = $request->validate([
|
|
'expires_at' => 'required|date|after:now',
|
|
]);
|
|
|
|
$pickupCode->update([
|
|
'expires_at' => $validated['expires_at'],
|
|
]);
|
|
|
|
return back()->with('success', __('Pickup code updated.'));
|
|
}
|
|
|
|
/**
|
|
* 刪除/取消取貨碼
|
|
*/
|
|
public function destroyPickupCode(PickupCode $pickupCode)
|
|
{
|
|
$pickupCode->update(['status' => 'cancelled']);
|
|
return back()->with('success', __('Pickup code cancelled.'));
|
|
}
|
|
|
|
// 購買單
|
|
public function orders()
|
|
{
|
|
return view('admin.placeholder', [
|
|
'title' => '購買單',
|
|
'description' => '購買訂單管理',
|
|
]);
|
|
}
|
|
|
|
// 促銷時段設定
|
|
public function promotions()
|
|
{
|
|
return view('admin.placeholder', [
|
|
'title' => '促銷時段設定',
|
|
'description' => '促銷活動時間設定',
|
|
]);
|
|
}
|
|
|
|
// 通行碼設定
|
|
public function passCodes(Request $request)
|
|
{
|
|
$query = PassCode::with(['machine', 'creator'])->latest();
|
|
|
|
if ($request->search) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('code', 'like', "%{$request->search}%")
|
|
->orWhere('name', 'like', "%{$request->search}%")
|
|
->orWhereHas('machine', function ($mq) use ($request) {
|
|
$mq->where('name', 'like', "%{$request->search}%")
|
|
->orWhere('serial_no', 'like', "%{$request->search}%");
|
|
});
|
|
});
|
|
}
|
|
|
|
if ($request->status && trim($request->status) !== '') {
|
|
$status = trim($request->status);
|
|
if ($status === 'active') {
|
|
$query->where('status', 'active')
|
|
->where(function ($q) {
|
|
$q->whereNull('expires_at')
|
|
->orWhere('expires_at', '>', now());
|
|
});
|
|
} elseif ($status === 'expired') {
|
|
$query->where('status', 'active')
|
|
->whereNotNull('expires_at')
|
|
->where('expires_at', '<=', now());
|
|
} else {
|
|
$query->where('status', $status);
|
|
}
|
|
}
|
|
|
|
$passCodes = $query->paginate(15)->withQueryString();
|
|
$machines = Machine::all();
|
|
|
|
return view('admin.sales.pass-codes.index', [
|
|
'title' => '通行碼設定',
|
|
'description' => '特殊通行權限碼管理 (測試/補貨用)',
|
|
'passCodes' => $passCodes,
|
|
'machines' => $machines,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 產生通行碼
|
|
*/
|
|
public function storePassCode(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'machine_id' => 'required|exists:machines,id',
|
|
'name' => 'required|string|max:50',
|
|
'expires_days' => 'nullable|integer|min:0',
|
|
'custom_code' => 'required|string|min:4|max:12',
|
|
]);
|
|
|
|
$machine = Machine::findOrFail($validated['machine_id']);
|
|
$expiresAt = $request->expires_days ? now()->addDays((int) $request->expires_days) : null;
|
|
|
|
$passCode = PassCode::create([
|
|
'machine_id' => $validated['machine_id'],
|
|
'name' => $validated['name'] ?? 'Manual Generate',
|
|
'code' => $validated['custom_code'] ?? PassCode::generateUniqueCode($validated['machine_id']),
|
|
'expires_at' => $expiresAt,
|
|
'status' => 'active',
|
|
'company_id' => $machine->company_id,
|
|
'created_by' => Auth::id(),
|
|
]);
|
|
|
|
return back()->with('success', __('Pass code created: :code', ['code' => $passCode->code]));
|
|
}
|
|
|
|
/**
|
|
* 更新通行碼
|
|
*/
|
|
public function updatePassCode(Request $request, PassCode $passCode)
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'nullable|string|max:50',
|
|
'expires_at' => 'nullable|date',
|
|
'status' => 'nullable|in:active,disabled',
|
|
]);
|
|
|
|
// 確保 expires_at 為空字串時轉為 null
|
|
if (isset($validated['expires_at']) && empty($validated['expires_at'])) {
|
|
$validated['expires_at'] = null;
|
|
}
|
|
|
|
$passCode->update(array_filter($validated, function ($value, $key) use ($request) {
|
|
return $request->has($key);
|
|
}, ARRAY_FILTER_USE_BOTH));
|
|
|
|
return back()->with('success', __('Pass code updated.'));
|
|
}
|
|
|
|
/**
|
|
* 刪除通行碼 (改為停用)
|
|
*/
|
|
public function destroyPassCode(PassCode $passCode)
|
|
{
|
|
$passCode->update(['status' => 'disabled']);
|
|
return back()->with('success', __('Pass code cancelled.'));
|
|
}
|
|
|
|
// 來店禮設定
|
|
public function storeGifts()
|
|
{
|
|
return view('admin.placeholder', [
|
|
'title' => '來店禮設定',
|
|
'description' => '來店優惠活動設定',
|
|
]);
|
|
}
|
|
}
|