fix form create update postcheck and precheck
This commit is contained in:
@@ -55,7 +55,6 @@ class AdminController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$ajax_url = route('dashboard_data').'?month='.$month.'&year='.$year.'&dealer='.$dealer;
|
$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'));
|
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::statement("PREPARE stmt FROM @sql");
|
||||||
$dealer_work_trx = DB::select(DB::raw("EXECUTE stmt"));
|
$dealer_work_trx = DB::select(DB::raw("EXECUTE stmt"));
|
||||||
DB::statement('DEALLOCATE PREPARE stmt');
|
DB::statement('DEALLOCATE PREPARE stmt');
|
||||||
// DD($dealer_work_trx);
|
|
||||||
$theads = ['DEALER'];
|
$theads = ['DEALER'];
|
||||||
$dealer_names = [];
|
$dealer_names = [];
|
||||||
$dealer_trx = [];
|
$dealer_trx = [];
|
||||||
@@ -165,7 +163,6 @@ class AdminController extends Controller
|
|||||||
|
|
||||||
$dealer_names[] = $dealer_work->DEALER;
|
$dealer_names[] = $dealer_work->DEALER;
|
||||||
}
|
}
|
||||||
// dd($dealer_trx);
|
|
||||||
$dealer_trx = array_values($dealer_trx);
|
$dealer_trx = array_values($dealer_trx);
|
||||||
$dealer = $request->dealer;
|
$dealer = $request->dealer;
|
||||||
$month = $request->month;
|
$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));
|
$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');
|
$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)) {
|
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);
|
// $month_trxs_total = array_values($month_trxs_total);
|
||||||
// $yesterday_month_trxs_total = array_values($yesterday_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_month_trxs_total = [];
|
||||||
$final_yesterday_month_trxs_total = [];
|
$final_yesterday_month_trxs_total = [];
|
||||||
foreach($works as $work1) {
|
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_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];
|
$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);
|
$month_trxs_total = array_values($final_month_trxs_total);
|
||||||
$yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total);
|
$yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total);
|
||||||
$totals = [];
|
$totals = [];
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ class ApiController extends Controller
|
|||||||
$prev_mth_end = $prev_mth[0].'-'.$prev_mth[1].'-'.date('t');
|
$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');
|
$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)) {
|
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_month_trxs_total[$work1->id] = $month_trxs_total[$work1->id];
|
||||||
$final_yesterday_month_trxs_total[$work1->id] = $yesterday_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);
|
$month_trxs_total = array_values($final_month_trxs_total);
|
||||||
$yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total);
|
$yesterday_month_trxs_total = array_values($final_yesterday_month_trxs_total);
|
||||||
|
|
||||||
|
|||||||
@@ -472,7 +472,6 @@ class ReportController extends Controller
|
|||||||
|
|
||||||
$sa_names = json_encode($sa_names);
|
$sa_names = json_encode($sa_names);
|
||||||
$trx_data = json_encode(array_values($trx_data));
|
$trx_data = json_encode(array_values($trx_data));
|
||||||
// dd($trx_data);
|
|
||||||
$work_count = count($works);
|
$work_count = count($works);
|
||||||
$month = $request->month;
|
$month = $request->month;
|
||||||
$dealer_id = $request->dealer;
|
$dealer_id = $request->dealer;
|
||||||
@@ -703,11 +702,28 @@ class ReportController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$data = Transaction::leftJoin('users', 'users.id', '=', 'transactions.user_id')
|
$data = Transaction::leftJoin('users', 'users.id', '=', 'transactions.user_id')
|
||||||
->leftJoin('users as sa', 'sa.id', '=', 'transactions.user_sa_id')
|
->leftJoin('users as sa', 'sa.id', '=', 'transactions.user_sa_id')
|
||||||
->leftJoin('works as w', 'w.id', '=', 'transactions.work_id')
|
->leftJoin('works as w', 'w.id', '=', 'transactions.work_id')
|
||||||
->leftJoin('categories as cat', 'cat.id', '=', 'w.category_id')
|
->leftJoin('categories as cat', 'cat.id', '=', 'w.category_id')
|
||||||
->leftJoin('dealers as d', 'd.id', '=', 'transactions.dealer_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('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
|
// Filter by allowed dealers based on user role
|
||||||
if($allowedDealers->count() > 0) {
|
if($allowedDealers->count() > 0) {
|
||||||
@@ -747,22 +763,68 @@ class ReportController extends Controller
|
|||||||
$data->orderBy('date', 'DESC');
|
$data->orderBy('date', 'DESC');
|
||||||
return DataTables::of($data)->addIndexColumn()
|
return DataTables::of($data)->addIndexColumn()
|
||||||
->addColumn('action', function($row) use ($menu) {
|
->addColumn('action', function($row) use ($menu) {
|
||||||
$btn = '<div class="d-flex justify-content-center">';
|
$btn = '<div class="d-flex justify-content-center align-items-center flex-wrap">';
|
||||||
|
|
||||||
if($row->status == 1) {
|
// Jika status closed
|
||||||
if(Gate::allows('delete', $menu)) {
|
if ($row->status == 1) {
|
||||||
$btn .= ' <button class="btn btn-danger btn-sm btn-bold mr-2" data-action="'. route('report.transaction.destroy', $row->id) .'" id="destroyTransaction'. $row->id .'" onclick="destroyTransaction('. $row->id .')"> Hapus </button>';
|
if (Gate::allows('delete', $menu)) {
|
||||||
}
|
$btn .= '<button class="btn btn-danger btn-sm font-weight-bold mr-2 mt-2"
|
||||||
$btn .= '<span class="badge badge-success">Closed</span>';
|
data-action="'. route('report.transaction.destroy', $row->id) .'"
|
||||||
}else{
|
id="destroyTransaction'. $row->id .'"
|
||||||
if(Gate::allows('delete', $menu)) {
|
onclick="destroyTransaction('. $row->id .')">
|
||||||
$btn .= '<button class="btn btn-danger btn-sm btn-bold mr-2" data-action="'. route('report.transaction.destroy', $row->id) .'" id="destroyTransaction'. $row->id .'" onclick="destroyTransaction('. $row->id .')"> Hapus </button>';
|
Hapus
|
||||||
|
</button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Gate::allows('update', $menu)) {
|
// Badge Closed rapi
|
||||||
$btn .= '<button class="btn btn-info btn-sm btn-bold mr-2" data-url="'. route('report.transaction.edit', $row->id) .'" data-action="'. route('report.transaction.update', $row->id) .'" onclick="editTransaction('. $row->id .')" id="editTransaction'. $row->id .'"> Edit </button>
|
$btn .= '<span class="btn btn-success btn-sm font-weight-bold px-3 py-2 mr-2 mt-2 disabled"
|
||||||
<button class="btn btn-warning btn-sm btn-bold" id="closeTransaction'. $row->id .'" data-url="'. route('report.transaction.close', $row->id) .'" onclick="closeTransaction('. $row->id .')"> Close </button>';
|
style="pointer-events: none; cursor: default;">
|
||||||
|
Closed
|
||||||
|
</span>';
|
||||||
|
} else {
|
||||||
|
if (Gate::allows('delete', $menu)) {
|
||||||
|
$btn .= '<button class="btn btn-danger btn-sm font-weight-bold mr-2 mt-2"
|
||||||
|
data-action="'. route('report.transaction.destroy', $row->id) .'"
|
||||||
|
id="destroyTransaction'. $row->id .'"
|
||||||
|
onclick="destroyTransaction('. $row->id .')">
|
||||||
|
Hapus
|
||||||
|
</button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Gate::allows('update', $menu)) {
|
||||||
|
$btn .= '<button class="btn btn-info btn-sm font-weight-bold mr-2 mt-2"
|
||||||
|
data-url="'. route('report.transaction.edit', $row->id) .'"
|
||||||
|
data-action="'. route('report.transaction.update', $row->id) .'"
|
||||||
|
onclick="editTransaction('. $row->id .')"
|
||||||
|
id="editTransaction'. $row->id .'">
|
||||||
|
Edit
|
||||||
|
</button>';
|
||||||
|
|
||||||
|
$btn .= '<button class="btn btn-warning btn-sm font-weight-bold mr-2 mt-2"
|
||||||
|
id="closeTransaction'. $row->id .'"
|
||||||
|
data-url="'. route('report.transaction.close', $row->id) .'"
|
||||||
|
onclick="closeTransaction('. $row->id .')">
|
||||||
|
Close
|
||||||
|
</button>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($row->precheck_id) {
|
||||||
|
$btn .= '<button class="btn btn-primary btn-sm font-weight-bold action-print mr-2 mt-2"
|
||||||
|
data-type="precheck"
|
||||||
|
data-id="'. $row->id .'"
|
||||||
|
data-url="'. route('report.transaction.precheck.print', $row->id) .'">
|
||||||
|
Pre Check
|
||||||
|
</button>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($row->postcheck_id) {
|
||||||
|
$btn .= '<button class="btn btn-success btn-sm font-weight-bold action-print mr-2 mt-2"
|
||||||
|
data-type="postcheck"
|
||||||
|
data-id="'. $row->id .'"
|
||||||
|
data-url="'. route('report.transaction.postcheck.print', $row->id) .'">
|
||||||
|
Post Check
|
||||||
|
</button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$btn .= '</div>';
|
$btn .= '</div>';
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ use Illuminate\Support\Carbon;
|
|||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use App\Models\Precheck;
|
||||||
|
use App\Models\Postcheck;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class TransactionController extends Controller
|
class TransactionController extends Controller
|
||||||
@@ -519,7 +522,6 @@ class TransactionController extends Controller
|
|||||||
$works_count = count($works);
|
$works_count = count($works);
|
||||||
$share = $month_trxs;
|
$share = $month_trxs;
|
||||||
$month = $request->month;
|
$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'));
|
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');
|
$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');
|
$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)) {
|
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_month_trxs_total = [];
|
||||||
$final_yesterday_month_trxs_total = [];
|
$final_yesterday_month_trxs_total = [];
|
||||||
foreach($works as $work1) {
|
foreach($works as $work1) {
|
||||||
$final_month_trxs_total[$work1->id] = $month_trxs_total[$work1->id];
|
$final_month_trxs_total[$work1->id] = $month_trxs_total[$work1->id];
|
||||||
$final_yesterday_month_trxs_total[$work1->id] = $yesterday_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);
|
$month_trxs_total = array_values($final_month_trxs_total);
|
||||||
$yesterday_month_trxs_total = array_values($final_yesterday_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)
|
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,
|
"spk" => $request->spk,
|
||||||
"date" => $request->date,
|
"date" => $request->date,
|
||||||
"police_number" => $request->police_number,
|
"police_number" => $request->police_number,
|
||||||
"work_id" => $request->work_id,
|
"work_id" => $request->work_id,
|
||||||
"qty" => $request->qty,
|
"qty" => $request->qty,
|
||||||
"warranty" => $request->warranty,
|
"warranty" => $request->warranty,
|
||||||
"user_sa_id" => $request->sa_id,
|
"user_sa_id" => $request->user_sa_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response = [
|
$response = [
|
||||||
"status" => 200,
|
"status" => 200,
|
||||||
"message" => "Data updated successfully"
|
"message" => "Transaksi berhasil diperbarui"
|
||||||
];
|
];
|
||||||
|
|
||||||
return response()->json($response);
|
return response()->json($response);
|
||||||
@@ -1147,6 +1157,8 @@ class TransactionController extends Controller
|
|||||||
'sa_name' => $transaction->sa_name,
|
'sa_name' => $transaction->sa_name,
|
||||||
'status' => $this->getStatusBadge($transaction->status),
|
'status' => $this->getStatusBadge($transaction->status),
|
||||||
'action' => $this->getActionButtons($transaction),
|
'action' => $this->getActionButtons($transaction),
|
||||||
|
'action_precheck' => $this->getActionButtonsPrecheck($transaction),
|
||||||
|
'action_postcheck' => $this->getActionButtonsPostcheck($transaction),
|
||||||
'claimed_at' => $transaction->claimed_at,
|
'claimed_at' => $transaction->claimed_at,
|
||||||
'claimed_by' => $transaction->claimed_by
|
'claimed_by' => $transaction->claimed_by
|
||||||
];
|
];
|
||||||
@@ -1259,41 +1271,111 @@ class TransactionController extends Controller
|
|||||||
{
|
{
|
||||||
$buttons = '';
|
$buttons = '';
|
||||||
|
|
||||||
// Only show buttons for mechanics
|
// Edit button - show for all users (not just mechanics)
|
||||||
|
$buttons .= '<button class="btn btn-sm btn-warning mr-1"
|
||||||
|
data-action="' . route('transaction.update', $transaction->id) . '"
|
||||||
|
data-url="' . route('transaction.edit', $transaction->id) . '"
|
||||||
|
onclick="editTransaction(' . $transaction->id . ')"
|
||||||
|
id="editTransaction' . $transaction->id . '"
|
||||||
|
title="Edit Transaksi"
|
||||||
|
style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-edit"></i> Edit
|
||||||
|
</button>';
|
||||||
|
|
||||||
|
// Delete button - show for all users
|
||||||
|
$buttons .= '<button class="btn btn-sm btn-danger mr-1"
|
||||||
|
onclick="deleteTransaction(' . $transaction->id . ')"
|
||||||
|
title="Hapus Transaksi"
|
||||||
|
style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-trash"></i> Hapus
|
||||||
|
</button>';
|
||||||
|
|
||||||
|
// Only show claim buttons for mechanics
|
||||||
if (Auth::user()->role_id == 3) {
|
if (Auth::user()->role_id == 3) {
|
||||||
|
|
||||||
// Claim button - show only if not claimed yet
|
// Claim button - show only if not claimed yet
|
||||||
if (empty($transaction->claimed_at) && empty($transaction->claimed_by)) {
|
if (empty($transaction->claimed_at) && empty($transaction->claimed_by)) {
|
||||||
$buttons .= '<button class="btn btn-sm btn-success mr-1" onclick="claimTransaction(' . $transaction->id . ')" title="Klaim Pekerjaan">';
|
$buttons .= '<button class="btn btn-sm btn-success mr-1" onclick="claimTransaction(' . $transaction->id . ')" title="Klaim Pekerjaan" style="font-size: 11px; padding: 4px 8px;">';
|
||||||
$buttons .= 'Klaim';
|
$buttons .= '<i class="fas fa-hand-paper"></i> Klaim';
|
||||||
$buttons .= '</button>';
|
$buttons .= '</button>';
|
||||||
} else {
|
} else {
|
||||||
if($transaction->claimed_by == Auth::user()->id) {
|
if ($transaction->claimed_by == Auth::user()->id) {
|
||||||
// Check if precheck exists
|
$precheck = Precheck::where('transaction_id', $transaction->id)->first();
|
||||||
$precheck = \App\Models\Precheck::where('transaction_id', $transaction->id)->first();
|
$postcheck = Postcheck::where('transaction_id', $transaction->id)->first();
|
||||||
if (!$precheck) {
|
|
||||||
$buttons .= '<a href="/transaction/prechecks/' . $transaction->id . '" class="btn btn-sm btn-warning mr-1" title="Precheck">';
|
if ($precheck && $postcheck) {
|
||||||
$buttons .= 'Precheck';
|
$buttons .= '<span class="badge badge-success" style="font-size: 10px;"><i class="fas fa-check"></i> Selesai</span>';
|
||||||
$buttons .= '</a>';
|
|
||||||
} else {
|
|
||||||
// Check if postcheck exists
|
|
||||||
$postcheck = \App\Models\Postcheck::where('transaction_id', $transaction->id)->first();
|
|
||||||
if (!$postcheck) {
|
|
||||||
$buttons .= '<a href="/transaction/postchecks/' . $transaction->id . '" class="btn btn-sm btn-info mr-1" title="Postcheck">';
|
|
||||||
$buttons .= 'Postcheck';
|
|
||||||
$buttons .= '</a>';
|
|
||||||
} else {
|
|
||||||
$buttons .= '<span class="badge badge-success">Selesai</span>';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$buttons .= '<span class="badge badge-info">Sudah Diklaim</span>';
|
$buttons .= '<span class="badge badge-info" style="font-size: 10px;"><i class="fas fa-check-circle"></i> Sudah Diklaim</span>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $buttons;
|
return $buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getActionButtonsPrecheck($transaction)
|
||||||
|
{
|
||||||
|
$buttons = '';
|
||||||
|
|
||||||
|
if (Auth::user()->role_id == 3) {
|
||||||
|
$precheck = Precheck::where('transaction_id', $transaction->id)->first();
|
||||||
|
|
||||||
|
if ($precheck) {
|
||||||
|
$buttons .= '<a href="' . route('prechecks.edit', [$transaction->id, $precheck->id]) . '"
|
||||||
|
class="btn btn-sm btn-warning mr-1" title="Edit Precheck" style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-edit"></i> Edit
|
||||||
|
</a>';
|
||||||
|
$buttons .= '<a href="' . route('prechecks.print', $transaction->id) . '"
|
||||||
|
class="btn btn-sm btn-primary mr-1" title="Lihat Precheck" target="_blank" style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-eye"></i> Lihat
|
||||||
|
</a>';
|
||||||
|
} else {
|
||||||
|
if (empty($transaction->claimed_at) && empty($transaction->claimed_by)) {
|
||||||
|
$buttons .= '<span class="badge badge-danger" style="font-size: 10px;">Transaksi Belum Diklaim</span>';
|
||||||
|
}else{
|
||||||
|
$buttons .= '<a href="' . route('prechecks.create', $transaction->id) . '"
|
||||||
|
class="btn btn-sm btn-success mr-1" title="Tambah Precheck" style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-plus"></i> Tambah
|
||||||
|
</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 .= '<a href="' . route('postchecks.edit', [$transaction->id, $postcheck->id]) . '"
|
||||||
|
class="btn btn-sm btn-warning mr-1" title="Edit Postcheck" style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-edit"></i> Edit
|
||||||
|
</a>';
|
||||||
|
$buttons .= '<a href="' . route('postchecks.print', $transaction->id) . '"
|
||||||
|
class="btn btn-sm btn-primary mr-1" title="Lihat Postcheck" target="_blank" style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-eye"></i> Lihat
|
||||||
|
</a>';
|
||||||
|
} else {
|
||||||
|
$buttons .= '<a href="' . route('postchecks.create', $transaction->id) . '"
|
||||||
|
class="btn btn-sm btn-success mr-1" title="Tambah Postcheck" style="font-size: 11px; padding: 4px 8px;">
|
||||||
|
<i class="fas fa-plus"></i> Tambah
|
||||||
|
</a>';
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$buttons .= '<span class="badge badge-danger" style="font-size: 10px;">Precheck Belum Disimpan</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $buttons;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get KPI data for AJAX refresh
|
* Get KPI data for AJAX refresh
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ use Illuminate\Support\Facades\Log;
|
|||||||
|
|
||||||
class PostchecksController extends Controller
|
class PostchecksController extends Controller
|
||||||
{
|
{
|
||||||
public function index(Transaction $transaction)
|
public function create(Transaction $transaction)
|
||||||
{
|
{
|
||||||
$acConditions = Postcheck::getAcConditionOptions();
|
$acConditions = Postcheck::getAcConditionOptions();
|
||||||
$blowerConditions = Postcheck::getBlowerConditionOptions();
|
$blowerConditions = Postcheck::getBlowerConditionOptions();
|
||||||
$evaporatorConditions = Postcheck::getEvaporatorConditionOptions();
|
$evaporatorConditions = Postcheck::getEvaporatorConditionOptions();
|
||||||
$compressorConditions = Postcheck::getCompressorConditionOptions();
|
$compressorConditions = Postcheck::getCompressorConditionOptions();
|
||||||
|
|
||||||
return view('transaction.postchecks', compact(
|
return view('transaction.postchecks.create', compact(
|
||||||
'transaction',
|
'transaction',
|
||||||
'acConditions',
|
'acConditions',
|
||||||
'blowerConditions',
|
'blowerConditions',
|
||||||
@@ -62,76 +62,15 @@ class PostchecksController extends Controller
|
|||||||
'compressor_condition' => $request->compressor_condition,
|
'compressor_condition' => $request->compressor_condition,
|
||||||
'postcheck_notes' => $request->postcheck_notes,
|
'postcheck_notes' => $request->postcheck_notes,
|
||||||
];
|
];
|
||||||
|
// Handle file uploads securely
|
||||||
// Handle file uploads
|
|
||||||
$imageFields = [
|
$imageFields = [
|
||||||
'front_image', 'cabin_temperature_image', 'ac_image',
|
'front_image', 'cabin_temperature_image', 'ac_image',
|
||||||
'blower_image', 'evaporator_image'
|
'blower_image', 'evaporator_image'
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($imageFields as $field) {
|
foreach ($imageFields as $field) {
|
||||||
if ($request->hasFile($field) && $request->file($field)->isValid()) {
|
$storedPath = $this->processImageUpload($request, $field, $transaction);
|
||||||
try {
|
if ($storedPath) {
|
||||||
$file = $request->file($field);
|
$data[$field] = $storedPath;
|
||||||
|
|
||||||
// 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()]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
* Ensure the base storage directory exists
|
||||||
*/
|
*/
|
||||||
@@ -185,4 +209,69 @@ class PostchecksController extends Controller
|
|||||||
rmdir($testDir);
|
rmdir($testDir);
|
||||||
Log::info('Storage directory is properly configured: ' . $storagePath);
|
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()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ use Illuminate\Support\Facades\Log;
|
|||||||
|
|
||||||
class PrechecksController extends Controller
|
class PrechecksController extends Controller
|
||||||
{
|
{
|
||||||
public function index(Transaction $transaction)
|
public function create(Transaction $transaction)
|
||||||
{
|
{
|
||||||
$acConditions = Precheck::getAcConditionOptions();
|
$acConditions = Precheck::getAcConditionOptions();
|
||||||
$blowerConditions = Precheck::getBlowerConditionOptions();
|
$blowerConditions = Precheck::getBlowerConditionOptions();
|
||||||
$evaporatorConditions = Precheck::getEvaporatorConditionOptions();
|
$evaporatorConditions = Precheck::getEvaporatorConditionOptions();
|
||||||
$compressorConditions = Precheck::getCompressorConditionOptions();
|
$compressorConditions = Precheck::getCompressorConditionOptions();
|
||||||
|
|
||||||
return view('transaction.prechecks', compact(
|
return view('transaction.prechecks.create', compact(
|
||||||
'transaction',
|
'transaction',
|
||||||
'acConditions',
|
'acConditions',
|
||||||
'blowerConditions',
|
'blowerConditions',
|
||||||
@@ -74,6 +74,11 @@ class PrechecksController extends Controller
|
|||||||
try {
|
try {
|
||||||
$file = $request->file($field);
|
$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
|
// Generate unique filename with transaction ID
|
||||||
$filename = time() . '_' . uniqid() . '_' . $transaction->id . '_' . $field . '.' . $file->getClientOriginalExtension();
|
$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
|
* Ensure the base storage directory exists
|
||||||
*/
|
*/
|
||||||
@@ -185,4 +342,138 @@ class PrechecksController extends Controller
|
|||||||
rmdir($testDir);
|
rmdir($testDir);
|
||||||
Log::info('Storage directory is properly configured: ' . $storagePath);
|
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 = [
|
||||||
|
'<?php', '<?=', '<script', 'javascript:', 'data:', 'vbscript:',
|
||||||
|
'..', '~', '$', '`', '|', '&', ';', '(', ')', '{', '}',
|
||||||
|
'exec', 'system', 'shell_exec', 'passthru', 'eval'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($suspiciousPatterns as $pattern) {
|
||||||
|
if (stripos($filename, $pattern) !== false) {
|
||||||
|
Log::warning('Suspicious filename pattern detected: ' . $pattern, [
|
||||||
|
'filename' => $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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class adminRole
|
|||||||
{
|
{
|
||||||
// check if user can access admin area
|
// 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();
|
$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) {
|
if (!$user) {
|
||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class Postcheck extends Model
|
|||||||
*/
|
*/
|
||||||
public function getFrontImageUrlAttribute()
|
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()
|
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()
|
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()
|
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()
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class Precheck extends Model
|
|||||||
*/
|
*/
|
||||||
public function getFrontImageUrlAttribute()
|
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()
|
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()
|
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()
|
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()
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,28 +1,5 @@
|
|||||||
{
|
{
|
||||||
"/js/app.js": "/js/app.js",
|
"/js/app.js": "/js/app.js",
|
||||||
"/js/vendor.js": "/js/vendor.js",
|
"/js/vendor.js": "/js/vendor.js",
|
||||||
"/js/warehouse_management/product_categories/index.js": "/js/warehouse_management/product_categories/index.js",
|
"/css/app.css": "/css/app.css"
|
||||||
"/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"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,7 +223,14 @@ var table = $('#kt_table').DataTable({
|
|||||||
return `<input type="checkbox" name="selected[]" value="${data}" />`;
|
return `<input type="checkbox" name="selected[]" value="${data}" />`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{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: 'dealer_name', name: 'd.name'},
|
||||||
{data: 'username', name: 'users.name'},
|
{data: 'username', name: 'users.name'},
|
||||||
{data: 'sa_name', name: 'sa.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");
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
|
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 {
|
#claimTransactionsTable {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@@ -352,6 +352,186 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
font-weight: 600;
|
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 */
|
/* Tab claim specific styling */
|
||||||
#form-claim {
|
#form-claim {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@@ -737,9 +917,6 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
<input type="hidden" name="category" value="work">
|
<input type="hidden" name="category" value="work">
|
||||||
<div class="work_multirow">
|
<div class="work_multirow">
|
||||||
@if (old('work_id') && old('form') == 'work')
|
@if (old('work_id') && old('form') == 'work')
|
||||||
{{-- @php
|
|
||||||
dd($errors->all());
|
|
||||||
@endphp --}}
|
|
||||||
<input type="hidden" class="work_field_counter" value="{{ count(old('work_id')) }}">
|
<input type="hidden" class="work_field_counter" value="{{ count(old('work_id')) }}">
|
||||||
@for ($i = 0; $i < count(old('work_id')); $i++)
|
@for ($i = 0; $i < count(old('work_id')); $i++)
|
||||||
<div class="form-group row" id="work_field{{ $i+1 }}">
|
<div class="form-group row" id="work_field{{ $i+1 }}">
|
||||||
@@ -957,20 +1134,21 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
<h6 class="mb-3">Daftar Pekerjaan yang Dapat Diklaim</h6>
|
<h6 class="mb-3">Daftar Pekerjaan yang Dapat Diklaim</h6>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered table-hover" id="claimTransactionsTable">
|
<table class="table table-bordered table-hover" id="claimTransactionsTable">
|
||||||
<thead class="thead-light">
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="10%">Tanggal</th>
|
<th>Tanggal</th>
|
||||||
<th width="15%">No. SPK</th>
|
<th>SPK</th>
|
||||||
<th width="15%">No. Polisi</th>
|
<th>No Polisi</th>
|
||||||
<th width="20%">Pekerjaan</th>
|
<th>Pekerjaan</th>
|
||||||
<th width="10%">Qty</th>
|
<th>Qty</th>
|
||||||
<th width="15%">Service Advisor</th>
|
<th>Service Advisor</th>
|
||||||
<th width="10%">Status</th>
|
<th>Status</th>
|
||||||
<th width="5%">Aksi</th>
|
<th>Aksi</th>
|
||||||
|
<th>Pre Check</th>
|
||||||
|
<th>Post Check</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- Data will be loaded via DataTables AJAX -->
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -1180,6 +1358,73 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Edit Transaksi -->
|
||||||
|
<div class="modal fade" id="editTransactionModal" tabindex="-1" role="dialog" aria-labelledby="editTransactionModalLabel">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editTransactionModalLabel">Edit Transaksi</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form id="editTransactionForm" action="" method="POST">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
<input type="hidden" name="transaction_id" value="">
|
||||||
|
<input type="hidden" name="from_transaction_page" value="1">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>No. SPK</label>
|
||||||
|
<input type="text" class="form-control" name="spk" placeholder="No. SPK" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Tanggal</label>
|
||||||
|
<input type="text" class="form-control" name="date" id="edit-transaction-date" placeholder="YYYY-MM-DD" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>No. Polisi</label>
|
||||||
|
<input type="text" class="form-control" name="police_number" placeholder="No. Polisi" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Pekerjaan</label>
|
||||||
|
<select class="form-control" name="work_id" required>
|
||||||
|
<option value="" disabled>Pilih Pekerjaan</option>
|
||||||
|
@foreach ($work_works as $work)
|
||||||
|
<option value="{{ $work->id }}">{{ $work->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Qty</label>
|
||||||
|
<input type="number" class="form-control" name="qty" min="1" placeholder="Qty" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Warranty</label>
|
||||||
|
<select class="form-control" name="warranty" required>
|
||||||
|
<option value="1">Ya</option>
|
||||||
|
<option value="0">Tidak</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Service Advisor</label>
|
||||||
|
<select class="form-control" name="user_sa_id" required>
|
||||||
|
<option value="">Pilih Service Advisor</option>
|
||||||
|
@foreach ($user_sas as $user_sa)
|
||||||
|
<option value="{{ $user_sa->id }}">{{ $user_sa->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
|
||||||
|
<button type="submit" class="btn btn-success" id="editTransactionButton">Simpan</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal Detail Mutasi -->
|
<!-- Modal Detail Mutasi -->
|
||||||
<div class="modal fade" id="mutationDetailModal" tabindex="-1" role="dialog" aria-labelledby="mutationDetailModalLabel">
|
<div class="modal fade" id="mutationDetailModal" tabindex="-1" role="dialog" aria-labelledby="mutationDetailModalLabel">
|
||||||
<div class="modal-dialog modal-xl" role="document">
|
<div class="modal-dialog modal-xl" role="document">
|
||||||
@@ -1866,6 +2111,11 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
autoclose: true,
|
autoclose: true,
|
||||||
todayHighlight: true
|
todayHighlight: true
|
||||||
});
|
});
|
||||||
|
$("#edit-transaction-date").datepicker({
|
||||||
|
format: 'yyyy-mm-dd',
|
||||||
|
autoclose: true,
|
||||||
|
todayHighlight: true
|
||||||
|
});
|
||||||
$("#date-opname").datepicker({
|
$("#date-opname").datepicker({
|
||||||
format: 'yyyy-mm-dd',
|
format: 'yyyy-mm-dd',
|
||||||
autoclose: true,
|
autoclose: true,
|
||||||
@@ -2508,19 +2758,21 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{data: 'date', name: 'date'},
|
{data: 'date', name: 'date', title: 'Tanggal'},
|
||||||
{data: 'spk', name: 'spk'},
|
{data: 'spk', name: 'spk', title: 'SPK'},
|
||||||
{data: 'police_number', name: 'police_number'},
|
{data: 'police_number', name: 'police_number', title: 'No Polisi'},
|
||||||
{data: 'work_name', name: 'work_name'},
|
{data: 'work_name', name: 'work_name', title: 'Pekerjaan'},
|
||||||
{data: 'qty', name: 'qty'},
|
{data: 'qty', name: 'qty', title: 'Qty'},
|
||||||
{data: 'sa_name', name: 'sa_name'},
|
{data: 'sa_name', name: 'sa_name', title: 'Service Advisor'},
|
||||||
{data: 'status', name: 'status', orderable: false},
|
{data: 'status', name: 'status', title: 'Status', orderable: false},
|
||||||
{data: 'action', name: 'action', orderable: false, searchable: false}
|
{data: 'action', name: 'action', title: 'Aksi', orderable: false, searchable: false},
|
||||||
|
{data: 'action_precheck', name: 'action_precheck', title: 'Pre Check', orderable: false, searchable: false},
|
||||||
|
{data: 'action_postcheck', name: 'action_postcheck', title: 'Post Check', orderable: false, searchable: false},
|
||||||
],
|
],
|
||||||
pageLength: 10,
|
pageLength: 10,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
scrollX: true,
|
scrollX: true,
|
||||||
order: [[0, 'desc']], // Sort by date descending
|
order: [[0, 'desc']]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2934,49 +3186,71 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
}
|
}
|
||||||
|
|
||||||
function editTransaction(transactionId) {
|
function editTransaction(transactionId) {
|
||||||
// Redirect to edit page or show edit modal
|
// Load transaction data and show modal with form
|
||||||
window.location.href = '{{ route("transaction.edit", ":id") }}'.replace(':id', transactionId);
|
$('#editTransactionModal').modal('show');
|
||||||
|
const url = '{{ route("transaction.edit", ":id") }}'.replace(':id', transactionId);
|
||||||
|
const action = '{{ route("transaction.update", ":id") }}'.replace(':id', transactionId);
|
||||||
|
|
||||||
|
// Reset form
|
||||||
|
const form = document.getElementById('editTransactionForm');
|
||||||
|
form.reset();
|
||||||
|
form.action = action;
|
||||||
|
form.querySelector('input[name="transaction_id"]').value = transactionId;
|
||||||
|
|
||||||
|
// Fetch data
|
||||||
|
$.get(url, function(response) {
|
||||||
|
if (response && response.status === 200 && response.data) {
|
||||||
|
const trx = response.data;
|
||||||
|
form.querySelector('input[name="spk"]').value = trx.spk || '';
|
||||||
|
form.querySelector('input[name="date"]').value = trx.date || '';
|
||||||
|
form.querySelector('input[name="police_number"]').value = trx.police_number || '';
|
||||||
|
form.querySelector('select[name="work_id"]').value = trx.work_id || '';
|
||||||
|
form.querySelector('input[name="qty"]').value = trx.qty || 1;
|
||||||
|
form.querySelector('select[name="warranty"]').value = String(trx.warranty ?? '0');
|
||||||
|
form.querySelector('select[name="user_sa_id"]').value = trx.user_sa_id || '';
|
||||||
|
} else {
|
||||||
|
Swal.fire({ icon: 'error', title: 'Error', text: 'Gagal memuat data transaksi' });
|
||||||
|
}
|
||||||
|
}).fail(function() {
|
||||||
|
Swal.fire({ icon: 'error', title: 'Error', text: 'Terjadi kesalahan saat memuat data' });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteTransaction(transactionId) {
|
function deleteTransaction(transactionId) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Konfirmasi Hapus',
|
title: 'Konfirmasi Hapus',
|
||||||
text: 'Apakah Anda yakin ingin menghapus transaksi ini?',
|
html: '<div class="text-left">Tindakan ini tidak dapat dibatalkan.<br>Hapus transaksi ini?</div>',
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonColor: '#d33',
|
confirmButtonColor: '#d33',
|
||||||
cancelButtonColor: '#3085d6',
|
cancelButtonColor: '#6c757d',
|
||||||
confirmButtonText: 'Ya, Hapus!',
|
confirmButtonText: 'Ya, Hapus',
|
||||||
cancelButtonText: 'Batal'
|
cancelButtonText: 'Batal'
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
// Send delete request
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '{{ route("transaction.destroy", ":id") }}'.replace(':id', transactionId),
|
url: '{{ route("transaction.destroy", ":id") }}'.replace(':id', transactionId),
|
||||||
method: 'DELETE',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
|
_method: 'DELETE',
|
||||||
_token: '{{ csrf_token() }}'
|
_token: '{{ csrf_token() }}'
|
||||||
},
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
title: 'Berhasil!',
|
title: 'Berhasil',
|
||||||
text: 'Transaksi berhasil dihapus',
|
text: response && response.message ? response.message : 'Transaksi berhasil dihapus',
|
||||||
timer: 2000,
|
timer: 1800,
|
||||||
showConfirmButton: false
|
showConfirmButton: false
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Refresh the table
|
|
||||||
if (claimTransactionsTable) {
|
if (claimTransactionsTable) {
|
||||||
claimTransactionsTable.ajax.reload();
|
claimTransactionsTable.ajax.reload(null, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function(xhr) {
|
||||||
Swal.fire({
|
const msg = (xhr.responseJSON && xhr.responseJSON.message) ? xhr.responseJSON.message : 'Gagal menghapus transaksi';
|
||||||
icon: 'error',
|
Swal.fire({ icon: 'error', title: 'Error', text: msg });
|
||||||
title: 'Error',
|
|
||||||
text: 'Gagal menghapus transaksi'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3274,6 +3548,51 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
initializePhysicalStock();
|
initializePhysicalStock();
|
||||||
handleServerSideErrors();
|
handleServerSideErrors();
|
||||||
initializeTabs();
|
initializeTabs();
|
||||||
|
|
||||||
|
// Handle edit transaction form submit via AJAX
|
||||||
|
$(document).on('submit', '#editTransactionForm', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = this;
|
||||||
|
const action = form.action;
|
||||||
|
const formData = $(form).serialize();
|
||||||
|
|
||||||
|
// Disable button to prevent double submit
|
||||||
|
const $btn = $('#editTransactionButton');
|
||||||
|
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Menyimpan...');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: action,
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
success: function(response) {
|
||||||
|
$('#editTransactionModal').modal('hide');
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil',
|
||||||
|
text: (response && response.message) ? response.message : 'Transaksi berhasil diperbarui',
|
||||||
|
timer: 1800,
|
||||||
|
showConfirmButton: false
|
||||||
|
}).then(() => {
|
||||||
|
if (claimTransactionsTable) {
|
||||||
|
claimTransactionsTable.ajax.reload(null, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
let msg = 'Gagal memperbarui transaksi';
|
||||||
|
if (xhr.responseJSON && xhr.responseJSON.errors) {
|
||||||
|
const errors = xhr.responseJSON.errors;
|
||||||
|
msg = Object.values(errors).flat().join('\n');
|
||||||
|
} else if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
|
msg = xhr.responseJSON.message;
|
||||||
|
}
|
||||||
|
Swal.fire({ icon: 'error', title: 'Error', text: msg });
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
$btn.prop('disabled', false).text('Simpan');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle sub-tab switching for transaksi tabs
|
// Handle sub-tab switching for transaksi tabs
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
<a href="{{ route('transaction') }}" class="mt-4 btn btn-danger"><i class="fa fa-chevron-left"></i> Kembali</a>
|
<a href="{{ route('transaction') }}" class="mt-4 btn btn-danger"><i class="fa fa-chevron-left"></i> Kembali</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
{{-- <h5 class="text-center mt-4">Cipta Kreasi Baru</h5> --}}
|
|
||||||
<a href="/"><img src="{{ asset('logo-ckb.png') }}" style="width: 100%" alt="LOGO CKB"></a>
|
<a href="/"><img src="{{ asset('logo-ckb.png') }}" style="width: 100%" alt="LOGO CKB"></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -294,17 +293,6 @@
|
|||||||
|
|
||||||
$string .= "*TOTAL: ".$total_qty." Unit*\n\n";
|
$string .= "*TOTAL: ".$total_qty." Unit*\n\n";
|
||||||
$overall_total += $total_qty;
|
$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 {
|
} else {
|
||||||
$string .= "*Tidak ada data transaksi hari ini*\n\n";
|
$string .= "*Tidak ada data transaksi hari ini*\n\n";
|
||||||
@@ -385,6 +373,18 @@
|
|||||||
<script>
|
<script>
|
||||||
$("#kt-table").DataTable()
|
$("#kt-table").DataTable()
|
||||||
|
|
||||||
|
function precheck(id) {
|
||||||
|
let url = $("#precheck"+id).attr("data-url")
|
||||||
|
console.log(url)
|
||||||
|
// window.open(url, "_blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
function postcheck(id) {
|
||||||
|
let url = $("#postcheck"+id).attr("data-url")
|
||||||
|
console.log(url)
|
||||||
|
// window.open(url, "_blank")
|
||||||
|
}
|
||||||
|
|
||||||
// Check if Web Share API is supported
|
// Check if Web Share API is supported
|
||||||
function isWebShareSupported() {
|
function isWebShareSupported() {
|
||||||
return navigator.share && typeof navigator.share === 'function';
|
return navigator.share && typeof navigator.share === 'function';
|
||||||
|
|||||||
1548
resources/views/transaction/postchecks/_form.blade.php
Normal file
1548
resources/views/transaction/postchecks/_form.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
5
resources/views/transaction/postchecks/create.blade.php
Normal file
5
resources/views/transaction/postchecks/create.blade.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@extends('layouts.frontapp')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('transaction.postchecks._form')
|
||||||
|
@endsection
|
||||||
5
resources/views/transaction/postchecks/edit.blade.php
Normal file
5
resources/views/transaction/postchecks/edit.blade.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@extends('layouts.frontapp')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('transaction.postchecks._form')
|
||||||
|
@endsection
|
||||||
397
resources/views/transaction/postchecks/print.blade.php
Normal file
397
resources/views/transaction/postchecks/print.blade.php
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Print Postcheck - {{ $postcheck->spk_number ?? '-' }}</title>
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
@page {
|
||||||
|
margin: 0.5in;
|
||||||
|
size: A4;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-print {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #333;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
border-bottom: 3px solid #2c5282;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-info {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-name {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-tagline {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 250px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px dotted #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4a5568;
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #2d3748;
|
||||||
|
width: 50%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th {
|
||||||
|
background-color: #2c5282;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table td {
|
||||||
|
padding: 10px 8px;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-box {
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
background: #f7fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-box img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 240px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-section {
|
||||||
|
background-color: #f7fafc;
|
||||||
|
border-left: 4px solid #2c5282;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signatures {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-box {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-line {
|
||||||
|
margin-top: 60px;
|
||||||
|
border-top: 1px solid #2d3748;
|
||||||
|
padding-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: #2c5282;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button:hover {
|
||||||
|
background-color: #2a4365;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right { text-align: right; }
|
||||||
|
.text-center { text-align: center; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button class="print-button no-print" onclick="window.print()">Print</button>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="company-info">
|
||||||
|
<div class="company-name">PT. CIPTA KREASI BARU</div>
|
||||||
|
<div class="company-tagline">Postcheck Kendaraan</div>
|
||||||
|
</div>
|
||||||
|
<div class="document-title">Dokumen Postcheck</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-section">
|
||||||
|
<div class="info-box">
|
||||||
|
<div class="info-title">Informasi Transaksi</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">No. Polisi</span>
|
||||||
|
<span class="info-value">{{ $postcheck->police_number ?? $postcheck->transaction->police_number ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">No. SPK</span>
|
||||||
|
<span class="info-value">{{ $postcheck->spk_number ?? $postcheck->transaction->spk ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Tanggal Postcheck</span>
|
||||||
|
<span class="info-value">{{ optional($postcheck->postcheck_at)->format('d F Y H:i') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Dibuat oleh</span>
|
||||||
|
<span class="info-value">{{ $postcheck->postcheckBy->name ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
|
<div class="info-title">Parameter Utama</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Kilometer</span>
|
||||||
|
<span class="info-value">{{ number_format((float)($postcheck->kilometer ?? 0), 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Tekanan High</span>
|
||||||
|
<span class="info-value">{{ number_format((float)($postcheck->pressure_high ?? 0), 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Tekanan Low</span>
|
||||||
|
<span class="info-value">{{ number_format((float)($postcheck->pressure_low ?? 0), 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Suhu Kabin</span>
|
||||||
|
<span class="info-value">{{ isset($postcheck->cabin_temperature) ? number_format((float)$postcheck->cabin_temperature, 2) : '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-title">Kondisi Komponen</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 35%;">Komponen</th>
|
||||||
|
<th style="width: 25%;">Status Pekerjaan</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>AC</td>
|
||||||
|
<td>{{ $postcheck->ac_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Blower</td>
|
||||||
|
<td>{{ $postcheck->blower_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Evaporator</td>
|
||||||
|
<td>{{ $postcheck->evaporator_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Compressor</td>
|
||||||
|
<td>{{ $postcheck->compressor_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="section-title">Dokumentasi Foto</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Depan</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($postcheck->front_image_url || $postcheck->front_image)
|
||||||
|
<img src="{{ $postcheck->front_image_url ?? asset('storage/' . ltrim($postcheck->front_image, '/')) }}" alt="Foto Depan">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Suhu Kabin</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($postcheck->cabin_temperature_image_url || $postcheck->cabin_temperature_image)
|
||||||
|
<img src="{{ $postcheck->cabin_temperature_image_url ?? asset('storage/' . ltrim($postcheck->cabin_temperature_image, '/')) }}" alt="Foto Suhu Kabin">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">AC</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($postcheck->ac_image_url || $postcheck->ac_image)
|
||||||
|
<img src="{{ $postcheck->ac_image_url ?? asset('storage/' . ltrim($postcheck->ac_image, '/')) }}" alt="Foto AC">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Blower</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($postcheck->blower_image_url || $postcheck->blower_image)
|
||||||
|
<img src="{{ $postcheck->blower_image_url ?? asset('storage/' . ltrim($postcheck->blower_image, '/')) }}" alt="Foto Blower">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Evaporator</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($postcheck->evaporator_image_url || $postcheck->evaporator_image)
|
||||||
|
<img src="{{ $postcheck->evaporator_image_url ?? asset('storage/' . ltrim($postcheck->evaporator_image, '/')) }}" alt="Foto Evaporator">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($postcheck->postcheck_notes)
|
||||||
|
<div class="notes-section">
|
||||||
|
<div class="info-title" style="border:0; padding:0; margin:0 0 6px 0;">Catatan</div>
|
||||||
|
<div>{{ $postcheck->postcheck_notes }}</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<div class="text-center" style="color:#666; font-size: 11px;">
|
||||||
|
Dicetak pada: {{ now()->format('d F Y H:i:s') }} | Sistem Postcheck PT. Cipta Kreasi Baru
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.ctrlKey && e.key === 'p') {
|
||||||
|
e.preventDefault();
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1552
resources/views/transaction/prechecks/_form.blade.php
Normal file
1552
resources/views/transaction/prechecks/_form.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
5
resources/views/transaction/prechecks/create.blade.php
Normal file
5
resources/views/transaction/prechecks/create.blade.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@extends('layouts.frontapp')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('transaction.prechecks._form')
|
||||||
|
@endsection
|
||||||
5
resources/views/transaction/prechecks/edit.blade.php
Normal file
5
resources/views/transaction/prechecks/edit.blade.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@extends('layouts.frontapp')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('transaction.prechecks._form')
|
||||||
|
@endsection
|
||||||
397
resources/views/transaction/prechecks/print.blade.php
Normal file
397
resources/views/transaction/prechecks/print.blade.php
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Print Precheck - {{ $precheck->spk_number ?? '-' }}</title>
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
@page {
|
||||||
|
margin: 0.5in;
|
||||||
|
size: A4;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-print {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #333;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
border-bottom: 3px solid #2c5282;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-info {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-name {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-tagline {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 250px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px dotted #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4a5568;
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #2d3748;
|
||||||
|
width: 50%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c5282;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th {
|
||||||
|
background-color: #2c5282;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table td {
|
||||||
|
padding: 10px 8px;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-box {
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
background: #f7fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-box img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 240px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-section {
|
||||||
|
background-color: #f7fafc;
|
||||||
|
border-left: 4px solid #2c5282;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signatures {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-box {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-line {
|
||||||
|
margin-top: 60px;
|
||||||
|
border-top: 1px solid #2d3748;
|
||||||
|
padding-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: #2c5282;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button:hover {
|
||||||
|
background-color: #2a4365;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right { text-align: right; }
|
||||||
|
.text-center { text-align: center; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button class="print-button no-print" onclick="window.print()">Print</button>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="company-info">
|
||||||
|
<div class="company-name">PT. CIPTA KREASI BARU</div>
|
||||||
|
<div class="company-tagline">Precheck Kendaraan</div>
|
||||||
|
</div>
|
||||||
|
<div class="document-title">Dokumen Precheck</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-section">
|
||||||
|
<div class="info-box">
|
||||||
|
<div class="info-title">Informasi Transaksi</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">No. Polisi</span>
|
||||||
|
<span class="info-value">{{ $precheck->police_number ?? $precheck->transaction->police_number ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">No. SPK</span>
|
||||||
|
<span class="info-value">{{ $precheck->spk_number ?? $precheck->transaction->spk ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Tanggal Precheck</span>
|
||||||
|
<span class="info-value">{{ optional($precheck->precheck_at)->format('d F Y H:i') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Dibuat oleh</span>
|
||||||
|
<span class="info-value">{{ $precheck->precheckBy->name ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
|
<div class="info-title">Parameter Utama</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Kilometer</span>
|
||||||
|
<span class="info-value">{{ number_format((float)($precheck->kilometer ?? 0), 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Tekanan High</span>
|
||||||
|
<span class="info-value">{{ number_format((float)($precheck->pressure_high ?? 0), 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Tekanan Low</span>
|
||||||
|
<span class="info-value">{{ number_format((float)($precheck->pressure_low ?? 0), 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">Suhu Kabin</span>
|
||||||
|
<span class="info-value">{{ isset($precheck->cabin_temperature) ? number_format((float)$precheck->cabin_temperature, 2) : '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-title">Kondisi Komponen</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 35%;">Komponen</th>
|
||||||
|
<th style="width: 25%;">Kondisi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>AC</td>
|
||||||
|
<td>{{ $precheck->ac_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Blower</td>
|
||||||
|
<td>{{ $precheck->blower_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Evaporator</td>
|
||||||
|
<td>{{ $precheck->evaporator_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Compressor</td>
|
||||||
|
<td>{{ $precheck->compressor_condition ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="section-title">Dokumentasi Foto</div>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Depan</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($precheck->front_image_url || $precheck->front_image)
|
||||||
|
<img src="{{ $precheck->front_image_url ?? asset('storage/' . ltrim($precheck->front_image, '/')) }}" alt="Foto Depan">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Suhu Kabin</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($precheck->cabin_temperature_image_url || $precheck->cabin_temperature_image)
|
||||||
|
<img src="{{ $precheck->cabin_temperature_image_url ?? asset('storage/' . ltrim($precheck->cabin_temperature_image, '/')) }}" alt="Foto Suhu Kabin">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">AC</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($precheck->ac_image_url || $precheck->ac_image)
|
||||||
|
<img src="{{ $precheck->ac_image_url ?? asset('storage/' . ltrim($precheck->ac_image, '/')) }}" alt="Foto AC">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Blower</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($precheck->blower_image_url || $precheck->blower_image)
|
||||||
|
<img src="{{ $precheck->blower_image_url ?? asset('storage/' . ltrim($precheck->blower_image, '/')) }}" alt="Foto Blower">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="info-title" style="margin-bottom:8px;">Evaporator</div>
|
||||||
|
<div class="image-box">
|
||||||
|
@if($precheck->evaporator_image_url || $precheck->evaporator_image)
|
||||||
|
<img src="{{ $precheck->evaporator_image_url ?? asset('storage/' . ltrim($precheck->evaporator_image, '/')) }}" alt="Foto Evaporator">
|
||||||
|
@else
|
||||||
|
<div>Tidak ada gambar</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($precheck->precheck_notes)
|
||||||
|
<div class="notes-section">
|
||||||
|
<div class="info-title" style="border:0; padding:0; margin:0 0 6px 0;">Catatan</div>
|
||||||
|
<div>{{ $precheck->precheck_notes }}</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<div class="text-center" style="color:#666; font-size: 11px;">
|
||||||
|
Dicetak pada: {{ now()->format('d F Y H:i:s') }} | Sistem Precheck PT. Cipta Kreasi Baru
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.ctrlKey && e.key === 'p') {
|
||||||
|
e.preventDefault();
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -176,13 +176,25 @@ Route::group(['middleware' => 'auth'], function() {
|
|||||||
Route::get('/transaction/get-claim-transactions', [TransactionController::class, 'getClaimTransactions'])->name('transaction.get-claim-transactions');
|
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');
|
Route::post('/transaction/claim/{id}', [TransactionController::class, 'claim'])->name('transaction.claim');
|
||||||
|
|
||||||
// Prechecks Routes
|
Route::prefix('transaction/{transaction}')->group(function () {
|
||||||
Route::get('/transaction/prechecks/{transaction}', [PrechecksController::class, 'index'])->name('prechecks.index');
|
// Prechecks
|
||||||
Route::post('/transaction/prechecks/{transaction}', [PrechecksController::class, 'store'])->name('prechecks.store');
|
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 Routes
|
// Postchecks
|
||||||
Route::get('/transaction/postchecks/{transaction}', [PostchecksController::class, 'index'])->name('postchecks.index');
|
Route::prefix('postchecks')->name('postchecks.')->group(function () {
|
||||||
Route::post('/transaction/postchecks/{transaction}', [PostchecksController::class, 'store'])->name('postchecks.store');
|
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
|
// 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_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_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_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 () {
|
Route::prefix('warehouse')->group(function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user