From 0d111bc8ba861a5eae566dea20682f54273670c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 11:24:11 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?(=EC=9A=B4=ED=96=89=EA=B8=B0=EB=A1=9D=EB=B6=80)=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VehicleLog 모델 생성 - VehicleLogController (CRUD, 엑셀 다운로드) - 차량일지 라우트 추가 (/finance/vehicle-logs/*) - React 기반 UI (vehicle-logs.blade.php) - VehicleLogMenuSeeder (법인차량관리 > 차량일지 메뉴) Co-Authored-By: Claude Opus 4.5 --- .../Finance/VehicleLogController.php | 266 +++++++ app/Models/VehicleLog.php | 86 +++ database/seeders/VehicleLogMenuSeeder.php | 110 +++ .../views/finance/vehicle-logs.blade.php | 674 ++++++++++++++++++ routes/web.php | 9 + 5 files changed, 1145 insertions(+) create mode 100644 app/Http/Controllers/Finance/VehicleLogController.php create mode 100644 app/Models/VehicleLog.php create mode 100644 database/seeders/VehicleLogMenuSeeder.php create mode 100644 resources/views/finance/vehicle-logs.blade.php diff --git a/app/Http/Controllers/Finance/VehicleLogController.php b/app/Http/Controllers/Finance/VehicleLogController.php new file mode 100644 index 00000000..8b240193 --- /dev/null +++ b/app/Http/Controllers/Finance/VehicleLogController.php @@ -0,0 +1,266 @@ +header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('finance.vehicle-logs')); + } + + $tenantId = session('tenant_id', 1); + $vehicles = CorporateVehicle::where('tenant_id', $tenantId) + ->where('status', 'active') + ->orderBy('plate_number') + ->get(); + + return view('finance.vehicle-logs', [ + 'vehicles' => $vehicles, + 'tripTypes' => VehicleLog::tripTypeLabels(), + 'locationTypes' => VehicleLog::locationTypeLabels(), + ]); + } + + public function list(Request $request): JsonResponse + { + $tenantId = session('tenant_id', 1); + + $request->validate([ + 'vehicle_id' => 'required|integer', + 'year' => 'required|integer', + 'month' => 'required|integer|min:1|max:12', + ]); + + $vehicleId = $request->vehicle_id; + $year = $request->year; + $month = $request->month; + + // 차량 정보 + $vehicle = CorporateVehicle::where('tenant_id', $tenantId) + ->findOrFail($vehicleId); + + // 해당 월의 운행 기록 + $startDate = sprintf('%04d-%02d-01', $year, $month); + $endDate = date('Y-m-t', strtotime($startDate)); + + $logs = VehicleLog::where('tenant_id', $tenantId) + ->where('vehicle_id', $vehicleId) + ->whereBetween('log_date', [$startDate, $endDate]) + ->orderBy('log_date') + ->orderBy('id') + ->get(); + + // 월별 합계 + $totals = [ + 'business_km' => $logs->whereIn('trip_type', ['commute_to', 'commute_from', 'business'])->sum('distance_km'), + 'personal_km' => $logs->where('trip_type', 'personal')->sum('distance_km'), + 'total_km' => $logs->sum('distance_km'), + ]; + + return response()->json([ + 'success' => true, + 'data' => [ + 'vehicle' => $vehicle, + 'logs' => $logs, + 'totals' => $totals, + ], + ]); + } + + public function store(Request $request): JsonResponse + { + $tenantId = session('tenant_id', 1); + + $request->validate([ + 'vehicle_id' => 'required|integer|exists:corporate_vehicles,id', + 'log_date' => 'required|date', + 'driver_name' => 'required|string|max:50', + 'trip_type' => 'required|in:commute_to,commute_from,business,personal', + 'distance_km' => 'required|integer|min:0', + ]); + + // 해당 차량이 현재 테넌트의 것인지 확인 + CorporateVehicle::where('tenant_id', $tenantId) + ->findOrFail($request->vehicle_id); + + $log = VehicleLog::create([ + 'tenant_id' => $tenantId, + 'vehicle_id' => $request->vehicle_id, + 'log_date' => $request->log_date, + 'department' => $request->department, + 'driver_name' => $request->driver_name, + 'trip_type' => $request->trip_type, + 'departure_type' => $request->departure_type, + 'departure_name' => $request->departure_name, + 'departure_address' => $request->departure_address, + 'arrival_type' => $request->arrival_type, + 'arrival_name' => $request->arrival_name, + 'arrival_address' => $request->arrival_address, + 'distance_km' => $request->distance_km, + 'note' => $request->note, + ]); + + return response()->json([ + 'success' => true, + 'message' => '운행기록이 등록되었습니다.', + 'data' => $log, + ]); + } + + public function update(Request $request, int $id): JsonResponse + { + $tenantId = session('tenant_id', 1); + + $log = VehicleLog::where('tenant_id', $tenantId)->findOrFail($id); + + $request->validate([ + 'log_date' => 'required|date', + 'driver_name' => 'required|string|max:50', + 'trip_type' => 'required|in:commute_to,commute_from,business,personal', + 'distance_km' => 'required|integer|min:0', + ]); + + $log->update([ + 'log_date' => $request->log_date, + 'department' => $request->department, + 'driver_name' => $request->driver_name, + 'trip_type' => $request->trip_type, + 'departure_type' => $request->departure_type, + 'departure_name' => $request->departure_name, + 'departure_address' => $request->departure_address, + 'arrival_type' => $request->arrival_type, + 'arrival_name' => $request->arrival_name, + 'arrival_address' => $request->arrival_address, + 'distance_km' => $request->distance_km, + 'note' => $request->note, + ]); + + return response()->json([ + 'success' => true, + 'message' => '운행기록이 수정되었습니다.', + 'data' => $log, + ]); + } + + public function destroy(int $id): JsonResponse + { + $tenantId = session('tenant_id', 1); + + $log = VehicleLog::where('tenant_id', $tenantId)->findOrFail($id); + $log->delete(); + + return response()->json([ + 'success' => true, + 'message' => '운행기록이 삭제되었습니다.', + ]); + } + + public function export(Request $request): StreamedResponse + { + $tenantId = session('tenant_id', 1); + + $request->validate([ + 'vehicle_id' => 'required|integer', + 'year' => 'required|integer', + 'month' => 'required|integer|min:1|max:12', + ]); + + $vehicleId = $request->vehicle_id; + $year = $request->year; + $month = $request->month; + + $vehicle = CorporateVehicle::where('tenant_id', $tenantId) + ->findOrFail($vehicleId); + + $startDate = sprintf('%04d-%02d-01', $year, $month); + $endDate = date('Y-m-t', strtotime($startDate)); + + $logs = VehicleLog::where('tenant_id', $tenantId) + ->where('vehicle_id', $vehicleId) + ->whereBetween('log_date', [$startDate, $endDate]) + ->orderBy('log_date') + ->orderBy('id') + ->get(); + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setTitle('운행기록부'); + + // 기본 정보 + $sheet->setCellValue('A1', '업무용승용차 운행기록부'); + $sheet->setCellValue('A3', '차량번호'); + $sheet->setCellValue('B3', $vehicle->plate_number); + $sheet->setCellValue('C3', '차종'); + $sheet->setCellValue('D3', $vehicle->model); + $sheet->setCellValue('E3', '구분'); + $sheet->setCellValue('F3', $this->getOwnershipTypeLabel($vehicle->ownership_type)); + $sheet->setCellValue('A4', '조회기간'); + $sheet->setCellValue('B4', sprintf('%d년 %d월', $year, $month)); + + // 헤더 + $headers = ['일자', '부서', '성명', '구분', '출발지', '도착지', '주행km', '비고']; + $col = 'A'; + foreach ($headers as $header) { + $sheet->setCellValue($col . '6', $header); + $col++; + } + + // 데이터 + $row = 7; + $tripTypeLabels = VehicleLog::tripTypeLabels(); + + foreach ($logs as $log) { + $sheet->setCellValue('A' . $row, $log->log_date->format('Y-m-d')); + $sheet->setCellValue('B' . $row, $log->department ?? ''); + $sheet->setCellValue('C' . $row, $log->driver_name); + $sheet->setCellValue('D' . $row, $tripTypeLabels[$log->trip_type] ?? $log->trip_type); + $sheet->setCellValue('E' . $row, $log->departure_name ?? ''); + $sheet->setCellValue('F' . $row, $log->arrival_name ?? ''); + $sheet->setCellValue('G' . $row, $log->distance_km); + $sheet->setCellValue('H' . $row, $log->note ?? ''); + $row++; + } + + // 합계 + $businessKm = $logs->whereIn('trip_type', ['commute_to', 'commute_from', 'business'])->sum('distance_km'); + $personalKm = $logs->where('trip_type', 'personal')->sum('distance_km'); + $totalKm = $logs->sum('distance_km'); + + $sheet->setCellValue('A' . $row, '합계'); + $sheet->setCellValue('F' . $row, '업무용: ' . number_format($businessKm) . 'km'); + $sheet->setCellValue('G' . $row, number_format($totalKm)); + $sheet->setCellValue('H' . $row, '비업무: ' . number_format($personalKm) . 'km'); + + $filename = sprintf('운행기록부_%s_%d년%d월.xlsx', $vehicle->plate_number, $year, $month); + + return response()->streamDownload(function () use ($spreadsheet) { + $writer = new Xlsx($spreadsheet); + $writer->save('php://output'); + }, $filename, [ + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ]); + } + + private function getOwnershipTypeLabel(string $type): string + { + return match ($type) { + 'corporate' => '회사', + 'rent' => '렌트', + 'lease' => '리스', + default => $type, + }; + } +} diff --git a/app/Models/VehicleLog.php b/app/Models/VehicleLog.php new file mode 100644 index 00000000..30c05dcc --- /dev/null +++ b/app/Models/VehicleLog.php @@ -0,0 +1,86 @@ + 'date', + 'distance_km' => 'integer', + ]; + + // trip_type 상수 + public const TRIP_TYPE_COMMUTE_TO = 'commute_to'; + public const TRIP_TYPE_COMMUTE_FROM = 'commute_from'; + public const TRIP_TYPE_BUSINESS = 'business'; + public const TRIP_TYPE_PERSONAL = 'personal'; + + // location_type 상수 + public const LOCATION_TYPE_HOME = 'home'; + public const LOCATION_TYPE_OFFICE = 'office'; + public const LOCATION_TYPE_CLIENT = 'client'; + public const LOCATION_TYPE_OTHER = 'other'; + + public static function tripTypeLabels(): array + { + return [ + self::TRIP_TYPE_COMMUTE_TO => '출근용', + self::TRIP_TYPE_COMMUTE_FROM => '퇴근용', + self::TRIP_TYPE_BUSINESS => '업무용', + self::TRIP_TYPE_PERSONAL => '비업무', + ]; + } + + public static function locationTypeLabels(): array + { + return [ + self::LOCATION_TYPE_HOME => '자택', + self::LOCATION_TYPE_OFFICE => '회사', + self::LOCATION_TYPE_CLIENT => '거래처', + self::LOCATION_TYPE_OTHER => '기타', + ]; + } + + public function vehicle(): BelongsTo + { + return $this->belongsTo(CorporateVehicle::class, 'vehicle_id'); + } + + public function getTripTypeLabelAttribute(): string + { + return self::tripTypeLabels()[$this->trip_type] ?? $this->trip_type; + } + + public function getDepartureTypeLabelAttribute(): string + { + return self::locationTypeLabels()[$this->departure_type] ?? ($this->departure_type ?? ''); + } + + public function getArrivalTypeLabelAttribute(): string + { + return self::locationTypeLabels()[$this->arrival_type] ?? ($this->arrival_type ?? ''); + } +} diff --git a/database/seeders/VehicleLogMenuSeeder.php b/database/seeders/VehicleLogMenuSeeder.php new file mode 100644 index 00000000..be81b80d --- /dev/null +++ b/database/seeders/VehicleLogMenuSeeder.php @@ -0,0 +1,110 @@ + 법인차량관리 하위에 차량일지 메뉴 추가 + */ +class VehicleLogMenuSeeder extends Seeder +{ + public function run(): void + { + $tenantId = 1; + + // 법인차량관리 메뉴 찾기 + $vehicleMenu = Menu::where('tenant_id', $tenantId) + ->where('name', '법인차량관리') + ->first(); + + if (!$vehicleMenu) { + $this->command->error('법인차량관리 메뉴를 찾을 수 없습니다.'); + return; + } + + // 차량일지 메뉴가 이미 있는지 확인 + $existingMenu = Menu::where('tenant_id', $tenantId) + ->where('name', '차량일지') + ->where('parent_id', $vehicleMenu->id) + ->first(); + + if ($existingMenu) { + $this->command->info('차량일지 메뉴가 이미 존재합니다.'); + return; + } + + // 법인차량관리가 그룹 메뉴인지 확인 + // 그룹 메뉴가 아니면 그룹으로 변경 + if ($vehicleMenu->url) { + // 기존 URL을 차량목록으로 변경 + $vehicleListMenu = Menu::create([ + 'tenant_id' => $tenantId, + 'parent_id' => $vehicleMenu->id, + 'name' => '차량목록', + 'url' => $vehicleMenu->url, + 'icon' => 'car', + 'sort_order' => 1, + 'is_active' => true, + ]); + $this->command->info("차량목록 하위 메뉴 생성: {$vehicleListMenu->url}"); + + // 법인차량관리를 그룹 메뉴로 변경 + $vehicleMenu->url = null; + $vehicleMenu->save(); + $this->command->info('법인차량관리를 그룹 메뉴로 변경'); + + // 차량일지 메뉴 생성 + $vehicleLogMenu = Menu::create([ + 'tenant_id' => $tenantId, + 'parent_id' => $vehicleMenu->id, + 'name' => '차량일지', + 'url' => '/finance/vehicle-logs', + 'icon' => 'file-text', + 'sort_order' => 2, + 'is_active' => true, + ]); + $this->command->info("차량일지 메뉴 생성: {$vehicleLogMenu->url}"); + + // 차량정비 메뉴가 있으면 순서 조정 + $maintenanceMenu = Menu::where('tenant_id', $tenantId) + ->where('name', '차량정비') + ->where('parent_id', $vehicleMenu->id) + ->first(); + + if ($maintenanceMenu) { + $maintenanceMenu->sort_order = 3; + $maintenanceMenu->save(); + } + } else { + // 이미 그룹 메뉴인 경우 차량일지만 추가 + // 기존 하위 메뉴 순서 확인 + $maxSortOrder = Menu::where('parent_id', $vehicleMenu->id) + ->max('sort_order') ?? 0; + + $vehicleLogMenu = Menu::create([ + 'tenant_id' => $tenantId, + 'parent_id' => $vehicleMenu->id, + 'name' => '차량일지', + 'url' => '/finance/vehicle-logs', + 'icon' => 'file-text', + 'sort_order' => $maxSortOrder + 1, + 'is_active' => true, + ]); + $this->command->info("차량일지 메뉴 생성: {$vehicleLogMenu->url}"); + } + + // 결과 출력 + $this->command->info(''); + $this->command->info('=== 법인차량관리 하위 메뉴 ==='); + $children = Menu::where('parent_id', $vehicleMenu->id) + ->orderBy('sort_order') + ->get(['name', 'url', 'sort_order']); + + foreach ($children as $child) { + $this->command->info("{$child->sort_order}. {$child->name} ({$child->url})"); + } + } +} diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php new file mode 100644 index 00000000..96aae406 --- /dev/null +++ b/resources/views/finance/vehicle-logs.blade.php @@ -0,0 +1,674 @@ +@extends('layouts.app') + +@section('title', '차량일지') + +@push('styles') + +@endpush + +@section('content') +
+@endsection + +@push('scripts') + + + + + +@verbatim + +@endverbatim +@endpush diff --git a/routes/web.php b/routes/web.php index 23c46bda..5de1cafd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -815,6 +815,15 @@ Route::post('/corporate-vehicles', [\App\Http\Controllers\Finance\CorporateVehicleController::class, 'store'])->name('corporate-vehicles.store'); Route::put('/corporate-vehicles/{id}', [\App\Http\Controllers\Finance\CorporateVehicleController::class, 'update'])->name('corporate-vehicles.update'); Route::delete('/corporate-vehicles/{id}', [\App\Http\Controllers\Finance\CorporateVehicleController::class, 'destroy'])->name('corporate-vehicles.destroy'); + + // 차량일지 (운행기록부) + Route::get('/vehicle-logs', [\App\Http\Controllers\Finance\VehicleLogController::class, 'index'])->name('vehicle-logs'); + Route::get('/vehicle-logs/list', [\App\Http\Controllers\Finance\VehicleLogController::class, 'list'])->name('vehicle-logs.list'); + Route::post('/vehicle-logs', [\App\Http\Controllers\Finance\VehicleLogController::class, 'store'])->name('vehicle-logs.store'); + Route::put('/vehicle-logs/{id}', [\App\Http\Controllers\Finance\VehicleLogController::class, 'update'])->name('vehicle-logs.update'); + Route::delete('/vehicle-logs/{id}', [\App\Http\Controllers\Finance\VehicleLogController::class, 'destroy'])->name('vehicle-logs.destroy'); + Route::get('/vehicle-logs/export', [\App\Http\Controllers\Finance\VehicleLogController::class, 'export'])->name('vehicle-logs.export'); + Route::get('/vehicle-maintenance', function () { if (request()->header('HX-Request')) { return response('', 200)->header('HX-Redirect', route('finance.vehicle-maintenance')); From 057ba589f06451bec613ed6df5eb81f5e03be2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 12:28:48 +0900 Subject: [PATCH 02/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=ED=96=89=20=ED=81=B4=EB=A6=AD?= =?UTF-8?q?=20=EC=8B=9C=20=EC=88=98=EC=A0=95=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=97=B4=EB=A6=AC=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/finance/vehicle-logs.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 96aae406..4e06be87 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -412,7 +412,7 @@ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus ) : ( logs.map(log => ( - + handleEdit(log)} className="border-b border-gray-100 hover:bg-emerald-50 cursor-pointer transition-colors"> {log.log_date?.split('T')[0]} From cf37e29659560bcc997e443275fd3a5ba3298fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 12:33:14 +0900 Subject: [PATCH 03/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EA=B8=B0=EA=B0=84=20=EC=84=A0=ED=83=9D=20UI=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20(=EC=8B=9C=EC=9E=91=EC=9D=BC~=EC=A2=85=EB=A3=8C?= =?UTF-8?q?=EC=9D=BC,=20=EC=9D=B4=EB=B2=88=EB=8B=AC/=EC=A7=80=EB=82=9C?= =?UTF-8?q?=EB=8B=AC=20=EB=B2=84=ED=8A=BC,=20=EC=A1=B0=ED=9A=8C=EA=B1=B4?= =?UTF-8?q?=EC=88=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Finance/VehicleLogController.php | 31 +++--- .../views/finance/vehicle-logs.blade.php | 99 ++++++++++++------- 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/app/Http/Controllers/Finance/VehicleLogController.php b/app/Http/Controllers/Finance/VehicleLogController.php index 8b240193..0963376f 100644 --- a/app/Http/Controllers/Finance/VehicleLogController.php +++ b/app/Http/Controllers/Finance/VehicleLogController.php @@ -40,21 +40,22 @@ public function list(Request $request): JsonResponse $request->validate([ 'vehicle_id' => 'required|integer', - 'year' => 'required|integer', - 'month' => 'required|integer|min:1|max:12', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', ]); $vehicleId = $request->vehicle_id; - $year = $request->year; - $month = $request->month; + $startDate = $request->start_date; + $endDate = $request->end_date; // 차량 정보 $vehicle = CorporateVehicle::where('tenant_id', $tenantId) ->findOrFail($vehicleId); - // 해당 월의 운행 기록 - $startDate = sprintf('%04d-%02d-01', $year, $month); - $endDate = date('Y-m-t', strtotime($startDate)); + // 전체 운행기록 수 (해당 차량) + $totalCount = VehicleLog::where('tenant_id', $tenantId) + ->where('vehicle_id', $vehicleId) + ->count(); $logs = VehicleLog::where('tenant_id', $tenantId) ->where('vehicle_id', $vehicleId) @@ -76,6 +77,7 @@ public function list(Request $request): JsonResponse 'vehicle' => $vehicle, 'logs' => $logs, 'totals' => $totals, + 'totalCount' => $totalCount, ], ]); } @@ -174,20 +176,17 @@ public function export(Request $request): StreamedResponse $request->validate([ 'vehicle_id' => 'required|integer', - 'year' => 'required|integer', - 'month' => 'required|integer|min:1|max:12', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', ]); $vehicleId = $request->vehicle_id; - $year = $request->year; - $month = $request->month; + $startDate = $request->start_date; + $endDate = $request->end_date; $vehicle = CorporateVehicle::where('tenant_id', $tenantId) ->findOrFail($vehicleId); - $startDate = sprintf('%04d-%02d-01', $year, $month); - $endDate = date('Y-m-t', strtotime($startDate)); - $logs = VehicleLog::where('tenant_id', $tenantId) ->where('vehicle_id', $vehicleId) ->whereBetween('log_date', [$startDate, $endDate]) @@ -208,7 +207,7 @@ public function export(Request $request): StreamedResponse $sheet->setCellValue('E3', '구분'); $sheet->setCellValue('F3', $this->getOwnershipTypeLabel($vehicle->ownership_type)); $sheet->setCellValue('A4', '조회기간'); - $sheet->setCellValue('B4', sprintf('%d년 %d월', $year, $month)); + $sheet->setCellValue('B4', sprintf('%s ~ %s', $startDate, $endDate)); // 헤더 $headers = ['일자', '부서', '성명', '구분', '출발지', '도착지', '주행km', '비고']; @@ -244,7 +243,7 @@ public function export(Request $request): StreamedResponse $sheet->setCellValue('G' . $row, number_format($totalKm)); $sheet->setCellValue('H' . $row, '비업무: ' . number_format($personalKm) . 'km'); - $filename = sprintf('운행기록부_%s_%d년%d월.xlsx', $vehicle->plate_number, $year, $month); + $filename = sprintf('운행기록부_%s_%s_%s.xlsx', $vehicle->plate_number, $startDate, $endDate); return response()->streamDownload(function () use ($spreadsheet) { $writer = new Xlsx($spreadsheet); diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 4e06be87..5126a521 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -58,14 +58,29 @@ function VehicleLogsManagement() { const { vehicles, tripTypes, locationTypes } = window.INITIAL_DATA; + // 이번 달 시작일/종료일 계산 + const getMonthRange = (date) => { + const year = date.getFullYear(); + const month = date.getMonth(); + const firstDay = new Date(year, month, 1); + const lastDay = new Date(year, month + 1, 0); + return { + start: firstDay.toISOString().split('T')[0], + end: lastDay.toISOString().split('T')[0] + }; + }; + const currentDate = new Date(); + const thisMonthRange = getMonthRange(currentDate); + const [selectedVehicle, setSelectedVehicle] = useState(vehicles[0]?.id || null); - const [selectedYear, setSelectedYear] = useState(currentDate.getFullYear()); - const [selectedMonth, setSelectedMonth] = useState(currentDate.getMonth() + 1); + const [startDate, setStartDate] = useState(thisMonthRange.start); + const [endDate, setEndDate] = useState(thisMonthRange.end); const [vehicleInfo, setVehicleInfo] = useState(null); const [logs, setLogs] = useState([]); const [totals, setTotals] = useState({ business_km: 0, personal_km: 0, total_km: 0 }); + const [totalCount, setTotalCount] = useState(0); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); @@ -103,8 +118,8 @@ function VehicleLogsManagement() { setLoading(true); const params = new URLSearchParams({ vehicle_id: selectedVehicle, - year: selectedYear, - month: selectedMonth + start_date: startDate, + end_date: endDate }); const response = await fetch(`/finance/vehicle-logs/list?${params}`); const result = await response.json(); @@ -112,6 +127,7 @@ function VehicleLogsManagement() { setVehicleInfo(result.data.vehicle); setLogs(result.data.logs); setTotals(result.data.totals); + setTotalCount(result.data.totalCount || 0); } } catch (error) { console.error('Failed to load logs:', error); @@ -122,36 +138,31 @@ function VehicleLogsManagement() { useEffect(() => { loadLogs(); - }, [selectedVehicle, selectedYear, selectedMonth]); + }, [selectedVehicle, startDate, endDate]); - const handlePrevMonth = () => { - if (selectedMonth === 1) { - setSelectedMonth(12); - setSelectedYear(prev => prev - 1); - } else { - setSelectedMonth(prev => prev - 1); - } + const handleThisMonth = () => { + const range = getMonthRange(new Date()); + setStartDate(range.start); + setEndDate(range.end); }; - const handleNextMonth = () => { - if (selectedMonth === 12) { - setSelectedMonth(1); - setSelectedYear(prev => prev + 1); - } else { - setSelectedMonth(prev => prev + 1); - } + const handleLastMonth = () => { + const lastMonth = new Date(); + lastMonth.setMonth(lastMonth.getMonth() - 1); + const range = getMonthRange(lastMonth); + setStartDate(range.start); + setEndDate(range.end); }; const handleAdd = () => { - const today = new Date(); - const dateStr = `${selectedYear}-${String(selectedMonth).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; + const today = new Date().toISOString().split('T')[0]; setModalMode('add'); setEditingItem(null); setFormData({ ...initialFormState, vehicle_id: selectedVehicle, - log_date: dateStr, + log_date: today, driver_name: vehicleInfo?.driver || '' }); setShowModal(true); @@ -248,8 +259,8 @@ function VehicleLogsManagement() { const handleExport = () => { const params = new URLSearchParams({ vehicle_id: selectedVehicle, - year: selectedYear, - month: selectedMonth + start_date: startDate, + end_date: endDate }); window.location.href = `/finance/vehicle-logs/export?${params}`; }; @@ -290,8 +301,7 @@ function VehicleLogsManagement() {
{/* 차량 선택 */} -
- +
setStartDate(e.target.value)} + className="px-3 py-2 border border-gray-300 rounded-lg text-sm" + /> + ~ + setEndDate(e.target.value)} + className="px-3 py-2 border border-gray-300 rounded-lg text-sm" + /> + -
- - {selectedYear}년 {selectedMonth}월 -
-
+ + {/* 조회 건수 */} +
+ 조회: {logs.length}건 / 전체 {totalCount}건 +
From c41dd2221e63eb06edfaf3d3e230f722ddf147c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 12:40:06 +0900 Subject: [PATCH 04/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20selectedYear/selectedMonth=20=EC=B0=B8=EC=A1=B0=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/finance/vehicle-logs.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 5126a521..6b696017 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -395,7 +395,7 @@ className="px-3 py-2 border border-gray-300 text-gray-700 rounded-lg text-sm fon

총 주행거리

{formatNumber(totals.total_km)} km

-

{selectedYear}년 {selectedMonth}월

+

{startDate} ~ {endDate}

From 5cbaff9fe92f247631b0489c36c61fe255a41e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:00:04 +0900 Subject: [PATCH 05/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EC=B6=9C=EB=B0=9C=EC=A7=80/=EB=8F=84?= =?UTF-8?q?=EC=B0=A9=EC=A7=80=20=ED=95=84=EB=93=9C=20=EB=84=88=EB=B9=84=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95=20(=EB=B6=84=EB=A5=98=20=EC=A2=81=EA=B2=8C,?= =?UTF-8?q?=20=EC=A3=BC=EC=86=8C=20=EB=84=93=EA=B2=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/finance/vehicle-logs.blade.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 6b696017..a241e410 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -573,13 +573,13 @@ className="w-full px-3 py-2 border border-gray-300 rounded-lg text-right"

출발지

-
-
+
+
-
+
-
+
도착지

-
-
+
+
-
+
-
+
Date: Tue, 3 Feb 2026 13:05:04 +0900 Subject: [PATCH 06/13] =?UTF-8?q?feat:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EB=B3=B5=EC=82=AC/=EC=B6=9C=EB=B0=9C=E2=86=94=EB=8F=84?= =?UTF-8?q?=EC=B0=A9=20=EA=B5=90=ED=99=98=20=ED=8E=B8=EC=9D=98=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/finance/vehicle-logs.blade.php | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index a241e410..23693002 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -54,6 +54,8 @@ const User = createIcon('user'); const Edit = createIcon('edit'); const Trash2 = createIcon('trash-2'); +const Copy = createIcon('copy'); +const ArrowUpDown = createIcon('arrow-up-down'); function VehicleLogsManagement() { const { vehicles, tripTypes, locationTypes } = window.INITIAL_DATA; @@ -265,6 +267,43 @@ function VehicleLogsManagement() { window.location.href = `/finance/vehicle-logs/export?${params}`; }; + // 복사 기능: 현재 데이터를 복사해서 새 기록 추가 모드로 전환 + const handleCopy = () => { + setModalMode('add'); + setEditingItem(null); + // 날짜를 오늘로 변경 + const today = new Date().toISOString().split('T')[0]; + setFormData(prev => ({ + ...prev, + log_date: today + })); + }; + + // 출발지↔도착지 교환 + 출근↔퇴근 전환 + const handleSwapLocations = () => { + setFormData(prev => { + // 출근↔퇴근 전환 + let newTripType = prev.trip_type; + if (prev.trip_type === 'commute_to') { + newTripType = 'commute_from'; + } else if (prev.trip_type === 'commute_from') { + newTripType = 'commute_to'; + } + + return { + ...prev, + trip_type: newTripType, + // 출발지 ↔ 도착지 교환 + departure_type: prev.arrival_type, + departure_name: prev.arrival_name, + departure_address: prev.arrival_address, + arrival_type: prev.departure_type, + arrival_name: prev.departure_name, + arrival_address: prev.departure_address, + }; + }); + }; + const getTripTypeColor = (type) => { switch(type) { case 'commute_to': return 'bg-blue-100 text-blue-700'; @@ -501,9 +540,34 @@ className="p-1 hover:bg-gray-100 rounded text-gray-400 hover:text-gray-600"
-

- {modalMode === 'add' ? '운행기록 추가' : '운행기록 수정'} -

+
+

+ {modalMode === 'add' ? '운행기록 추가' : '운행기록 수정'} +

+ {/* 편의 기능 버튼 */} +
+ {modalMode === 'edit' && ( + + )} + +
+
From 132f6e8c3d1fb50b48aad232638b3bca6e8a7351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:06:50 +0900 Subject: [PATCH 07/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EB=B3=B5=EC=82=AC=20=EC=8B=9C=20=EC=9A=B4=ED=96=89=EC=9D=BC?= =?UTF-8?q?=EC=9E=90=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/finance/vehicle-logs.blade.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 23693002..9f4ac94e 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -271,12 +271,7 @@ function VehicleLogsManagement() { const handleCopy = () => { setModalMode('add'); setEditingItem(null); - // 날짜를 오늘로 변경 - const today = new Date().toISOString().split('T')[0]; - setFormData(prev => ({ - ...prev, - log_date: today - })); + // 기존 데이터 그대로 유지 (날짜 포함) }; // 출발지↔도착지 교환 + 출근↔퇴근 전환 From f996771d5d8042439c83d81a5b860b7f96900e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:13:56 +0900 Subject: [PATCH 08/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EC=9A=B4=ED=96=89=EC=9D=BC=EC=9E=90=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=EB=8C=80=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95=20(date:Y-m?= =?UTF-8?q?-d=20=ED=98=95=EC=8B=9D=20=EC=A7=80=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/VehicleLog.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/VehicleLog.php b/app/Models/VehicleLog.php index 30c05dcc..d9aeba0e 100644 --- a/app/Models/VehicleLog.php +++ b/app/Models/VehicleLog.php @@ -28,7 +28,7 @@ class VehicleLog extends Model ]; protected $casts = [ - 'log_date' => 'date', + 'log_date' => 'date:Y-m-d', 'distance_km' => 'integer', ]; From 4e5f8ba77c7e6bbe9be5d96dfe41464e53b46401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:16:47 +0900 Subject: [PATCH 09/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EA=B8=B0=EA=B0=84=20=EC=84=A4=EC=A0=95=20=EC=8B=9C=EA=B0=84?= =?UTF-8?q?=EB=8C=80=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95=20(?= =?UTF-8?q?=EB=A1=9C=EC=BB=AC=20=EC=8B=9C=EA=B0=84=EB=8C=80=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/finance/vehicle-logs.blade.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 9f4ac94e..8e1bb38b 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -60,6 +60,14 @@ function VehicleLogsManagement() { const { vehicles, tripTypes, locationTypes } = window.INITIAL_DATA; + // 날짜를 YYYY-MM-DD 형식으로 포맷 (로컬 시간대 사용) + const formatDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; + }; + // 이번 달 시작일/종료일 계산 const getMonthRange = (date) => { const year = date.getFullYear(); @@ -67,8 +75,8 @@ function VehicleLogsManagement() { const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); return { - start: firstDay.toISOString().split('T')[0], - end: lastDay.toISOString().split('T')[0] + start: formatDate(firstDay), + end: formatDate(lastDay) }; }; @@ -157,7 +165,7 @@ function VehicleLogsManagement() { }; const handleAdd = () => { - const today = new Date().toISOString().split('T')[0]; + const today = formatDate(new Date()); setModalMode('add'); setEditingItem(null); From 35d02607326187f4a64025e48da78fd83d9feb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:27:56 +0900 Subject: [PATCH 10/13] =?UTF-8?q?feat:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84=20=EC=9C=A0=ED=98=95=20=ED=99=95=EC=9E=A5?= =?UTF-8?q?=20=EB=B0=8F=20=EB=9D=BC=EB=B2=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구분 유형 추가: 출퇴근용(왕복), 업무용(왕복), 비업무용(왕복) - 비업무 라벨을 '비업무용(개인)'으로 변경 - 출발지/도착지 장소명 라벨 수정 (장소명 → 출발지명/도착지명) - 새 유형별 색상 추가 Co-Authored-By: Claude --- .../Controllers/Finance/VehicleLogController.php | 12 ++++++------ app/Models/VehicleLog.php | 8 +++++++- resources/views/finance/vehicle-logs.blade.php | 11 +++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/Finance/VehicleLogController.php b/app/Http/Controllers/Finance/VehicleLogController.php index 0963376f..3f745284 100644 --- a/app/Http/Controllers/Finance/VehicleLogController.php +++ b/app/Http/Controllers/Finance/VehicleLogController.php @@ -66,8 +66,8 @@ public function list(Request $request): JsonResponse // 월별 합계 $totals = [ - 'business_km' => $logs->whereIn('trip_type', ['commute_to', 'commute_from', 'business'])->sum('distance_km'), - 'personal_km' => $logs->where('trip_type', 'personal')->sum('distance_km'), + 'business_km' => $logs->whereIn('trip_type', ['commute_to', 'commute_from', 'business', 'commute_round', 'business_round'])->sum('distance_km'), + 'personal_km' => $logs->whereIn('trip_type', ['personal', 'personal_round'])->sum('distance_km'), 'total_km' => $logs->sum('distance_km'), ]; @@ -90,7 +90,7 @@ public function store(Request $request): JsonResponse 'vehicle_id' => 'required|integer|exists:corporate_vehicles,id', 'log_date' => 'required|date', 'driver_name' => 'required|string|max:50', - 'trip_type' => 'required|in:commute_to,commute_from,business,personal', + 'trip_type' => 'required|in:commute_to,commute_from,business,personal,commute_round,business_round,personal_round', 'distance_km' => 'required|integer|min:0', ]); @@ -131,7 +131,7 @@ public function update(Request $request, int $id): JsonResponse $request->validate([ 'log_date' => 'required|date', 'driver_name' => 'required|string|max:50', - 'trip_type' => 'required|in:commute_to,commute_from,business,personal', + 'trip_type' => 'required|in:commute_to,commute_from,business,personal,commute_round,business_round,personal_round', 'distance_km' => 'required|integer|min:0', ]); @@ -234,8 +234,8 @@ public function export(Request $request): StreamedResponse } // 합계 - $businessKm = $logs->whereIn('trip_type', ['commute_to', 'commute_from', 'business'])->sum('distance_km'); - $personalKm = $logs->where('trip_type', 'personal')->sum('distance_km'); + $businessKm = $logs->whereIn('trip_type', ['commute_to', 'commute_from', 'business', 'commute_round', 'business_round'])->sum('distance_km'); + $personalKm = $logs->whereIn('trip_type', ['personal', 'personal_round'])->sum('distance_km'); $totalKm = $logs->sum('distance_km'); $sheet->setCellValue('A' . $row, '합계'); diff --git a/app/Models/VehicleLog.php b/app/Models/VehicleLog.php index d9aeba0e..ded7a901 100644 --- a/app/Models/VehicleLog.php +++ b/app/Models/VehicleLog.php @@ -37,6 +37,9 @@ class VehicleLog extends Model public const TRIP_TYPE_COMMUTE_FROM = 'commute_from'; public const TRIP_TYPE_BUSINESS = 'business'; public const TRIP_TYPE_PERSONAL = 'personal'; + public const TRIP_TYPE_COMMUTE_ROUND = 'commute_round'; + public const TRIP_TYPE_BUSINESS_ROUND = 'business_round'; + public const TRIP_TYPE_PERSONAL_ROUND = 'personal_round'; // location_type 상수 public const LOCATION_TYPE_HOME = 'home'; @@ -50,7 +53,10 @@ public static function tripTypeLabels(): array self::TRIP_TYPE_COMMUTE_TO => '출근용', self::TRIP_TYPE_COMMUTE_FROM => '퇴근용', self::TRIP_TYPE_BUSINESS => '업무용', - self::TRIP_TYPE_PERSONAL => '비업무', + self::TRIP_TYPE_PERSONAL => '비업무용(개인)', + self::TRIP_TYPE_COMMUTE_ROUND => '출퇴근용(왕복)', + self::TRIP_TYPE_BUSINESS_ROUND => '업무용(왕복)', + self::TRIP_TYPE_PERSONAL_ROUND => '비업무용(왕복)', ]; } diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 8e1bb38b..0342f694 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -313,6 +313,9 @@ function VehicleLogsManagement() { case 'commute_from': return 'bg-indigo-100 text-indigo-700'; case 'business': return 'bg-emerald-100 text-emerald-700'; case 'personal': return 'bg-orange-100 text-orange-700'; + case 'commute_round': return 'bg-purple-100 text-purple-700'; + case 'business_round': return 'bg-teal-100 text-teal-700'; + case 'personal_round': return 'bg-rose-100 text-rose-700'; default: return 'bg-gray-100 text-gray-700'; } }; @@ -655,12 +658,12 @@ className="w-full px-2 py-2 border border-gray-300 rounded-lg text-sm"
- + setFormData(prev => ({ ...prev, departure_name: e.target.value }))} - placeholder="장소명" + placeholder="출발지명" className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm" />
@@ -697,12 +700,12 @@ className="w-full px-2 py-2 border border-gray-300 rounded-lg text-sm"
- + setFormData(prev => ({ ...prev, arrival_name: e.target.value }))} - placeholder="장소명" + placeholder="도착지명" className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm" />
From ca2ff8403cf749a6aec5aee3bfdaba66390aad1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:30:29 +0900 Subject: [PATCH 11/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EA=B8=B0=EA=B0=84=20=EB=B2=84=ED=8A=BC=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=ED=91=9C=EC=8B=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/finance/vehicle-logs.blade.php | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/resources/views/finance/vehicle-logs.blade.php b/resources/views/finance/vehicle-logs.blade.php index 0342f694..2b4e617a 100644 --- a/resources/views/finance/vehicle-logs.blade.php +++ b/resources/views/finance/vehicle-logs.blade.php @@ -86,6 +86,7 @@ function VehicleLogsManagement() { const [selectedVehicle, setSelectedVehicle] = useState(vehicles[0]?.id || null); const [startDate, setStartDate] = useState(thisMonthRange.start); const [endDate, setEndDate] = useState(thisMonthRange.end); + const [activePeriod, setActivePeriod] = useState('thisMonth'); // 'thisMonth', 'lastMonth', 'custom' const [vehicleInfo, setVehicleInfo] = useState(null); const [logs, setLogs] = useState([]); @@ -154,6 +155,7 @@ function VehicleLogsManagement() { const range = getMonthRange(new Date()); setStartDate(range.start); setEndDate(range.end); + setActivePeriod('thisMonth'); }; const handleLastMonth = () => { @@ -162,6 +164,16 @@ function VehicleLogsManagement() { const range = getMonthRange(lastMonth); setStartDate(range.start); setEndDate(range.end); + setActivePeriod('lastMonth'); + }; + + const handleDateChange = (type, value) => { + if (type === 'start') { + setStartDate(value); + } else { + setEndDate(value); + } + setActivePeriod('custom'); }; const handleAdd = () => { @@ -367,25 +379,33 @@ className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus setStartDate(e.target.value)} + onChange={(e) => handleDateChange('start', e.target.value)} className="px-3 py-2 border border-gray-300 rounded-lg text-sm" /> ~ setEndDate(e.target.value)} + onChange={(e) => handleDateChange('end', e.target.value)} className="px-3 py-2 border border-gray-300 rounded-lg text-sm" /> From 35a205c48ff4b2a00bf058525bf81dda365ae57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 13:38:55 +0900 Subject: [PATCH 12/13] =?UTF-8?q?fix:=EC=B0=A8=EB=9F=89=EC=9D=BC=EC=A7=80?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=A0=95=EB=A0=AC=20=EC=88=9C=EC=84=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=9D=BC=EC=9E=90=20=EB=82=B4=EB=A6=BC=EC=B0=A8?= =?UTF-8?q?=EC=88=9C=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Finance/VehicleLogController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Finance/VehicleLogController.php b/app/Http/Controllers/Finance/VehicleLogController.php index 3f745284..e32970dc 100644 --- a/app/Http/Controllers/Finance/VehicleLogController.php +++ b/app/Http/Controllers/Finance/VehicleLogController.php @@ -60,8 +60,8 @@ public function list(Request $request): JsonResponse $logs = VehicleLog::where('tenant_id', $tenantId) ->where('vehicle_id', $vehicleId) ->whereBetween('log_date', [$startDate, $endDate]) - ->orderBy('log_date') - ->orderBy('id') + ->orderBy('log_date', 'desc') + ->orderBy('id', 'desc') ->get(); // 월별 합계 From 86fa7502174dbed4d2ed6bf595c3acb38c45455d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 3 Feb 2026 14:01:49 +0900 Subject: [PATCH 13/13] =?UTF-8?q?fix:=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=EC=9D=84=20=EC=B5=9C=EA=B3=A0=EA=B4=80=EB=A6=AC=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Requests/UpdateProfileRequest.php | 7 ++++++- app/Services/ProfileService.php | 7 ++++++- resources/views/profile/index.blade.php | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/Http/Requests/UpdateProfileRequest.php b/app/Http/Requests/UpdateProfileRequest.php index 87feec04..7895a229 100644 --- a/app/Http/Requests/UpdateProfileRequest.php +++ b/app/Http/Requests/UpdateProfileRequest.php @@ -21,8 +21,13 @@ public function authorize(): bool */ public function rules(): array { + // 최고관리자만 이름 수정 가능 + $nameRule = auth()->user()->isSuperAdmin() + ? 'required|string|max:100' + : 'nullable'; + return [ - 'name' => 'required|string|max:100', + 'name' => $nameRule, 'phone' => 'nullable|string|max:20', ]; } diff --git a/app/Services/ProfileService.php b/app/Services/ProfileService.php index 27b046dc..ade5056f 100644 --- a/app/Services/ProfileService.php +++ b/app/Services/ProfileService.php @@ -9,10 +9,15 @@ class ProfileService { /** * 프로필 정보 수정 (이름, 전화번호) + * 이름은 최고관리자만 수정 가능 */ public function updateProfile(User $user, array $data): bool { - $user->name = $data['name']; + // 최고관리자만 이름 수정 가능 + if ($user->isSuperAdmin() && isset($data['name'])) { + $user->name = $data['name']; + } + $user->phone = $data['phone'] ?? null; $user->updated_by = $user->id; diff --git a/resources/views/profile/index.blade.php b/resources/views/profile/index.blade.php index 2d92b4b0..3f37d084 100644 --- a/resources/views/profile/index.blade.php +++ b/resources/views/profile/index.blade.php @@ -60,11 +60,16 @@ class="w-full px-4 py-2 border border-gray-200 rounded-lg bg-gray-50 text-gray-5
+ @if(auth()->user()->isSuperAdmin()) + @else + + @endif