create feature sa create list claim and price to work per dealer

This commit is contained in:
2025-07-07 19:11:04 +07:00
parent fa554446ca
commit 956df5cfe6
16 changed files with 3062 additions and 404 deletions

View File

@@ -52,10 +52,10 @@ class TransactionController extends Controller
// Get KPI data for current user using KPI service
$kpiService = app(\App\Services\KpiService::class);
// Auto-calculate current month KPI achievement to ensure data is up-to-date
$kpiService->calculateKpiAchievement(Auth::user());
// Auto-calculate current month KPI achievement including claimed transactions
$kpiService->calculateKpiAchievementWithClaims(Auth::user());
$kpiSummary = $kpiService->getKpiSummary(Auth::user());
$kpiSummary = $kpiService->getKpiSummaryWithClaims(Auth::user());
// Get current month period name
$currentMonthName = now()->translatedFormat('F Y');
@@ -896,7 +896,7 @@ class TransactionController extends Controller
"warranty" => $request->warranty,
"user_sa_id" => $request->user_sa_id,
"date" => $request->date,
"status" => 'completed', // Mark as completed to trigger stock reduction
"status" => 0, // pending (0) - Mark as pending initially
"created_at" => date('Y-m-d H:i:s'),
"updated_at" => date('Y-m-d H:i:s')
];
@@ -919,6 +919,10 @@ class TransactionController extends Controller
$this->stockService->reduceStockForTransaction($transaction);
}
// Recalculate KPI achievement after creating transactions
$kpiService = app(\App\Services\KpiService::class);
$kpiService->calculateKpiAchievementWithClaims(Auth::user());
DB::commit();
return redirect()->back()->with('success', 'Berhasil input pekerjaan dan stock telah dikurangi otomatis');
@@ -1018,4 +1022,256 @@ class TransactionController extends Controller
], 500);
}
}
/**
* Get claim transactions for DataTable - Only for mechanics
*/
public function getClaimTransactions(Request $request)
{
// Only allow mechanics to access this endpoint
if (Auth::user()->role_id != 3) {
return response()->json([
'draw' => intval($request->input('draw')),
'recordsTotal' => 0,
'recordsFiltered' => 0,
'data' => []
]);
}
$request->validate([
'dealer_id' => 'required|exists:dealers,id'
]);
try {
$query = Transaction::leftJoin('users', 'users.id', '=', 'transactions.user_id')
->leftJoin('users as sa', 'sa.id', '=', 'transactions.user_sa_id')
->leftJoin('works as w', 'w.id', '=', 'transactions.work_id')
->select([
'transactions.id',
'transactions.date',
'transactions.spk',
'transactions.police_number',
'transactions.qty',
'transactions.status',
'transactions.claimed_at',
'transactions.claimed_by',
'w.name as work_name',
'sa.name as sa_name',
'users.name as mechanic_name'
])
->where('transactions.dealer_id', $request->dealer_id)
->where('users.role_id', 4) // Only transactions created by SA
->whereIn('transactions.status', [0, 1]) // Only pending and completed transactions
->orderBy('transactions.date', 'desc');
// Handle DataTables server-side processing
$total = $query->count();
// Search functionality
if ($request->has('search') && !empty($request->search['value'])) {
$searchValue = $request->search['value'];
$query->where(function($q) use ($searchValue) {
$q->where('transactions.spk', 'like', "%{$searchValue}%")
->orWhere('transactions.police_number', 'like', "%{$searchValue}%")
->orWhere('w.name', 'like', "%{$searchValue}%")
->orWhere('sa.name', 'like', "%{$searchValue}%")
->orWhere('users.name', 'like', "%{$searchValue}%");
});
}
$filteredTotal = $query->count();
// Pagination
$start = $request->input('start', 0);
$length = $request->input('length', 15);
$query->skip($start)->take($length);
$transactions = $query->get();
$data = [];
foreach ($transactions as $transaction) {
$data[] = [
'date' => date('d/m/Y', strtotime($transaction->date)),
'spk' => $transaction->spk,
'police_number' => $transaction->police_number,
'work_name' => $transaction->work_name,
'qty' => number_format($transaction->qty),
'sa_name' => $transaction->sa_name,
'status' => $this->getStatusBadge($transaction->status),
'action' => $this->getActionButtons($transaction),
'claimed_at' => $transaction->claimed_at,
'claimed_by' => $transaction->claimed_by
];
}
return response()->json([
'draw' => intval($request->input('draw')),
'recordsTotal' => $total,
'recordsFiltered' => $filteredTotal,
'data' => $data
]);
} catch (Exception $e) {
return response()->json([
'error' => 'Error fetching claim transactions: ' . $e->getMessage()
], 500);
}
}
/**
* Get status badge HTML
*/
private function getStatusBadge($status)
{
switch ($status) {
case 0: // pending
return '<span class="badge badge-warning">Menunggu</span>';
case 1: // completed
return '<span class="badge badge-success">Selesai</span>';
case 2: // in_progress
return '<span class="badge badge-primary">Sedang Dikerjakan</span>';
case 3: // claimed
return '<span class="badge badge-info">Diklaim</span>';
case 4: // cancelled
return '<span class="badge badge-danger">Dibatalkan</span>';
default:
return '<span class="badge badge-secondary">Tidak Diketahui</span>';
}
}
/**
* Claim a transaction - Only for mechanics
*/
public function claim($id)
{
// Only allow mechanics to claim transactions
if (Auth::user()->role_id != 3) {
return response()->json([
'status' => 403,
'message' => 'Hanya mekanik yang dapat mengklaim pekerjaan'
], 403);
}
try {
$transaction = Transaction::find($id);
if (!$transaction) {
return response()->json([
'status' => 404,
'message' => 'Transaksi tidak ditemukan'
], 404);
}
// Check if transaction belongs to current user's dealer
if ($transaction->dealer_id !== Auth::user()->dealer_id) {
return response()->json([
'status' => 403,
'message' => 'Anda tidak memiliki akses ke transaksi ini'
], 403);
}
// Check if transaction can be claimed (pending or completed)
if (!in_array($transaction->status, [0, 1])) { // pending (0) and completed (1)
return response()->json([
'status' => 400,
'message' => 'Hanya transaksi yang menunggu atau sudah selesai yang dapat diklaim'
], 400);
}
// Check if transaction is already claimed
if (!empty($transaction->claimed_at) || !empty($transaction->claimed_by)) {
return response()->json([
'status' => 400,
'message' => 'Transaksi ini sudah diklaim sebelumnya'
], 400);
}
// Check if transaction was created by SA (role_id = 4)
$creator = User::find($transaction->user_id);
if (!$creator || $creator->role_id != 4) {
return response()->json([
'status' => 400,
'message' => 'Hanya transaksi yang dibuat oleh Service Advisor yang dapat diklaim'
], 400);
}
// Update transaction with claim information
$transaction->update([
'claimed_at' => now(),
'claimed_by' => Auth::user()->id
]);
// Recalculate KPI achievement after claiming
$kpiService = app(\App\Services\KpiService::class);
$kpiService->calculateKpiAchievementWithClaims(Auth::user());
return response()->json([
'status' => 200,
'message' => 'Pekerjaan berhasil diklaim'
]);
} catch (Exception $e) {
return response()->json([
'status' => 500,
'message' => 'Gagal mengklaim pekerjaan: ' . $e->getMessage()
], 500);
}
}
/**
* Get action buttons HTML for claim transactions - Only for mechanics
*/
private function getActionButtons($transaction)
{
$buttons = '';
// Only show buttons for mechanics
if (Auth::user()->role_id == 3) {
// Claim button - show only if not claimed yet
if (empty($transaction->claimed_at) && empty($transaction->claimed_by)) {
$buttons .= '<button class="btn btn-sm btn-success mr-1" onclick="claimTransaction(' . $transaction->id . ')" title="Klaim Pekerjaan">';
$buttons .= 'Klaim';
$buttons .= '</button>';
} else {
$buttons .= '<span class="badge badge-info">Sudah Diklaim</span>';
}
}
return $buttons;
}
/**
* Get KPI data for AJAX refresh
*/
public function getKpiData()
{
try {
$kpiService = app(\App\Services\KpiService::class);
$kpiSummary = $kpiService->getKpiSummaryWithClaims(Auth::user());
$currentMonthName = now()->translatedFormat('F Y');
$kpiData = [
'target' => $kpiSummary['current_target'] ? $kpiSummary['current_target']->target_value : 0,
'actual' => $kpiSummary['current_achievement'] ? $kpiSummary['current_achievement']->actual_value : 0,
'percentage' => $kpiSummary['current_percentage'],
'status' => $kpiSummary['current_achievement'] ? $kpiSummary['current_achievement']->status : 'pending',
'status_color' => $kpiSummary['current_achievement'] ? $kpiSummary['current_achievement']->status_color : 'secondary',
'period' => $currentMonthName,
'has_target' => $kpiSummary['current_target'] ? true : false
];
return response()->json([
'success' => true,
'data' => $kpiData
]);
} catch (Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error fetching KPI data: ' . $e->getMessage()
], 500);
}
}
}