financeService = $financeService; } public function index(Request $request) { $filters = $request->only(['search', 'category', 'date_start', 'date_end', 'sort_field', 'sort_direction', 'per_page']); $fees = $this->financeService->getUtilityFees($filters)->withQueryString(); $availableCategories = $this->financeService->getUniqueCategories(); return Inertia::render('UtilityFee/Index', [ 'fees' => $fees, 'availableCategories' => $availableCategories, 'filters' => $filters, ]); } public function store(Request $request) { $validated = $request->validate([ 'transaction_date' => 'nullable|date', 'due_date' => 'required|date', 'category' => 'required|string|max:255', 'amount' => 'required|numeric|min:0', 'invoice_number' => 'nullable|string|max:255', 'description' => 'nullable|string', ]); $validated['payment_status'] = $this->determineStatus($validated); $fee = UtilityFee::create($validated); activity() ->performedOn($fee) ->causedBy(auth()->user()) ->event('created') ->log('created'); return redirect()->back(); } public function update(Request $request, UtilityFee $utility_fee) { $validated = $request->validate([ 'transaction_date' => 'nullable|date', 'due_date' => 'required|date', 'category' => 'required|string|max:255', 'amount' => 'required|numeric|min:0', 'invoice_number' => 'nullable|string|max:255', 'description' => 'nullable|string', ]); $validated['payment_status'] = $this->determineStatus($validated); $utility_fee->update($validated); activity() ->performedOn($utility_fee) ->causedBy(auth()->user()) ->event('updated') ->log('updated'); return redirect()->back(); } /** * 判定繳費狀態 */ private function determineStatus(array $data): string { if (!empty($data['transaction_date'])) { return UtilityFee::STATUS_PAID; } if (!empty($data['due_date']) && now()->startOfDay()->gt(\Illuminate\Support\Carbon::parse($data['due_date']))) { return UtilityFee::STATUS_OVERDUE; } return UtilityFee::STATUS_PENDING; } public function destroy(UtilityFee $utility_fee) { activity() ->performedOn($utility_fee) ->causedBy(auth()->user()) ->event('deleted') ->log('deleted'); // 刪除實體檔案 (如果 cascade 沒處理或是想要手動清理) foreach ($utility_fee->attachments as $attachment) { Storage::disk('public')->delete($attachment->file_path); } $utility_fee->delete(); return redirect()->back(); } /** * 獲取附件列表 */ public function attachments(UtilityFee $utility_fee) { return response()->json([ 'attachments' => $utility_fee->attachments()->orderBy('created_at', 'desc')->get() ]); } /** * 上傳附件 */ public function uploadAttachment(Request $request, UtilityFee $utility_fee) { $request->validate([ 'file' => 'required|file|mimes:jpeg,jpg,png,webp,pdf|max:2048', // 2MB ]); // 檢查數量限制 (最多 3 張) if ($utility_fee->attachments()->count() >= 3) { return response()->json(['message' => '附件數量已達上限 (最多 3 個)'], 422); } $file = $request->file('file'); $path = $file->store("utility-fee-attachments/{$utility_fee->id}", 'public'); $attachment = $utility_fee->attachments()->create([ 'file_path' => $path, 'original_name' => $file->getClientOriginalName(), 'mime_type' => $file->getMimeType(), 'size' => $file->getSize(), ]); activity() ->performedOn($utility_fee) ->causedBy(auth()->user()) ->event('attachment_uploaded') ->log("uploaded attachment: {$attachment->original_name}"); return response()->json([ 'message' => '上傳成功', 'attachment' => $attachment ]); } /** * 刪除附件 */ public function deleteAttachment(UtilityFee $utility_fee, UtilityFeeAttachment $attachment) { // 確保附件屬於該費用 if ($attachment->utility_fee_id !== $utility_fee->id) { abort(403); } Storage::disk('public')->delete($attachment->file_path); $attachment->delete(); activity() ->performedOn($utility_fee) ->causedBy(auth()->user()) ->event('attachment_deleted') ->log("deleted attachment: {$attachment->original_name}"); return response()->json(['message' => '刪除成功']); } }