From db4c586535742fe4b4b2221f57fd629e27d4c452 Mon Sep 17 00:00:00 2001 From: arifal Date: Fri, 19 Sep 2025 20:44:28 +0700 Subject: [PATCH] fix form create update postcheck and precheck --- app/Http/Controllers/AdminController.php | 8 - app/Http/Controllers/ApiController.php | 2 - app/Http/Controllers/ReportController.php | 98 +- .../Controllers/TransactionController.php | 142 +- .../Transactions/PostchecksController.php | 225 ++- .../Transactions/PrechecksController.php | 295 +++- app/Http/Middleware/adminRole.php | 1 - app/Models/Postcheck.php | 10 +- app/Models/Precheck.php | 10 +- public/mix-manifest.json | 25 +- .../views/back/report/transaction.blade.php | 17 +- resources/views/transaction/index.blade.php | 401 ++++- resources/views/transaction/lists.blade.php | 24 +- .../transaction/postchecks/_form.blade.php | 1548 ++++++++++++++++ .../transaction/postchecks/create.blade.php | 5 + .../transaction/postchecks/edit.blade.php | 5 + .../transaction/postchecks/print.blade.php | 397 +++++ .../transaction/prechecks/_form.blade.php | 1552 +++++++++++++++++ .../transaction/prechecks/create.blade.php | 5 + .../transaction/prechecks/edit.blade.php | 5 + .../transaction/prechecks/print.blade.php | 397 +++++ routes/web.php | 29 +- 22 files changed, 4977 insertions(+), 224 deletions(-) create mode 100644 resources/views/transaction/postchecks/_form.blade.php create mode 100644 resources/views/transaction/postchecks/create.blade.php create mode 100644 resources/views/transaction/postchecks/edit.blade.php create mode 100644 resources/views/transaction/postchecks/print.blade.php create mode 100644 resources/views/transaction/prechecks/_form.blade.php create mode 100644 resources/views/transaction/prechecks/create.blade.php create mode 100644 resources/views/transaction/prechecks/edit.blade.php create mode 100644 resources/views/transaction/prechecks/print.blade.php diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 0e109a0..981692e 100755 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -55,7 +55,6 @@ class AdminController extends Controller } $ajax_url = route('dashboard_data').'?month='.$month.'&year='.$year.'&dealer='.$dealer; - // dd($ajax_url); return view('dashboard', compact('month','year', 'ajax_url', 'dealer', 'dealer_datas')); } @@ -134,7 +133,6 @@ class AdminController extends Controller $dealer_work_trx = DB::statement("PREPARE stmt FROM @sql"); $dealer_work_trx = DB::select(DB::raw("EXECUTE stmt")); DB::statement('DEALLOCATE PREPARE stmt'); - // DD($dealer_work_trx); $theads = ['DEALER']; $dealer_names = []; $dealer_trx = []; @@ -165,7 +163,6 @@ class AdminController extends Controller $dealer_names[] = $dealer_work->DEALER; } - // dd($dealer_trx); $dealer_trx = array_values($dealer_trx); $dealer = $request->dealer; $month = $request->month; @@ -319,7 +316,6 @@ class AdminController extends Controller $prev_mth_end = $prev_mth[0].'-'.$prev_mth[1].'-'.date('t', strtotime($prev_mth_start)); } - // dd($prev_mth_end); $yesterday_month_trx = Transaction::where('work_id', $work1->id)->where('dealer_id', $dealer->id)->whereDate('date', '>=', $prev_mth_start)->whereDate('date', '<=', $prev_mth_end)->sum('qty'); if(array_key_exists($work1->id, $prev_month_trxs_total)) { @@ -528,16 +524,12 @@ class AdminController extends Controller // $month_trxs_total = array_values($month_trxs_total); // $yesterday_month_trxs_total = array_values($yesterday_month_trxs_total); -// dd(["month_trxs_total" => $month_trxs_total, "yesterday_month_trxs_total" => $yesterday_month_trxs_total, "works" => $works->toArray()]); -// dd($month_trxs_total); -// dd($yesterday_month_trxs_total); $final_month_trxs_total = []; $final_yesterday_month_trxs_total = []; foreach($works as $work1) { $final_month_trxs_total[$work1->id] = array_key_exists($work1->id, $month_trxs_total) ? $month_trxs_total[$work1->id] : 0; $final_yesterday_month_trxs_total[$work1->id] = $yesterday_month_trxs_total[$work1->id]; } - // dd([$final_month_trxs_total, $final_yesterday_month_trxs_total]); $month_trxs_total = array_values($final_month_trxs_total); $yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total); $totals = []; diff --git a/app/Http/Controllers/ApiController.php b/app/Http/Controllers/ApiController.php index 41e2eeb..d3cbc6c 100755 --- a/app/Http/Controllers/ApiController.php +++ b/app/Http/Controllers/ApiController.php @@ -93,7 +93,6 @@ class ApiController extends Controller $prev_mth_end = $prev_mth[0].'-'.$prev_mth[1].'-'.date('t'); } - // dd($prev_mth_end); $yesterday_month_trx = Transaction::where('work_id', $work1->id)->where('dealer_id', $id)->whereDate('date', '>=', $prev_mth_start)->whereDate('date', '<=', $prev_mth_end)->sum('qty'); if(array_key_exists($work1->id, $yesterday_month_trxs_total)) { @@ -153,7 +152,6 @@ class ApiController extends Controller $final_month_trxs_total[$work1->id] = $month_trxs_total[$work1->id]; $final_yesterday_month_trxs_total[$work1->id] = $yesterday_month_trxs_total[$work1->id]; } - // dd([$final_month_trxs_total, $final_yesterday_month_trxs_total]); $month_trxs_total = array_values($final_month_trxs_total); $yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total); diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 822019c..fe34e6d 100755 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -472,7 +472,6 @@ class ReportController extends Controller $sa_names = json_encode($sa_names); $trx_data = json_encode(array_values($trx_data)); - // dd($trx_data); $work_count = count($works); $month = $request->month; $dealer_id = $request->dealer; @@ -703,11 +702,28 @@ class ReportController extends Controller } $data = Transaction::leftJoin('users', 'users.id', '=', 'transactions.user_id') - ->leftJoin('users as sa', 'sa.id', '=', 'transactions.user_sa_id') - ->leftJoin('works as w', 'w.id', '=', 'transactions.work_id') - ->leftJoin('categories as cat', 'cat.id', '=', 'w.category_id') - ->leftJoin('dealers as d', 'd.id', '=', 'transactions.dealer_id') - ->select('transactions.id', 'transactions.status', 'transactions.user_id as user_id', 'transactions.user_sa_id as user_sa_id', 'users.name as username', 'sa.name as sa_name', 'cat.name as category_name', 'w.name as workname', 'transactions.qty as qty', 'transactions.date as date', 'transactions.police_number as police_number', 'transactions.warranty as warranty', 'transactions.spk as spk', 'transactions.dealer_id', 'd.name as dealer_name'); + ->leftJoin('users as sa', 'sa.id', '=', 'transactions.user_sa_id') + ->leftJoin('works as w', 'w.id', '=', 'transactions.work_id') + ->leftJoin('categories as cat', 'cat.id', '=', 'w.category_id') + ->leftJoin('dealers as d', 'd.id', '=', 'transactions.dealer_id') + ->leftJoin('prechecks as pre', 'pre.transaction_id', '=', 'transactions.id') + ->leftJoin('postchecks as post', 'post.transaction_id', '=', 'transactions.id') + ->select( + 'transactions.id', + 'transactions.status', + 'users.name as username', + 'sa.name as sa_name', + 'cat.name as category_name', + 'w.name as workname', + 'transactions.qty as qty', + 'transactions.date as date', + 'transactions.police_number as police_number', + 'transactions.warranty as warranty', + 'transactions.spk as spk', + 'd.name as dealer_name', + DB::raw('pre.id as precheck_id'), + DB::raw('post.id as postcheck_id') + ); // Filter by allowed dealers based on user role if($allowedDealers->count() > 0) { @@ -747,24 +763,70 @@ class ReportController extends Controller $data->orderBy('date', 'DESC'); return DataTables::of($data)->addIndexColumn() ->addColumn('action', function($row) use ($menu) { - $btn = '
'; + $btn = '
'; - if($row->status == 1) { - if(Gate::allows('delete', $menu)) { - $btn .= ' '; + // Jika status closed + if ($row->status == 1) { + if (Gate::allows('delete', $menu)) { + $btn .= ''; } - $btn .= 'Closed'; - }else{ - if(Gate::allows('delete', $menu)) { - $btn .= ''; + + // Badge Closed rapi + $btn .= ' + Closed + '; + } else { + if (Gate::allows('delete', $menu)) { + $btn .= ''; } - - if(Gate::allows('update', $menu)) { - $btn .= ' - '; + + if (Gate::allows('update', $menu)) { + $btn .= ''; + + $btn .= ''; } } + if ($row->precheck_id) { + $btn .= ''; + } + + if ($row->postcheck_id) { + $btn .= ''; + } + $btn .= '
'; return $btn; diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index c4aa4a1..a6342a2 100755 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -15,6 +15,9 @@ use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; +use App\Models\Precheck; +use App\Models\Postcheck; +use Illuminate\Support\Facades\Log; use Exception; class TransactionController extends Controller @@ -519,7 +522,6 @@ class TransactionController extends Controller $works_count = count($works); $share = $month_trxs; $month = $request->month; - dd($share); return view('transaction.recap', compact('month_trxs_total', 'yesterday_month_trxs_total', 'month', 'trx_data', 'sa_names', 'works', 'works_count', 'trxs', 'month_trxs','dealer', 'share', 'mechanic')); } @@ -568,7 +570,6 @@ class TransactionController extends Controller $prev_mth_end = $prev_mth[0].'-'.$prev_mth[1].'-'.date('t'); } - // dd($prev_mth_end); $yesterday_month_trx = Transaction::whereNull('deleted_at')->where('work_id', $work1->id)->where('dealer_id', $id)->whereDate('date', '>=', $prev_mth_start)->whereDate('date', '<=', $prev_mth_end)->sum('qty'); if(array_key_exists($work1->id, $yesterday_month_trxs_total)) { @@ -678,15 +679,12 @@ class TransactionController extends Controller } } - // dd($works); - // dd([$month_trxs_total, $yesterday_month_trxs_total]); $final_month_trxs_total = []; $final_yesterday_month_trxs_total = []; foreach($works as $work1) { $final_month_trxs_total[$work1->id] = $month_trxs_total[$work1->id]; $final_yesterday_month_trxs_total[$work1->id] = $yesterday_month_trxs_total[$work1->id]; } - // dd([$final_month_trxs_total, $final_yesterday_month_trxs_total]); $month_trxs_total = array_values($final_month_trxs_total); $yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total); @@ -994,19 +992,31 @@ class TransactionController extends Controller public function update(Request $request, $id) { - Transaction::find($id)->update([ + $request->validate([ + 'spk' => 'required|string|max:255', + 'date' => 'required|date', + 'police_number' => 'required|string|max:255', + 'work_id' => 'required|exists:works,id', + 'qty' => 'required|integer|min:1', + 'warranty' => 'required|in:0,1', + 'user_sa_id' => 'required|exists:users,id', + ]); + + $transaction = Transaction::findOrFail($id); + + $transaction->update([ "spk" => $request->spk, "date" => $request->date, "police_number" => $request->police_number, "work_id" => $request->work_id, "qty" => $request->qty, "warranty" => $request->warranty, - "user_sa_id" => $request->sa_id, + "user_sa_id" => $request->user_sa_id, ]); $response = [ "status" => 200, - "message" => "Data updated successfully" + "message" => "Transaksi berhasil diperbarui" ]; return response()->json($response); @@ -1147,6 +1157,8 @@ class TransactionController extends Controller 'sa_name' => $transaction->sa_name, 'status' => $this->getStatusBadge($transaction->status), 'action' => $this->getActionButtons($transaction), + 'action_precheck' => $this->getActionButtonsPrecheck($transaction), + 'action_postcheck' => $this->getActionButtonsPostcheck($transaction), 'claimed_at' => $transaction->claimed_at, 'claimed_by' => $transaction->claimed_by ]; @@ -1259,38 +1271,108 @@ class TransactionController extends Controller { $buttons = ''; - // Only show buttons for mechanics + // Edit button - show for all users (not just mechanics) + $buttons .= ''; + + // Delete button - show for all users + $buttons .= ''; + + // Only show claim buttons for mechanics if (Auth::user()->role_id == 3) { // Claim button - show only if not claimed yet if (empty($transaction->claimed_at) && empty($transaction->claimed_by)) { - $buttons .= ''; } else { - if($transaction->claimed_by == Auth::user()->id) { - // Check if precheck exists - $precheck = \App\Models\Precheck::where('transaction_id', $transaction->id)->first(); - if (!$precheck) { - $buttons .= ''; - $buttons .= 'Precheck'; - $buttons .= ''; - } else { - // Check if postcheck exists - $postcheck = \App\Models\Postcheck::where('transaction_id', $transaction->id)->first(); - if (!$postcheck) { - $buttons .= ''; - $buttons .= 'Postcheck'; - $buttons .= ''; - } else { - $buttons .= 'Selesai'; - } + if ($transaction->claimed_by == Auth::user()->id) { + $precheck = Precheck::where('transaction_id', $transaction->id)->first(); + $postcheck = Postcheck::where('transaction_id', $transaction->id)->first(); + + if ($precheck && $postcheck) { + $buttons .= ' Selesai'; } } - $buttons .= 'Sudah Diklaim'; + $buttons .= ' Sudah Diklaim'; + } + } + + return $buttons; + } + + private function getActionButtonsPrecheck($transaction) + { + $buttons = ''; + + if (Auth::user()->role_id == 3) { + $precheck = Precheck::where('transaction_id', $transaction->id)->first(); + + if ($precheck) { + $buttons .= ' + Edit + '; + $buttons .= ' + Lihat + '; + } else { + if (empty($transaction->claimed_at) && empty($transaction->claimed_by)) { + $buttons .= 'Transaksi Belum Diklaim'; + }else{ + $buttons .= ' + Tambah + '; + } + } + } + + return $buttons; + } + + private function getActionButtonsPostcheck($transaction) + { + $buttons = ''; + + if (Auth::user()->role_id == 3) { + $postcheck = Postcheck::where('transaction_id', $transaction->id)->first(); + $precheck = Precheck::where('transaction_id', $transaction->id)->first(); + + if($precheck){ + if ($postcheck) { + $buttons .= ' + Edit + '; + $buttons .= ' + Lihat + '; + } else { + $buttons .= ' + Tambah + '; + } + }else{ + $buttons .= 'Precheck Belum Disimpan'; } } - return $buttons; } diff --git a/app/Http/Controllers/Transactions/PostchecksController.php b/app/Http/Controllers/Transactions/PostchecksController.php index 9e617b5..c6bfc00 100644 --- a/app/Http/Controllers/Transactions/PostchecksController.php +++ b/app/Http/Controllers/Transactions/PostchecksController.php @@ -11,14 +11,14 @@ use Illuminate\Support\Facades\Log; class PostchecksController extends Controller { - public function index(Transaction $transaction) + public function create(Transaction $transaction) { $acConditions = Postcheck::getAcConditionOptions(); $blowerConditions = Postcheck::getBlowerConditionOptions(); $evaporatorConditions = Postcheck::getEvaporatorConditionOptions(); $compressorConditions = Postcheck::getCompressorConditionOptions(); - return view('transaction.postchecks', compact( + return view('transaction.postchecks.create', compact( 'transaction', 'acConditions', 'blowerConditions', @@ -62,76 +62,15 @@ class PostchecksController extends Controller 'compressor_condition' => $request->compressor_condition, 'postcheck_notes' => $request->postcheck_notes, ]; - - // Handle file uploads + // Handle file uploads securely $imageFields = [ - 'front_image', 'cabin_temperature_image', 'ac_image', + 'front_image', 'cabin_temperature_image', 'ac_image', 'blower_image', 'evaporator_image' ]; - foreach ($imageFields as $field) { - if ($request->hasFile($field) && $request->file($field)->isValid()) { - try { - $file = $request->file($field); - - // Generate unique filename with transaction ID - $filename = time() . '_' . uniqid() . '_' . $transaction->id . '_' . $field . '.' . $file->getClientOriginalExtension(); - - // Create directory path: transactions/{transaction_id}/postcheck/ - $directory = 'transactions/' . $transaction->id . '/postcheck'; - - // Ensure base storage directory exists - $this->ensureStorageDirectoryExists(); - - // Ensure transactions directory exists - if (!Storage::disk('public')->exists('transactions')) { - Storage::disk('public')->makeDirectory('transactions', 0755, true); - Log::info('Created transactions directory'); - } - - // Ensure transaction ID directory exists - $transactionDir = 'transactions/' . $transaction->id; - if (!Storage::disk('public')->exists($transactionDir)) { - Storage::disk('public')->makeDirectory($transactionDir, 0755, true); - Log::info('Created transaction directory: ' . $transactionDir); - } - - // Ensure postcheck directory exists - if (!Storage::disk('public')->exists($directory)) { - Storage::disk('public')->makeDirectory($directory, 0755, true); - Log::info('Created postcheck directory: ' . $directory); - } - - // Store file in organized directory structure - $path = $file->storeAs($directory, $filename, 'public'); - - // Store file path - $data[$field] = $path; - - // Store metadata - $data[$field . '_metadata'] = [ - 'original_name' => $file->getClientOriginalName(), - 'size' => $file->getSize(), - 'mime_type' => $file->getMimeType(), - 'uploaded_at' => now()->toISOString(), - 'transaction_id' => $transaction->id, - 'filename' => $filename, - ]; - - Log::info('File uploaded successfully: ' . $path); - - } catch (\Exception $e) { - // Log error for debugging - Log::error('File upload failed: ' . $e->getMessage(), [ - 'field' => $field, - 'file' => $file->getClientOriginalName(), - 'transaction_id' => $transaction->id, - 'error' => $e->getMessage(), - 'trace' => $e->getTraceAsString() - ]); - - return back()->withErrors(['error' => 'Gagal mengupload file: ' . $field . '. Error: ' . $e->getMessage()]); - } + $storedPath = $this->processImageUpload($request, $field, $transaction); + if ($storedPath) { + $data[$field] = $storedPath; } } @@ -144,6 +83,91 @@ class PostchecksController extends Controller } } + public function edit(Transaction $transaction, Postcheck $postcheck) + { + $acConditions = Postcheck::getAcConditionOptions(); + $blowerConditions = Postcheck::getBlowerConditionOptions(); + $evaporatorConditions = Postcheck::getEvaporatorConditionOptions(); + $compressorConditions = Postcheck::getCompressorConditionOptions(); + + return view('transaction.postchecks.edit', compact( + 'transaction', + 'postcheck', + 'acConditions', + 'blowerConditions', + 'evaporatorConditions', + 'compressorConditions' + )); + } + + public function update(Request $request, Transaction $transaction, Postcheck $postcheck) + { + $request->validate([ + 'kilometer' => 'required|numeric|min:0', + 'pressure_high' => 'required|numeric|min:0', + 'pressure_low' => 'nullable|numeric|min:0', + 'cabin_temperature' => 'nullable|numeric', + 'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'ac_condition' => 'nullable|in:' . implode(',', Postcheck::getAcConditionOptions()), + 'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'blower_condition' => 'nullable|in:' . implode(',', Postcheck::getBlowerConditionOptions()), + 'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'evaporator_condition' => 'nullable|in:' . implode(',', Postcheck::getEvaporatorConditionOptions()), + 'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'compressor_condition' => 'nullable|in:' . implode(',', Postcheck::getCompressorConditionOptions()), + 'postcheck_notes' => 'nullable|string', + 'front_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + ]); + + $updateData = [ + 'kilometer' => $request->kilometer, + 'pressure_high' => $request->pressure_high, + 'pressure_low' => $request->pressure_low, + 'cabin_temperature' => $request->cabin_temperature, + 'ac_condition' => $request->ac_condition, + 'blower_condition' => $request->blower_condition, + 'evaporator_condition' => $request->evaporator_condition, + 'compressor_condition' => $request->compressor_condition, + 'postcheck_notes' => $request->postcheck_notes, + ]; + + $imageFields = [ + 'front_image', 'cabin_temperature_image', 'ac_image', + 'blower_image', 'evaporator_image' + ]; + + foreach ($imageFields as $field) { + $newPath = $this->processImageUpload($request, $field, $transaction); + if ($newPath) { + // delete old file if exists + if ($postcheck->{$field}) { + $this->deleteIfExists($postcheck->{$field}); + } + $updateData[$field] = $newPath; + } + } + + try { + $postcheck->update($updateData); + return redirect()->route('transaction')->with('success', 'Postcheck berhasil diperbarui'); + } catch (\Exception $e) { + Log::error('Postcheck update failed: ' . $e->getMessage()); + return back()->withErrors(['error' => 'Gagal memperbarui data postcheck. Silakan coba lagi.']); + } + } + + public function print($transaction_id) + { + try { + $postcheck = Postcheck::where('transaction_id', $transaction_id)->firstOrFail(); + + return view('transaction.postchecks.print', compact('postcheck')); + } catch (\Exception $e) { + Log::error('Error printing postcheck: ' . $e->getMessage()); + return back()->with('error', 'Gagal membuka halaman print postcheck.'); + } + } + /** * Ensure the base storage directory exists */ @@ -185,4 +209,69 @@ class PostchecksController extends Controller rmdir($testDir); Log::info('Storage directory is properly configured: ' . $storagePath); } + + /** + * Securely process image upload to prevent RCE. + * - Only allows jpeg and png + * - Generates safe filename + * - Validates actual image content using getimagesize + */ + private function processImageUpload(Request $request, string $field, Transaction $transaction): ?string + { + if (!($request->hasFile($field) && $request->file($field)->isValid())) { + return null; + } + + $file = $request->file($field); + + // Double-check mime type from PHP, disallow svg/gif + $allowedMimes = ['image/jpeg' => 'jpg', 'image/png' => 'png']; + $mime = $file->getMimeType(); + if (!array_key_exists($mime, $allowedMimes)) { + throw new \RuntimeException('Tipe file tidak diperbolehkan'); + } + + // Verify it's a real image by reading dimensions + $imageInfo = @getimagesize($file->getRealPath()); + if ($imageInfo === false) { + throw new \RuntimeException('File bukan gambar yang valid'); + } + + // Prepare directory + $directory = 'transactions/' . $transaction->id . '/postcheck'; + $this->ensureStorageDirectoryExists(); + if (!Storage::disk('public')->exists('transactions')) { + Storage::disk('public')->makeDirectory('transactions', 0755, true); + } + if (!Storage::disk('public')->exists('transactions/' . $transaction->id)) { + Storage::disk('public')->makeDirectory('transactions/' . $transaction->id, 0755, true); + } + if (!Storage::disk('public')->exists($directory)) { + Storage::disk('public')->makeDirectory($directory, 0755, true); + } + + // Safe filename + $ext = $allowedMimes[$mime]; + $filename = time() . '_' . bin2hex(random_bytes(6)) . '_' . $transaction->id . '_' . $field . '.' . $ext; + + // Store + $path = $file->storeAs($directory, $filename, 'public'); + Log::info('Secure image stored', ['field' => $field, 'path' => $path]); + + return $path; + } + + /** + * Delete a file from public storage if it exists + */ + private function deleteIfExists(string $path): void + { + try { + if ($path && Storage::disk('public')->exists($path)) { + Storage::disk('public')->delete($path); + } + } catch (\Throwable $e) { + Log::warning('Failed to delete old image', ['path' => $path, 'error' => $e->getMessage()]); + } + } } diff --git a/app/Http/Controllers/Transactions/PrechecksController.php b/app/Http/Controllers/Transactions/PrechecksController.php index 3b15fab..5eb9f4a 100644 --- a/app/Http/Controllers/Transactions/PrechecksController.php +++ b/app/Http/Controllers/Transactions/PrechecksController.php @@ -11,14 +11,14 @@ use Illuminate\Support\Facades\Log; class PrechecksController extends Controller { - public function index(Transaction $transaction) + public function create(Transaction $transaction) { $acConditions = Precheck::getAcConditionOptions(); $blowerConditions = Precheck::getBlowerConditionOptions(); $evaporatorConditions = Precheck::getEvaporatorConditionOptions(); $compressorConditions = Precheck::getCompressorConditionOptions(); - return view('transaction.prechecks', compact( + return view('transaction.prechecks.create', compact( 'transaction', 'acConditions', 'blowerConditions', @@ -74,6 +74,11 @@ class PrechecksController extends Controller try { $file = $request->file($field); + // Enhanced security validation + if (!$this->isValidImageFile($file)) { + return back()->withErrors(['error' => 'File tidak valid atau berbahaya: ' . $field]); + } + // Generate unique filename with transaction ID $filename = time() . '_' . uniqid() . '_' . $transaction->id . '_' . $field . '.' . $file->getClientOriginalExtension(); @@ -144,6 +149,158 @@ class PrechecksController extends Controller } } + public function edit(Transaction $transaction, Precheck $precheck) + { + + $acConditions = Precheck::getAcConditionOptions(); + $blowerConditions = Precheck::getBlowerConditionOptions(); + $evaporatorConditions = Precheck::getEvaporatorConditionOptions(); + $compressorConditions = Precheck::getCompressorConditionOptions(); + + return view('transaction.prechecks.edit', compact( + 'transaction', + 'precheck', + 'acConditions', + 'blowerConditions', + 'evaporatorConditions', + 'compressorConditions' + )); + } + + public function update(Request $request, Transaction $transaction, Precheck $precheck) + { + + $request->validate([ + 'kilometer' => 'required|numeric|min:0', + 'pressure_high' => 'required|numeric|min:0', + 'pressure_low' => 'nullable|numeric|min:0', + 'cabin_temperature' => 'nullable|numeric', + 'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'ac_condition' => 'nullable|in:' . implode(',', Precheck::getAcConditionOptions()), + 'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'blower_condition' => 'nullable|in:' . implode(',', Precheck::getBlowerConditionOptions()), + 'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'evaporator_condition' => 'nullable|in:' . implode(',', Precheck::getEvaporatorConditionOptions()), + 'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + 'compressor_condition' => 'nullable|in:' . implode(',', Precheck::getCompressorConditionOptions()), + 'precheck_notes' => 'nullable|string', + 'front_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480', + ]); + + $data = [ + 'kilometer' => $request->kilometer, + 'pressure_high' => $request->pressure_high, + 'pressure_low' => $request->pressure_low, + 'cabin_temperature' => $request->cabin_temperature, + 'ac_condition' => $request->ac_condition, + 'blower_condition' => $request->blower_condition, + 'evaporator_condition' => $request->evaporator_condition, + 'compressor_condition' => $request->compressor_condition, + 'precheck_notes' => $request->precheck_notes, + ]; + + // Handle file uploads with security validation + $imageFields = [ + 'front_image', 'cabin_temperature_image', 'ac_image', + 'blower_image', 'evaporator_image' + ]; + + foreach ($imageFields as $field) { + if ($request->hasFile($field) && $request->file($field)->isValid()) { + try { + $file = $request->file($field); + + // Enhanced security validation + if (!$this->isValidImageFile($file)) { + return back()->withErrors(['error' => 'File tidak valid atau berbahaya: ' . $field]); + } + + // Generate unique filename with transaction ID + $filename = time() . '_' . uniqid() . '_' . $transaction->id . '_' . $field . '.' . $file->getClientOriginalExtension(); + + // Create directory path: transactions/{transaction_id}/precheck/ + $directory = 'transactions/' . $transaction->id . '/precheck'; + + // Ensure base storage directory exists + $this->ensureStorageDirectoryExists(); + + // Ensure transactions directory exists + if (!Storage::disk('public')->exists('transactions')) { + Storage::disk('public')->makeDirectory('transactions', 0755, true); + Log::info('Created transactions directory'); + } + + // Ensure transaction ID directory exists + $transactionDir = 'transactions/' . $transaction->id; + if (!Storage::disk('public')->exists($transactionDir)) { + Storage::disk('public')->makeDirectory($transactionDir, 0755, true); + Log::info('Created transaction directory: ' . $transactionDir); + } + + // Ensure precheck directory exists + if (!Storage::disk('public')->exists($directory)) { + Storage::disk('public')->makeDirectory($directory, 0755, true); + Log::info('Created precheck directory: ' . $directory); + } + + // Delete old file if exists + if ($precheck->$field && Storage::disk('public')->exists($precheck->$field)) { + Storage::disk('public')->delete($precheck->$field); + } + + // Store file in organized directory structure + $path = $file->storeAs($directory, $filename, 'public'); + + // Store file path + $data[$field] = $path; + + // Store metadata + $data[$field . '_metadata'] = [ + 'original_name' => $file->getClientOriginalName(), + 'size' => $file->getSize(), + 'mime_type' => $file->getMimeType(), + 'uploaded_at' => now()->toISOString(), + 'transaction_id' => $transaction->id, + 'filename' => $filename, + ]; + + Log::info('File uploaded successfully: ' . $path); + + } catch (\Exception $e) { + // Log error for debugging + Log::error('File upload failed: ' . $e->getMessage(), [ + 'field' => $field, + 'file' => $file->getClientOriginalName(), + 'transaction_id' => $transaction->id, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return back()->withErrors(['error' => 'Gagal mengupload file: ' . $field . '. Error: ' . $e->getMessage()]); + } + } + } + + try { + $precheck->update($data); + return redirect()->route('transaction')->with('success', 'Precheck berhasil diperbarui'); + } catch (\Exception $e) { + Log::error('Precheck update failed: ' . $e->getMessage()); + return back()->withErrors(['error' => 'Gagal memperbarui data precheck. Silakan coba lagi.']); + } + } + + public function print($transaction_id) + { + try { + $precheck = Precheck::where('transaction_id', $transaction_id)->firstOrFail(); + return view('transaction.prechecks.print', compact('precheck')); + } catch (\Exception $e) { + Log::error('Error printing precheck: ' . $e->getMessage()); + return back()->with('error', 'Gagal membuka halaman print precheck.'); + } + } + /** * Ensure the base storage directory exists */ @@ -185,4 +342,138 @@ class PrechecksController extends Controller rmdir($testDir); Log::info('Storage directory is properly configured: ' . $storagePath); } + + /** + * Enhanced security validation for image files to prevent RCE attacks + * + * @param \Illuminate\Http\UploadedFile $file + * @return bool + */ + private function isValidImageFile($file) + { + try { + // 1. Check file extension (whitelist approach) + $allowedExtensions = ['jpg', 'jpeg', 'png']; + $extension = strtolower($file->getClientOriginalExtension()); + + if (!in_array($extension, $allowedExtensions)) { + Log::warning('Invalid file extension: ' . $extension, [ + 'filename' => $file->getClientOriginalName(), + 'user_id' => auth()->id() + ]); + return false; + } + + // 2. Check MIME type + $allowedMimeTypes = [ + 'image/jpeg', + 'image/jpg', + 'image/png' + ]; + + $mimeType = $file->getMimeType(); + if (!in_array($mimeType, $allowedMimeTypes)) { + Log::warning('Invalid MIME type: ' . $mimeType, [ + 'filename' => $file->getClientOriginalName(), + 'user_id' => auth()->id() + ]); + return false; + } + + // 3. Verify file is actually an image using getimagesize + $imageInfo = @getimagesize($file->getPathname()); + if ($imageInfo === false) { + Log::warning('File is not a valid image: ' . $file->getClientOriginalName(), [ + 'user_id' => auth()->id() + ]); + return false; + } + + // 4. Check image dimensions (prevent extremely large images) + $maxWidth = 5000; + $maxHeight = 5000; + if ($imageInfo[0] > $maxWidth || $imageInfo[1] > $maxHeight) { + Log::warning('Image dimensions too large: ' . $imageInfo[0] . 'x' . $imageInfo[1], [ + 'filename' => $file->getClientOriginalName(), + 'user_id' => auth()->id() + ]); + return false; + } + + // 5. Check file size (max 20MB) + $maxSize = 20 * 1024 * 1024; // 20MB + if ($file->getSize() > $maxSize) { + Log::warning('File size too large: ' . $file->getSize(), [ + 'filename' => $file->getClientOriginalName(), + 'user_id' => auth()->id() + ]); + return false; + } + + // 6. Check for suspicious content in filename + $filename = $file->getClientOriginalName(); + $suspiciousPatterns = [ + ' $filename, + 'user_id' => auth()->id() + ]); + return false; + } + } + + // 7. Additional security: Check file header (magic bytes) + $handle = fopen($file->getPathname(), 'rb'); + if ($handle) { + $header = fread($handle, 8); + fclose($handle); + + // Check for valid image headers + $validHeaders = [ + "\xFF\xD8\xFF", // JPEG + "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", // PNG + ]; + + $isValidHeader = false; + foreach ($validHeaders as $validHeader) { + if (substr($header, 0, strlen($validHeader)) === $validHeader) { + $isValidHeader = true; + break; + } + } + + if (!$isValidHeader) { + Log::warning('Invalid file header detected', [ + 'filename' => $filename, + 'user_id' => auth()->id(), + 'header' => bin2hex($header) + ]); + return false; + } + } + + Log::info('File validation passed', [ + 'filename' => $filename, + 'size' => $file->getSize(), + 'mime_type' => $mimeType, + 'user_id' => auth()->id() + ]); + + return true; + + } catch (\Exception $e) { + Log::error('File validation error: ' . $e->getMessage(), [ + 'filename' => $file->getClientOriginalName(), + 'user_id' => auth()->id(), + 'error' => $e->getMessage() + ]); + return false; + } + } } diff --git a/app/Http/Middleware/adminRole.php b/app/Http/Middleware/adminRole.php index 67a84ae..1c6ffba 100755 --- a/app/Http/Middleware/adminRole.php +++ b/app/Http/Middleware/adminRole.php @@ -20,7 +20,6 @@ class adminRole { // check if user can access admin area $user = Privilege::join('menus AS m', 'm.id', '=', 'privileges.menu_id')->where('m.link', 'adminarea')->where('role_id', Auth::user()->role_id)->where('view', 1)->get(); - // dd($user); if (!$user) { abort(404); } diff --git a/app/Models/Postcheck.php b/app/Models/Postcheck.php index 83b6b01..06db87e 100644 --- a/app/Models/Postcheck.php +++ b/app/Models/Postcheck.php @@ -75,7 +75,7 @@ class Postcheck extends Model */ public function getFrontImageUrlAttribute() { - return $this->front_image ? Storage::disk('public')->url($this->front_image) : null; + return $this->front_image ? asset('storage/' . ltrim($this->front_image, '/')) : null; } /** @@ -83,7 +83,7 @@ class Postcheck extends Model */ public function getCabinTemperatureImageUrlAttribute() { - return $this->cabin_temperature_image ? Storage::disk('public')->url($this->cabin_temperature_image) : null; + return $this->cabin_temperature_image ? asset('storage/' . ltrim($this->cabin_temperature_image, '/')) : null; } /** @@ -91,7 +91,7 @@ class Postcheck extends Model */ public function getAcImageUrlAttribute() { - return $this->ac_image ? Storage::disk('public')->url($this->ac_image) : null; + return $this->ac_image ? asset('storage/' . ltrim($this->ac_image, '/')) : null; } /** @@ -99,7 +99,7 @@ class Postcheck extends Model */ public function getBlowerImageUrlAttribute() { - return $this->blower_image ? Storage::disk('public')->url($this->blower_image) : null; + return $this->blower_image ? asset('storage/' . ltrim($this->blower_image, '/')) : null; } /** @@ -107,7 +107,7 @@ class Postcheck extends Model */ public function getEvaporatorImageUrlAttribute() { - return $this->evaporator_image ? Storage::disk('public')->url($this->evaporator_image) : null; + return $this->evaporator_image ? asset('storage/' . ltrim($this->evaporator_image, '/')) : null; } /** diff --git a/app/Models/Precheck.php b/app/Models/Precheck.php index 598ea04..5641168 100644 --- a/app/Models/Precheck.php +++ b/app/Models/Precheck.php @@ -75,7 +75,7 @@ class Precheck extends Model */ public function getFrontImageUrlAttribute() { - return $this->front_image ? Storage::disk('public')->url($this->front_image) : null; + return $this->front_image ? asset('storage/' . ltrim($this->front_image, '/')) : null; } /** @@ -83,7 +83,7 @@ class Precheck extends Model */ public function getCabinTemperatureImageUrlAttribute() { - return $this->cabin_temperature_image ? Storage::disk('public')->url($this->cabin_temperature_image) : null; + return $this->cabin_temperature_image ? asset('storage/' . ltrim($this->cabin_temperature_image, '/')) : null; } /** @@ -91,7 +91,7 @@ class Precheck extends Model */ public function getAcImageUrlAttribute() { - return $this->ac_image ? Storage::disk('public')->url($this->ac_image) : null; + return $this->ac_image ? asset('storage/' . ltrim($this->ac_image, '/')) : null; } /** @@ -99,7 +99,7 @@ class Precheck extends Model */ public function getBlowerImageUrlAttribute() { - return $this->blower_image ? Storage::disk('public')->url($this->blower_image) : null; + return $this->blower_image ? asset('storage/' . ltrim($this->blower_image, '/')) : null; } /** @@ -107,7 +107,7 @@ class Precheck extends Model */ public function getEvaporatorImageUrlAttribute() { - return $this->evaporator_image ? Storage::disk('public')->url($this->evaporator_image) : null; + return $this->evaporator_image ? asset('storage/' . ltrim($this->evaporator_image, '/')) : null; } /** diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 975b60f..a4b2a48 100755 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,28 +1,5 @@ { "/js/app.js": "/js/app.js", "/js/vendor.js": "/js/vendor.js", - "/js/warehouse_management/product_categories/index.js": "/js/warehouse_management/product_categories/index.js", - "/js/warehouse_management/products/index.js": "/js/warehouse_management/products/index.js", - "/js/warehouse_management/opnames/index.js": "/js/warehouse_management/opnames/index.js", - "/js/warehouse_management/opnames/create.js": "/js/warehouse_management/opnames/create.js", - "/js/warehouse_management/opnames/detail.js": "/js/warehouse_management/opnames/detail.js", - "/js/warehouse_management/mutations/index.js": "/js/warehouse_management/mutations/index.js", - "/js/warehouse_management/mutations/create.js": "/js/warehouse_management/mutations/create.js", - "/js/warehouse_management/stock_audit/index.js": "/js/warehouse_management/stock_audit/index.js", - "/css/app.css": "/css/app.css", - "/js/vendor/jquery.dataTables.min.js": "/js/vendor/jquery.dataTables.min.js", - "/js/vendor/dataTables.bootstrap4.min.js": "/js/vendor/dataTables.bootstrap4.min.js", - "/js/vendor/dataTables.fixedColumns.min.js": "/js/vendor/dataTables.fixedColumns.min.js", - "/js/vendor/sweetalert2.min.js": "/js/vendor/sweetalert2.min.js", - "/js/vendor/chart.umd.js": "/js/vendor/chart.umd.js", - "/js/vendor/chartjs-plugin-datalabels.min.js": "/js/vendor/chartjs-plugin-datalabels.min.js", - "/css/vendor/dataTables.bootstrap4.min.css": "/css/vendor/dataTables.bootstrap4.min.css", - "/css/vendor/fixedColumns.bootstrap4.min.css": "/css/vendor/fixedColumns.bootstrap4.min.css", - "/css/vendor/sweetalert2.min.css": "/css/vendor/sweetalert2.min.css", - "/js/cdn/dataTables.bootstrap4.min.js": "/js/cdn/dataTables.bootstrap4.min.js", - "/js/cdn/dataTables.fixedColumns.min.js": "/js/cdn/dataTables.fixedColumns.min.js", - "/js/cdn/jquery.dataTables.min.js": "/js/cdn/jquery.dataTables.min.js", - "/css/dataTables.bootstrap4.min.css": "/css/dataTables.bootstrap4.min.css", - "/css/fixedColumns.bootstrap4.min.css": "/css/fixedColumns.bootstrap4.min.css", - "/css/google-font.css": "/css/google-font.css" + "/css/app.css": "/css/app.css" } diff --git a/resources/views/back/report/transaction.blade.php b/resources/views/back/report/transaction.blade.php index e0cf812..ac5c4b9 100755 --- a/resources/views/back/report/transaction.blade.php +++ b/resources/views/back/report/transaction.blade.php @@ -223,7 +223,14 @@ var table = $('#kt_table').DataTable({ return ``; } }, - {data: 'date', name: 'transactions.date'}, + { + data: 'date', + name: 'transactions.date', + render: function (data, type, row) { + if (!data) return ''; + return data.split(' ')[0]; // ambil bagian sebelum spasi + } + }, {data: 'dealer_name', name: 'd.name'}, {data: 'username', name: 'users.name'}, {data: 'sa_name', name: 'sa.name'}, @@ -415,6 +422,14 @@ jQuery(document).ready(function () { }) }) + $(document).on("click", ".action-print", function () { + let type = $(this).data("type"); + let id = $(this).data("id"); + let url = $(this).data("url"); + + window.open(url, "_blank"); + }); + }) diff --git a/resources/views/transaction/index.blade.php b/resources/views/transaction/index.blade.php index 3fe500b..cf28d9a 100755 --- a/resources/views/transaction/index.blade.php +++ b/resources/views/transaction/index.blade.php @@ -320,7 +320,7 @@ use Illuminate\Support\Facades\Auth; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - /* Claim tab specific styling */ + /* Claim tab specific styling - Enhanced and Fixed */ #claimTransactionsTable { font-size: 14px; } @@ -352,6 +352,186 @@ use Illuminate\Support\Facades\Auth; font-weight: 600; } + /* Fixed column widths for claim table */ + #claimTransactionsTable th:nth-child(1), + #claimTransactionsTable td:nth-child(1) { + width: 12% !important; + min-width: 100px !important; + } /* Tanggal */ + #claimTransactionsTable th:nth-child(2), + #claimTransactionsTable td:nth-child(2) { + width: 10% !important; + min-width: 80px !important; + } /* SPK */ + #claimTransactionsTable th:nth-child(3), + #claimTransactionsTable td:nth-child(3) { + width: 12% !important; + min-width: 100px !important; + } /* No Polisi */ + #claimTransactionsTable th:nth-child(4), + #claimTransactionsTable td:nth-child(4) { + width: 20% !important; + min-width: 150px !important; + } /* Pekerjaan */ + #claimTransactionsTable th:nth-child(5), + #claimTransactionsTable td:nth-child(5) { + width: 8% !important; + min-width: 60px !important; + } /* Qty */ + #claimTransactionsTable th:nth-child(6), + #claimTransactionsTable td:nth-child(6) { + width: 15% !important; + min-width: 120px !important; + } /* Service Advisor */ + #claimTransactionsTable th:nth-child(7), + #claimTransactionsTable td:nth-child(7) { + width: 10% !important; + min-width: 80px !important; + } /* Status */ + #claimTransactionsTable th:nth-child(8), + #claimTransactionsTable td:nth-child(8) { + width: 8% !important; + min-width: 80px !important; + } /* Aksi */ + #claimTransactionsTable th:nth-child(9), + #claimTransactionsTable td:nth-child(9) { + width: 8% !important; + min-width: 100px !important; + } /* Pre Check */ + #claimTransactionsTable th:nth-child(10), + #claimTransactionsTable td:nth-child(10) { + width: 8% !important; + min-width: 100px !important; + } /* Post Check */ + + /* Action column specific styling - Enhanced */ + #claimTransactionsTable td:nth-child(8), + #claimTransactionsTable td:nth-child(9), + #claimTransactionsTable td:nth-child(10) { + text-align: center !important; + vertical-align: middle !important; + padding: 8px 4px !important; + } + + /* Button alignment in action columns - Fixed */ + #claimTransactionsTable td:nth-child(8) .btn, + #claimTransactionsTable td:nth-child(9) .btn, + #claimTransactionsTable td:nth-child(10) .btn { + margin: 1px 2px !important; + vertical-align: middle !important; + display: inline-block !important; + } + + /* Specific button styling for different types */ + #claimTransactionsTable .btn-success { + background-color: #28a745 !important; + border-color: #28a745 !important; + color: white !important; + } + + #claimTransactionsTable .btn-warning { + background-color: #ffc107 !important; + border-color: #ffc107 !important; + color: #212529 !important; + } + + #claimTransactionsTable .btn-primary { + background-color: #007bff !important; + border-color: #007bff !important; + color: white !important; + } + + #claimTransactionsTable .btn-danger { + background-color: #dc3545 !important; + border-color: #dc3545 !important; + color: white !important; + } + + /* Badge styling fixes */ + #claimTransactionsTable .badge-warning { + background-color: #ffc107 !important; + color: #212529 !important; + } + + #claimTransactionsTable .badge-success { + background-color: #28a745 !important; + color: white !important; + } + + #claimTransactionsTable .badge-info { + background-color: #17a2b8 !important; + color: white !important; + } + + #claimTransactionsTable .badge-danger { + background-color: #dc3545 !important; + color: white !important; + } + + /* DataTable specific fixes */ + #claimTransactionsTable_wrapper { + overflow-x: auto !important; + } + + #claimTransactionsTable_wrapper .dataTables_scrollHead { + overflow: hidden !important; + } + + #claimTransactionsTable_wrapper .dataTables_scrollBody { + overflow-x: auto !important; + } + + /* Responsive adjustments for smaller screens */ + @media (max-width: 768px) { + #claimTransactionsTable { + font-size: 12px !important; + } + + #claimTransactionsTable th, + #claimTransactionsTable td { + padding: 8px 4px !important; + } + + #claimTransactionsTable .btn { + font-size: 10px !important; + padding: 3px 6px !important; + min-width: 50px !important; + } + + #claimTransactionsTable .badge { + font-size: 9px !important; + padding: 3px 6px !important; + } + + /* Adjust column widths for mobile */ + #claimTransactionsTable th:nth-child(4), + #claimTransactionsTable td:nth-child(4) { + width: 25% !important; + min-width: 120px !important; + } /* Pekerjaan - wider on mobile */ + } + + /* Additional fixes for DataTable rendering */ + .dataTables_wrapper .dataTables_length, + .dataTables_wrapper .dataTables_filter, + .dataTables_wrapper .dataTables_info, + .dataTables_wrapper .dataTables_processing, + .dataTables_wrapper .dataTables_paginate { + color: #333 !important; + } + + .dataTables_wrapper .dataTables_paginate .paginate_button { + padding: 0.5em 1em !important; + margin: 0 2px !important; + border-radius: 4px !important; + } + + .dataTables_wrapper .dataTables_paginate .paginate_button.current { + background: #007bff !important; + color: white !important; + border: 1px solid #007bff !important; + } + /* Tab claim specific styling */ #form-claim { background-color: #fff; @@ -737,9 +917,6 @@ use Illuminate\Support\Facades\Auth;
@if (old('work_id') && old('form') == 'work') - {{-- @php - dd($errors->all()); - @endphp --}} @for ($i = 0; $i < count(old('work_id')); $i++)
@@ -957,20 +1134,21 @@ use Illuminate\Support\Facades\Auth;
Daftar Pekerjaan yang Dapat Diklaim
- + - - - - - - - - + + + + + + + + + + -
TanggalNo. SPKNo. PolisiPekerjaanQtyService AdvisorStatusAksiTanggalSPKNo PolisiPekerjaanQtyService AdvisorStatusAksiPre CheckPost Check
@@ -1180,6 +1358,73 @@ use Illuminate\Support\Facades\Auth;
+ + + @@ -294,17 +293,6 @@ $string .= "*TOTAL: ".$total_qty." Unit*\n\n"; $overall_total += $total_qty; - - // Remove monthly data display since this is daily report - // if (isset($trx['total_body']) && is_array($trx['total_body'])) { - // foreach ($trx['total_body'] as $total) { - // $string .= $total; - // } - // } - - // if (isset($trx['total_total'])) { - // $string .= $trx['total_total']."\n\n"; - // } } } else { $string .= "*Tidak ada data transaksi hari ini*\n\n"; @@ -385,6 +373,18 @@ + +@endsection diff --git a/resources/views/transaction/postchecks/create.blade.php b/resources/views/transaction/postchecks/create.blade.php new file mode 100644 index 0000000..c180d1f --- /dev/null +++ b/resources/views/transaction/postchecks/create.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.frontapp') + +@section('content') + @include('transaction.postchecks._form') +@endsection diff --git a/resources/views/transaction/postchecks/edit.blade.php b/resources/views/transaction/postchecks/edit.blade.php new file mode 100644 index 0000000..c180d1f --- /dev/null +++ b/resources/views/transaction/postchecks/edit.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.frontapp') + +@section('content') + @include('transaction.postchecks._form') +@endsection diff --git a/resources/views/transaction/postchecks/print.blade.php b/resources/views/transaction/postchecks/print.blade.php new file mode 100644 index 0000000..1efd92e --- /dev/null +++ b/resources/views/transaction/postchecks/print.blade.php @@ -0,0 +1,397 @@ + + + + + + Print Postcheck - {{ $postcheck->spk_number ?? '-' }} + + + + + +
+
+
+
PT. CIPTA KREASI BARU
+
Postcheck Kendaraan
+
+
Dokumen Postcheck
+
+ +
+
+
Informasi Transaksi
+
+ No. Polisi + {{ $postcheck->police_number ?? $postcheck->transaction->police_number ?? '-' }} +
+
+ No. SPK + {{ $postcheck->spk_number ?? $postcheck->transaction->spk ?? '-' }} +
+
+ Tanggal Postcheck + {{ optional($postcheck->postcheck_at)->format('d F Y H:i') }} +
+
+ Dibuat oleh + {{ $postcheck->postcheckBy->name ?? '-' }} +
+
+ +
+
Parameter Utama
+
+ Kilometer + {{ number_format((float)($postcheck->kilometer ?? 0), 2) }} +
+
+ Tekanan High + {{ number_format((float)($postcheck->pressure_high ?? 0), 2) }} +
+
+ Tekanan Low + {{ number_format((float)($postcheck->pressure_low ?? 0), 2) }} +
+
+ Suhu Kabin + {{ isset($postcheck->cabin_temperature) ? number_format((float)$postcheck->cabin_temperature, 2) : '-' }} +
+
+
+ +
Kondisi Komponen
+ + + + + + + + + + + + + + + + + + + + + + + + + +
KomponenStatus Pekerjaan
AC{{ $postcheck->ac_condition ?? '-' }}
Blower{{ $postcheck->blower_condition ?? '-' }}
Evaporator{{ $postcheck->evaporator_condition ?? '-' }}
Compressor{{ $postcheck->compressor_condition ?? '-' }}
+ +
Dokumentasi Foto
+
+
+
Depan
+
+ @if($postcheck->front_image_url || $postcheck->front_image) + Foto Depan + @else +
Tidak ada gambar
+ @endif +
+
+
+
Suhu Kabin
+
+ @if($postcheck->cabin_temperature_image_url || $postcheck->cabin_temperature_image) + Foto Suhu Kabin + @else +
Tidak ada gambar
+ @endif +
+
+
+
AC
+
+ @if($postcheck->ac_image_url || $postcheck->ac_image) + Foto AC + @else +
Tidak ada gambar
+ @endif +
+
+
+
Blower
+
+ @if($postcheck->blower_image_url || $postcheck->blower_image) + Foto Blower + @else +
Tidak ada gambar
+ @endif +
+
+
+
Evaporator
+
+ @if($postcheck->evaporator_image_url || $postcheck->evaporator_image) + Foto Evaporator + @else +
Tidak ada gambar
+ @endif +
+
+
+ + @if($postcheck->postcheck_notes) +
+
Catatan
+
{{ $postcheck->postcheck_notes }}
+
+ @endif + + +
+ + + + diff --git a/resources/views/transaction/prechecks/_form.blade.php b/resources/views/transaction/prechecks/_form.blade.php new file mode 100644 index 0000000..952eb3a --- /dev/null +++ b/resources/views/transaction/prechecks/_form.blade.php @@ -0,0 +1,1552 @@ +@extends('layouts.frontapp') + +@section('styles') + + + +@endsection + +@section('content') +
+
+
+
+ LOGO CKB +
+
+ + {{ __('Logout') }} + +
+ @csrf +
+
+
+ +
+
+

{{ isset($precheck) ? 'Edit Form Precheck' : 'Form Precheck' }}

+
+
+ + @if(session('success')) + + @endif + + @if($errors->any()) + + @endif + +
+
+
+
+ + Kembali + +
+ @csrf + @if(isset($precheck)) + @method('PUT') + @endif + + + +
+
Informasi Transaksi
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
Data Dasar
+
+
+
+
+ + + Tanggal dan waktu saat ini akan digunakan +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
Foto Depan Kendaraan *
+
+
+ +
+ + +
+ + + + +
+
+ Atau upload foto dari galeri (maksimal 20MB, format: JPG, JPEG, PNG): + +
+
+ @if(isset($precheck) && $precheck->front_image_url) +
+ +
+ Foto saat ini +
+
+ @endif +
+
+
+ + +
+
Foto Suhu Kabin
+
+
+
+
+ + +
+
+
+
+ + +
+ + +
+ + + + +
+
+ Atau upload foto dari galeri (maksimal 20MB, format: JPG, JPEG, PNG): + +
+
+ @if(isset($precheck) && $precheck->cabin_temperature_image_url) +
+ +
+ Foto saat ini +
+
+ @endif +
+
+
+
+
+ + +
+
Kondisi AC
+
+
+
+
+ + +
+
+
+
+ + +
+ + +
+ + + + +
+
+ Atau upload foto dari galeri (maksimal 20MB, format: JPG, JPEG, PNG): + +
+
+ @if(isset($precheck) && $precheck->ac_image_url) +
+ +
+ Foto saat ini +
+
+ @endif +
+
+
+
+
+ + +
+
Kondisi Blower
+
+
+
+
+ + +
+
+
+
+ + +
+ + +
+ + + + +
+
+ Atau upload foto dari galeri (maksimal 20MB, format: JPG, JPEG, PNG): + +
+
+ @if(isset($precheck) && $precheck->blower_image_url) +
+ +
+ Foto saat ini +
+
+ @endif +
+
+
+
+
+ + +
+
Kondisi Evaporator
+
+
+
+
+ + +
+
+
+
+ + +
+ + +
+ + + + +
+
+ Atau upload foto dari galeri (maksimal 20MB, format: JPG, JPEG, PNG): + +
+
+ @if(isset($precheck) && $precheck->evaporator_image_url) +
+ +
+ Foto saat ini +
+
+ @endif +
+
+
+
+
+ + +
+
Kondisi Compressor
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
Simpan Data
+
+
+ +
+
+
+
+
+
+
+
+@endsection + +@section('javascripts') + + + +@endsection \ No newline at end of file diff --git a/resources/views/transaction/prechecks/create.blade.php b/resources/views/transaction/prechecks/create.blade.php new file mode 100644 index 0000000..8c7506b --- /dev/null +++ b/resources/views/transaction/prechecks/create.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.frontapp') + +@section('content') + @include('transaction.prechecks._form') +@endsection diff --git a/resources/views/transaction/prechecks/edit.blade.php b/resources/views/transaction/prechecks/edit.blade.php new file mode 100644 index 0000000..8c7506b --- /dev/null +++ b/resources/views/transaction/prechecks/edit.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.frontapp') + +@section('content') + @include('transaction.prechecks._form') +@endsection diff --git a/resources/views/transaction/prechecks/print.blade.php b/resources/views/transaction/prechecks/print.blade.php new file mode 100644 index 0000000..64de863 --- /dev/null +++ b/resources/views/transaction/prechecks/print.blade.php @@ -0,0 +1,397 @@ + + + + + + Print Precheck - {{ $precheck->spk_number ?? '-' }} + + + + + +
+
+
+
PT. CIPTA KREASI BARU
+
Precheck Kendaraan
+
+
Dokumen Precheck
+
+ +
+
+
Informasi Transaksi
+
+ No. Polisi + {{ $precheck->police_number ?? $precheck->transaction->police_number ?? '-' }} +
+
+ No. SPK + {{ $precheck->spk_number ?? $precheck->transaction->spk ?? '-' }} +
+
+ Tanggal Precheck + {{ optional($precheck->precheck_at)->format('d F Y H:i') }} +
+
+ Dibuat oleh + {{ $precheck->precheckBy->name ?? '-' }} +
+
+ +
+
Parameter Utama
+
+ Kilometer + {{ number_format((float)($precheck->kilometer ?? 0), 2) }} +
+
+ Tekanan High + {{ number_format((float)($precheck->pressure_high ?? 0), 2) }} +
+
+ Tekanan Low + {{ number_format((float)($precheck->pressure_low ?? 0), 2) }} +
+
+ Suhu Kabin + {{ isset($precheck->cabin_temperature) ? number_format((float)$precheck->cabin_temperature, 2) : '-' }} +
+
+
+ +
Kondisi Komponen
+ + + + + + + + + + + + + + + + + + + + + + + + + +
KomponenKondisi
AC{{ $precheck->ac_condition ?? '-' }}
Blower{{ $precheck->blower_condition ?? '-' }}
Evaporator{{ $precheck->evaporator_condition ?? '-' }}
Compressor{{ $precheck->compressor_condition ?? '-' }}
+ +
Dokumentasi Foto
+
+
+
Depan
+
+ @if($precheck->front_image_url || $precheck->front_image) + Foto Depan + @else +
Tidak ada gambar
+ @endif +
+
+
+
Suhu Kabin
+
+ @if($precheck->cabin_temperature_image_url || $precheck->cabin_temperature_image) + Foto Suhu Kabin + @else +
Tidak ada gambar
+ @endif +
+
+
+
AC
+
+ @if($precheck->ac_image_url || $precheck->ac_image) + Foto AC + @else +
Tidak ada gambar
+ @endif +
+
+
+
Blower
+
+ @if($precheck->blower_image_url || $precheck->blower_image) + Foto Blower + @else +
Tidak ada gambar
+ @endif +
+
+
+
Evaporator
+
+ @if($precheck->evaporator_image_url || $precheck->evaporator_image) + Foto Evaporator + @else +
Tidak ada gambar
+ @endif +
+
+
+ + @if($precheck->precheck_notes) +
+
Catatan
+
{{ $precheck->precheck_notes }}
+
+ @endif + + +
+ + + + diff --git a/routes/web.php b/routes/web.php index 0e2b1e9..5b4714a 100755 --- a/routes/web.php +++ b/routes/web.php @@ -176,13 +176,25 @@ Route::group(['middleware' => 'auth'], function() { Route::get('/transaction/get-claim-transactions', [TransactionController::class, 'getClaimTransactions'])->name('transaction.get-claim-transactions'); Route::post('/transaction/claim/{id}', [TransactionController::class, 'claim'])->name('transaction.claim'); - // Prechecks Routes - Route::get('/transaction/prechecks/{transaction}', [PrechecksController::class, 'index'])->name('prechecks.index'); - Route::post('/transaction/prechecks/{transaction}', [PrechecksController::class, 'store'])->name('prechecks.store'); - - // Postchecks Routes - Route::get('/transaction/postchecks/{transaction}', [PostchecksController::class, 'index'])->name('postchecks.index'); - Route::post('/transaction/postchecks/{transaction}', [PostchecksController::class, 'store'])->name('postchecks.store'); + Route::prefix('transaction/{transaction}')->group(function () { + // Prechecks + Route::prefix('prechecks')->name('prechecks.')->group(function () { + Route::get('create', [PrechecksController::class, 'create'])->name('create'); + Route::post('store', [PrechecksController::class, 'store'])->name('store'); + Route::get('{precheck}/edit', [PrechecksController::class, 'edit'])->name('edit'); + Route::put('{precheck}', [PrechecksController::class, 'update'])->name('update'); + Route::get('print', [PrechecksController::class, 'print'])->name('print'); + }); + + // Postchecks + Route::prefix('postchecks')->name('postchecks.')->group(function () { + Route::get('create', [PostchecksController::class, 'create'])->name('create'); + Route::post('store', [PostchecksController::class, 'store'])->name('store'); + Route::get('{postcheck}/edit', [PostchecksController::class, 'edit'])->name('edit'); + Route::put('{postcheck}', [PostchecksController::class, 'update'])->name('update'); + Route::get('print', [PostchecksController::class, 'print'])->name('print'); + }); + }); }); // KPI Data Route - accessible to all authenticated users @@ -259,6 +271,9 @@ Route::group(['middleware' => 'auth'], function() { Route::get('/report/transaction_dealer/export', [ReportController::class, 'dealer_export'])->name('report.transaction_dealer.export'); Route::get('/report/transaction_sa/export', [ReportController::class, 'sa_export'])->name('report.transaction_sa.export'); Route::get('/report/transaction_dealer', [ReportController::class, 'transaction_dealer'])->name('report.transaction_dealer'); + + Route::get('report/transaction/precheck/{transaction_id}/print', [PrechecksController::class, 'print'])->name('report.transaction.precheck.print'); + Route::get('report/transaction/postcheck/{transaction_id}/print', [PostchecksController::class, 'print'])->name('report.transaction.postcheck.print'); }); Route::prefix('warehouse')->group(function () {