From 810c1f67ddc9ee5758b480ad3a90befec76638a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 6 Mar 2026 21:38:41 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[finance]=20=EA=B2=BD=EC=A1=B0=EC=82=AC?= =?UTF-8?q?=EB=B9=84=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 거래처 경조사비 관리대장 CRUD (등록/수정/삭제) - 축의/부조 구분, 부조금(현금/계좌이체/카드), 선물(종류/금액) 관리 - 연도별 필터, 구분별 필터, 거래처/내역 검색 - 통계 카드 (총건수, 총금액, 부조금 합계, 선물 합계, 축의/부조 비율) - CSV 내보내기 - 라우트: /finance/condolence-expenses --- .../Finance/CondolenceExpenseController.php | 170 ++++++ app/Models/Finance/CondolenceExpense.php | 48 ++ .../finance/condolence-expenses.blade.php | 512 ++++++++++++++++++ routes/web.php | 9 + 4 files changed, 739 insertions(+) create mode 100644 app/Http/Controllers/Finance/CondolenceExpenseController.php create mode 100644 app/Models/Finance/CondolenceExpense.php create mode 100644 resources/views/finance/condolence-expenses.blade.php diff --git a/app/Http/Controllers/Finance/CondolenceExpenseController.php b/app/Http/Controllers/Finance/CondolenceExpenseController.php new file mode 100644 index 00000000..166b18ae --- /dev/null +++ b/app/Http/Controllers/Finance/CondolenceExpenseController.php @@ -0,0 +1,170 @@ +header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('finance.condolence-expenses')); + } + + return view('finance.condolence-expenses'); + } + + public function list(Request $request): JsonResponse + { + $tenantId = session('selected_tenant_id', 1); + $query = CondolenceExpense::forTenant($tenantId); + + if ($year = $request->input('year')) { + $query->whereYear('event_date', $year); + } + + if ($category = $request->input('category')) { + if ($category !== 'all') { + $query->where('category', $category); + } + } + + if ($search = $request->input('search')) { + $query->where(function ($q) use ($search) { + $q->where('partner_name', 'like', "%{$search}%") + ->orWhere('description', 'like', "%{$search}%") + ->orWhere('memo', 'like', "%{$search}%"); + }); + } + + $records = $query->orderBy('event_date', 'desc') + ->orderBy('id', 'desc') + ->get() + ->map(fn ($item) => [ + 'id' => $item->id, + 'event_date' => $item->event_date?->format('Y-m-d'), + 'expense_date' => $item->expense_date?->format('Y-m-d'), + 'partner_name' => $item->partner_name, + 'description' => $item->description, + 'category' => $item->category, + 'has_cash' => $item->has_cash, + 'cash_method' => $item->cash_method, + 'cash_amount' => $item->cash_amount, + 'has_gift' => $item->has_gift, + 'gift_type' => $item->gift_type, + 'gift_amount' => $item->gift_amount, + 'total_amount' => $item->total_amount, + 'memo' => $item->memo, + ]); + + $all = CondolenceExpense::forTenant($tenantId); + if ($year) { + $all = $all->whereYear('event_date', $year); + } + $all = $all->get(); + + $stats = [ + 'totalCount' => $all->count(), + 'totalAmount' => $all->sum('total_amount'), + 'cashTotal' => $all->sum('cash_amount'), + 'giftTotal' => $all->sum('gift_amount'), + 'congratulationCount' => $all->where('category', 'congratulation')->count(), + 'condolenceCount' => $all->where('category', 'condolence')->count(), + ]; + + return response()->json([ + 'success' => true, + 'data' => $records, + 'stats' => $stats, + ]); + } + + public function store(Request $request): JsonResponse + { + $request->validate([ + 'partner_name' => 'required|string|max:100', + 'category' => 'required|in:congratulation,condolence', + ]); + + $tenantId = session('selected_tenant_id', 1); + + $cashAmount = (int) $request->input('cash_amount', 0); + $giftAmount = (int) $request->input('gift_amount', 0); + + CondolenceExpense::create([ + 'tenant_id' => $tenantId, + 'event_date' => $request->input('event_date'), + 'expense_date' => $request->input('expense_date'), + 'partner_name' => $request->input('partner_name'), + 'description' => $request->input('description'), + 'category' => $request->input('category'), + 'has_cash' => $request->boolean('has_cash'), + 'cash_method' => $request->input('cash_method'), + 'cash_amount' => $cashAmount, + 'has_gift' => $request->boolean('has_gift'), + 'gift_type' => $request->input('gift_type'), + 'gift_amount' => $giftAmount, + 'total_amount' => $cashAmount + $giftAmount, + 'memo' => $request->input('memo'), + 'created_by' => auth()->id(), + ]); + + return response()->json([ + 'success' => true, + 'message' => '경조사비가 등록되었습니다.', + ]); + } + + public function update(Request $request, int $id): JsonResponse + { + $tenantId = session('selected_tenant_id', 1); + $item = CondolenceExpense::forTenant($tenantId)->findOrFail($id); + + $request->validate([ + 'partner_name' => 'required|string|max:100', + 'category' => 'required|in:congratulation,condolence', + ]); + + $cashAmount = (int) $request->input('cash_amount', 0); + $giftAmount = (int) $request->input('gift_amount', 0); + + $item->update([ + 'event_date' => $request->input('event_date'), + 'expense_date' => $request->input('expense_date'), + 'partner_name' => $request->input('partner_name'), + 'description' => $request->input('description'), + 'category' => $request->input('category'), + 'has_cash' => $request->boolean('has_cash'), + 'cash_method' => $request->input('cash_method'), + 'cash_amount' => $cashAmount, + 'has_gift' => $request->boolean('has_gift'), + 'gift_type' => $request->input('gift_type'), + 'gift_amount' => $giftAmount, + 'total_amount' => $cashAmount + $giftAmount, + 'memo' => $request->input('memo'), + ]); + + return response()->json([ + 'success' => true, + 'message' => '경조사비가 수정되었습니다.', + ]); + } + + public function destroy(int $id): JsonResponse + { + $tenantId = session('selected_tenant_id', 1); + $item = CondolenceExpense::forTenant($tenantId)->findOrFail($id); + $item->delete(); + + return response()->json([ + 'success' => true, + 'message' => '경조사비가 삭제되었습니다.', + ]); + } +} diff --git a/app/Models/Finance/CondolenceExpense.php b/app/Models/Finance/CondolenceExpense.php new file mode 100644 index 00000000..9943f38b --- /dev/null +++ b/app/Models/Finance/CondolenceExpense.php @@ -0,0 +1,48 @@ + 'date', + 'expense_date' => 'date', + 'has_cash' => 'boolean', + 'has_gift' => 'boolean', + 'cash_amount' => 'integer', + 'gift_amount' => 'integer', + 'total_amount' => 'integer', + 'options' => 'array', + ]; + + public function scopeForTenant($query, $tenantId) + { + return $query->where('tenant_id', $tenantId); + } +} diff --git a/resources/views/finance/condolence-expenses.blade.php b/resources/views/finance/condolence-expenses.blade.php new file mode 100644 index 00000000..7419ccb6 --- /dev/null +++ b/resources/views/finance/condolence-expenses.blade.php @@ -0,0 +1,512 @@ +@extends('layouts.app') + +@section('title', '경조사비 관리') + +@push('styles') + +@endpush + +@section('content') +
+ + {{-- 헤더 --}} +
+
+

경조사비 관리

+

거래처 경조사비 관리대장

+
+
+ + +
+
+ + {{-- 통계 카드 --}} +
+
+
총 건수
+
+
+
+
총 금액
+
+
+
+
부조금 합계
+
+
+
+
선물 합계
+
+
+
+
+
+
축의
+
+
+
/
+
+
부조
+
+
+
+
+
+ + {{-- 필터 --}} +
+
+
+ +
+
+ +
+
+
+ + +
+
+
+
+ + {{-- 테이블 --}} +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + {{-- 합계 행 --}} + + + + + + + + + + +
No경조사일자지출일자거래처명내역구분부조금지출방법부조금액선물선물종류선물금액총금액비고관리
합계
+
+
+ + {{-- 등록/수정 모달 --}} + +
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 7ecbbace..07e82b72 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1449,6 +1449,15 @@ Route::put('/{id}', [\App\Http\Controllers\Finance\VatRecordController::class, 'update'])->name('update'); Route::delete('/{id}', [\App\Http\Controllers\Finance\VatRecordController::class, 'destroy'])->name('destroy'); }); + + // 경조사비 관리 + Route::get('/condolence-expenses', [\App\Http\Controllers\Finance\CondolenceExpenseController::class, 'index'])->name('condolence-expenses'); + Route::prefix('condolence-expenses')->name('condolence-expenses.')->group(function () { + Route::get('/list', [\App\Http\Controllers\Finance\CondolenceExpenseController::class, 'list'])->name('list'); + Route::post('/store', [\App\Http\Controllers\Finance\CondolenceExpenseController::class, 'store'])->name('store'); + Route::put('/{id}', [\App\Http\Controllers\Finance\CondolenceExpenseController::class, 'update'])->name('update'); + Route::delete('/{id}', [\App\Http\Controllers\Finance\CondolenceExpenseController::class, 'destroy'])->name('destroy'); + }); }); /*