406 lines
16 KiB
PHP
406 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Penugasan;
|
|
use App\Models\Teknisi;
|
|
use App\Models\TarifPekerjaan;
|
|
use App\Models\TimTeknisiPenugasan;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Carbon\Carbon;
|
|
|
|
class PenugasanController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
$query = Penugasan::with(['teknisi', 'tarif', 'timTeknisi.teknisi']);
|
|
|
|
if ($request->filled('status')) {
|
|
if ($request->status === 'garansi_aktif') {
|
|
$query->garansiAktif();
|
|
} else {
|
|
$query->where('status_pekerjaan', $request->status);
|
|
}
|
|
}
|
|
|
|
if ($request->filled('jenis_pekerjaan')) {
|
|
$query->where('jenis_pekerjaan', $request->jenis_pekerjaan);
|
|
}
|
|
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where(function($q) use ($search) {
|
|
$q->where('catatan_admin', 'LIKE', "%{$search}%")
|
|
->orWhere('detail_pekerjaan', 'LIKE', "%{$search}%")
|
|
->orWhereHas('teknisi', function($tq) use ($search) {
|
|
$tq->where('nama', 'LIKE', "%{$search}%");
|
|
});
|
|
});
|
|
}
|
|
|
|
if ($request->filled('id_teknisi')) {
|
|
$teknisiId = $request->id_teknisi;
|
|
$query->where(function($q) use ($teknisiId) {
|
|
$q->where('id_teknisi', $teknisiId)
|
|
->orWhereHas('timTeknisi', function($tq) use ($teknisiId) {
|
|
$tq->where('id_teknisi', $teknisiId);
|
|
});
|
|
});
|
|
}
|
|
|
|
if ($request->filled('start_date') && $request->filled('end_date')) {
|
|
$query->whereBetween('tanggal_diberikan', [$request->start_date, $request->end_date]);
|
|
} elseif ($request->filled('start_date')) {
|
|
$query->where('tanggal_diberikan', '>=', $request->start_date);
|
|
} elseif ($request->filled('end_date')) {
|
|
$query->where('tanggal_diberikan', '<=', $request->end_date);
|
|
}
|
|
|
|
$penugasan = $query->orderBy('created_at', 'desc')->paginate(10);
|
|
|
|
$totalPenugasan = Penugasan::count();
|
|
$belumMulai = Penugasan::where('status_pekerjaan', 'belum_mulai')->count();
|
|
$dalamProses = Penugasan::where('status_pekerjaan', 'dalam_proses')->count();
|
|
$selesai = Penugasan::where('status_pekerjaan', 'selesai')->count();
|
|
$dibatalkan = Penugasan::where('status_pekerjaan', 'dibatalkan')->count();
|
|
$garansiAktif = Penugasan::garansiAktif()->count();
|
|
|
|
$today = Carbon::today()->toDateString();
|
|
$absentTechIds = \App\Models\Absensi::where('tanggal', $today)
|
|
->whereIn('status', ['izin', 'sakit'])
|
|
->pluck('id_teknisi')
|
|
->toArray();
|
|
|
|
$abandonedTasks = collect();
|
|
if (!empty($absentTechIds)) {
|
|
$abandonedTasks = Penugasan::whereIn('status_pekerjaan', ['belum_mulai', 'dalam_proses'])
|
|
->where(function($q) use ($absentTechIds) {
|
|
$q->whereIn('id_teknisi', $absentTechIds)
|
|
->orWhereHas('timTeknisi', function($tq) use ($absentTechIds) {
|
|
$tq->whereIn('id_teknisi', $absentTechIds);
|
|
});
|
|
})
|
|
->with('teknisi')
|
|
->get();
|
|
}
|
|
|
|
$teknisis = Teknisi::where('status', 'aktif')->orderBy('nama')->get();
|
|
$teknisiList = $teknisis; // Use same data for the new dropdown
|
|
|
|
return view('Admin.KelolaPekerjaan.Penugasan', compact(
|
|
'penugasan', 'totalPenugasan', 'belumMulai', 'dalamProses',
|
|
'selesai', 'dibatalkan', 'garansiAktif', 'teknisis', 'teknisiList', 'abandonedTasks'
|
|
));
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
try {
|
|
$validated = $request->validate([
|
|
'id_teknisi' => 'required|array',
|
|
'id_teknisi.*' => 'exists:teknisis,id_teknisi',
|
|
'tanggal_diberikan' => 'required|date',
|
|
'foto_surat' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:5120',
|
|
'catatan_admin' => 'nullable|string',
|
|
'jenis_pekerjaan' => 'required|string',
|
|
'alamat_lokasi' => 'required|string',
|
|
'nama_pelanggan' => 'nullable|string',
|
|
'no_sambungan' => 'nullable|string',
|
|
]);
|
|
|
|
DB::beginTransaction();
|
|
|
|
$path = null;
|
|
if ($request->hasFile('foto_surat')) {
|
|
$path = $request->file('foto_surat')->store('penugasan/surat', 'public');
|
|
}
|
|
|
|
$penugasan = Penugasan::create([
|
|
'id_teknisi' => $validated['id_teknisi'][0],
|
|
'foto_surat' => $path,
|
|
'tanggal_diberikan' => $validated['tanggal_diberikan'],
|
|
'catatan_admin' => $validated['catatan_admin'] ?? null,
|
|
'jenis_pekerjaan' => $validated['jenis_pekerjaan'],
|
|
'alamat_lokasi' => $validated['alamat_lokasi'],
|
|
'nama_pelanggan' => $validated['nama_pelanggan'] ?? null,
|
|
'no_sambungan' => $validated['no_sambungan'] ?? null,
|
|
'status_pekerjaan' => 'belum_mulai',
|
|
]);
|
|
|
|
$techIds = array_unique($validated['id_teknisi']);
|
|
foreach ($techIds as $techId) {
|
|
TimTeknisiPenugasan::create([
|
|
'id_penugasan' => $penugasan->id_penugasan,
|
|
'id_teknisi' => $techId,
|
|
'status_kehadiran' => 'hadir',
|
|
]);
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('pekerjaan.penugasan.index')
|
|
->with('success', 'Penugasan berhasil dibuat! Teknisi akan melengkapi detail via mobile.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->back()
|
|
->with('error', 'Gagal menambahkan data: ' . $e->getMessage())
|
|
->withInput();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ✅ FIX: Tambah field garansi lengkap di response show()
|
|
* - is_garansi_aktif → true/false apakah garansi masih aktif
|
|
* - sisa_hari_garansi → berapa hari tersisa
|
|
* - tanggal_garansi_mulai → sudah ikut dari toArray()
|
|
* - tanggal_garansi_selesai → sudah ikut dari toArray()
|
|
*
|
|
* Sebelumnya field-field ini tidak di-append sehingga
|
|
* badge garansi di modal detail tidak pernah muncul
|
|
* meski datanya sudah ada di database
|
|
*/
|
|
public function show($id)
|
|
{
|
|
try {
|
|
$penugasan = Penugasan::with(['teknisi', 'tarif', 'timTeknisi.teknisi', 'items.tarif'])
|
|
->findOrFail($id);
|
|
|
|
$data = $penugasan->toArray();
|
|
|
|
$data['foto_surat_url'] = $penugasan->foto_surat_url;
|
|
$data['foto_sebelum_url'] = $penugasan->foto_sebelum_url;
|
|
$data['foto_sesudah_url'] = $penugasan->foto_sesudah_url;
|
|
$data['label_jenis_pekerjaan'] = $penugasan->label_jenis_pekerjaan;
|
|
|
|
// ✅ FIX: Info garansi — dibutuhkan oleh blade untuk tampilkan badge
|
|
$data['is_garansi_aktif'] = $penugasan->isGaransiAktif();
|
|
$data['sisa_hari_garansi'] = $penugasan->getSisaHariGaransi();
|
|
|
|
return response()->json(['success' => true, 'data' => $data]);
|
|
|
|
} catch (\Exception $e) {
|
|
return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
|
|
}
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
try {
|
|
$penugasan = Penugasan::with(['teknisi', 'timTeknisi.teknisi'])->findOrFail($id);
|
|
|
|
$data = $penugasan->toArray();
|
|
$data['foto_surat_url'] = $penugasan->foto_surat_url;
|
|
|
|
return response()->json(['success' => true, 'data' => $data]);
|
|
|
|
} catch (\Exception $e) {
|
|
return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
|
|
}
|
|
}
|
|
|
|
public function update(Request $request, $id)
|
|
{
|
|
try {
|
|
$penugasan = Penugasan::findOrFail($id);
|
|
|
|
$validated = $request->validate([
|
|
'id_teknisi' => 'required|array',
|
|
'id_teknisi.*' => 'exists:teknisis,id_teknisi',
|
|
'tanggal_diberikan' => 'required|date',
|
|
'foto_surat' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:5120',
|
|
'catatan_admin' => 'nullable|string',
|
|
'jenis_pekerjaan' => 'required|string',
|
|
'alamat_lokasi' => 'required|string',
|
|
'nama_pelanggan' => 'nullable|string',
|
|
'no_sambungan' => 'nullable|string',
|
|
]);
|
|
|
|
DB::beginTransaction();
|
|
|
|
$updateData = [
|
|
'id_teknisi' => $validated['id_teknisi'][0],
|
|
'tanggal_diberikan' => $validated['tanggal_diberikan'],
|
|
'catatan_admin' => $validated['catatan_admin'] ?? null,
|
|
'jenis_pekerjaan' => $validated['jenis_pekerjaan'],
|
|
'alamat_lokasi' => $validated['alamat_lokasi'],
|
|
'nama_pelanggan' => $validated['nama_pelanggan'] ?? null,
|
|
'no_sambungan' => $validated['no_sambungan'] ?? null,
|
|
];
|
|
|
|
if ($request->hasFile('foto_surat')) {
|
|
if ($penugasan->foto_surat) {
|
|
Storage::disk('public')->delete($penugasan->foto_surat);
|
|
}
|
|
$updateData['foto_surat'] = $request->file('foto_surat')->store('penugasan/surat', 'public');
|
|
}
|
|
|
|
$penugasan->update($updateData);
|
|
|
|
TimTeknisiPenugasan::where('id_penugasan', $penugasan->id_penugasan)->delete();
|
|
|
|
$techIds = array_unique($validated['id_teknisi']);
|
|
foreach ($techIds as $techId) {
|
|
TimTeknisiPenugasan::create([
|
|
'id_penugasan' => $penugasan->id_penugasan,
|
|
'id_teknisi' => $techId,
|
|
'status_kehadiran' => 'hadir',
|
|
]);
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return response()->json(['success' => true, 'message' => 'Data berhasil diupdate!']);
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json(['success' => false, 'message' => 'Gagal update: ' . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function destroy($id)
|
|
{
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
$penugasan = Penugasan::findOrFail($id);
|
|
|
|
if ($penugasan->foto_surat) {
|
|
Storage::disk('public')->delete($penugasan->foto_surat);
|
|
}
|
|
|
|
$penugasan->delete();
|
|
|
|
DB::commit();
|
|
|
|
return response()->json(['success' => true, 'message' => 'Data berhasil dihapus!']);
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return response()->json(['success' => false, 'message' => 'Gagal menghapus: ' . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
public function getTarifByJenis(Request $request)
|
|
{
|
|
try {
|
|
$tarifs = TarifPekerjaan::where('jenis_pekerjaan', $request->jenis_pekerjaan)
|
|
->where('is_active', true)
|
|
->get();
|
|
|
|
return response()->json(['success' => true, 'data' => $tarifs]);
|
|
|
|
} catch (\Exception $e) {
|
|
return response()->json(['success' => false, 'message' => 'Gagal mengambil tarif'], 500);
|
|
}
|
|
}
|
|
|
|
public function getTeknisiByDate(Request $request)
|
|
{
|
|
try {
|
|
// Selalu tampilkan seluruh teknisi aktif agar fleksibel secara operasional
|
|
$teknisis = Teknisi::where('status', 'aktif')
|
|
->orderBy('nama')
|
|
->get();
|
|
|
|
return response()->json(['success' => true, 'data' => $teknisis]);
|
|
} catch (\Exception $e) {
|
|
return response()->json(['success' => false, 'message' => 'Gagal mengambil data teknisi: ' . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* ✅ FIX: Filter tanggal monitoring pakai tanggal_diselesaikan
|
|
* bukan updated_at agar hasil filter akurat
|
|
*/
|
|
public function monitoring(Request $request)
|
|
{
|
|
$query = Penugasan::with(['teknisi', 'timTeknisi.teknisi']);
|
|
|
|
if ($request->filled('status')) {
|
|
$query->where('status_pekerjaan', $request->status);
|
|
}
|
|
|
|
if ($request->filled('teknisi_id')) {
|
|
$teknisiId = $request->teknisi_id;
|
|
$query->where(function($q) use ($teknisiId) {
|
|
$q->where('id_teknisi', $teknisiId)
|
|
->orWhereHas('timTeknisi', function($tq) use ($teknisiId) {
|
|
$tq->where('id_teknisi', $teknisiId);
|
|
});
|
|
});
|
|
}
|
|
|
|
if ($request->filled('start_date') && $request->filled('end_date')) {
|
|
$start = $request->start_date . ' 00:00:00';
|
|
$end = $request->end_date . ' 23:59:59';
|
|
$query->where(function($q) use ($start, $end) {
|
|
$q->where(function($q2) use ($start, $end) {
|
|
$q2->whereNotNull('tanggal_diselesaikan')
|
|
->whereBetween('tanggal_diselesaikan', [$start, $end]);
|
|
})->orWhere(function($q2) use ($start, $end) {
|
|
$q2->whereNull('tanggal_diselesaikan')
|
|
->whereBetween('updated_at', [$start, $end]);
|
|
});
|
|
});
|
|
} elseif ($request->filled('start_date')) {
|
|
$start = $request->start_date . ' 00:00:00';
|
|
$query->where(function($q) use ($start) {
|
|
$q->where(function($q2) use ($start) {
|
|
$q2->whereNotNull('tanggal_diselesaikan')
|
|
->where('tanggal_diselesaikan', '>=', $start);
|
|
})->orWhere(function($q2) use ($start) {
|
|
$q2->whereNull('tanggal_diselesaikan')
|
|
->where('updated_at', '>=', $start);
|
|
});
|
|
});
|
|
} elseif ($request->filled('end_date')) {
|
|
$end = $request->end_date . ' 23:59:59';
|
|
$query->where(function($q) use ($end) {
|
|
$q->where(function($q2) use ($end) {
|
|
$q2->whereNotNull('tanggal_diselesaikan')
|
|
->where('tanggal_diselesaikan', '<=', $end);
|
|
})->orWhere(function($q2) use ($end) {
|
|
$q2->whereNull('tanggal_diselesaikan')
|
|
->where('updated_at', '<=', $end);
|
|
});
|
|
});
|
|
}
|
|
|
|
$progresKerja = $query->orderBy('updated_at', 'desc')->paginate(12);
|
|
|
|
$progresKerja->getCollection()->transform(function ($item) {
|
|
$item->persentase_pekerjaan = match($item->status_pekerjaan) {
|
|
'selesai' => 100,
|
|
'dalam_proses' => 50,
|
|
default => 0,
|
|
};
|
|
$item->is_garansi_aktif = $item->isGaransiAktif();
|
|
$item->sisa_hari_garansi = $item->getSisaHariGaransi();
|
|
return $item;
|
|
});
|
|
|
|
$statistics = [
|
|
'total' => Penugasan::count(),
|
|
'dalam_progres' => Penugasan::where('status_pekerjaan', 'dalam_proses')->count(),
|
|
'selesai' => Penugasan::where('status_pekerjaan', 'selesai')->count(),
|
|
'belum_mulai' => Penugasan::where('status_pekerjaan', 'belum_mulai')->count(),
|
|
'garansi_aktif' => Penugasan::garansiAktif()->count(),
|
|
];
|
|
|
|
$statusList = [
|
|
'belum_mulai' => 'Belum Mulai',
|
|
'dalam_proses' => 'Dalam Proses',
|
|
'selesai' => 'Selesai',
|
|
];
|
|
|
|
$teknisiList = Teknisi::orderBy('nama')->get();
|
|
|
|
return view('Admin.KelolaPekerjaan.ProgresKerja', compact(
|
|
'progresKerja', 'statistics', 'statusList', 'teknisiList'
|
|
));
|
|
}
|
|
} |