From 8717310ebb9501ec02fd763aa1ff222ae8d62359 Mon Sep 17 00:00:00 2001 From: mikael-zakaria Date: Wed, 4 Mar 2026 23:34:45 +0700 Subject: [PATCH] Update Fitur CRUD Sertifikat Maintenance --- app/Config/Filters.php | 2 +- app/Config/Routes.php | 30 +- app/Controllers/Activities.php | 29 +- app/Controllers/Certificates.php | 754 ++++++++++++------ app/Models/CertificateModel.php | 76 ++ app/Views/activities_editor.php | 282 +++++++ app/Views/certificate_calibrate_index.php | 382 +++++++++ app/Views/certificate_installation_index.php | 381 +++++++++ app/Views/certificate_maintenance_index.php | 409 ++++++---- app/Views/certificate_training_index.php | 697 ++++++++-------- .../certificates/certificate_installation.php | 183 +++++ .../certificates/certificate_maintenance.php | 38 +- .../certificates/certificate_training.php | 10 +- 13 files changed, 2501 insertions(+), 772 deletions(-) create mode 100644 app/Models/CertificateModel.php create mode 100644 app/Views/certificate_calibrate_index.php create mode 100644 app/Views/certificate_installation_index.php create mode 100644 app/Views/certificates/certificate_installation.php diff --git a/app/Config/Filters.php b/app/Config/Filters.php index fb757f0..74e7326 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -76,7 +76,7 @@ class Filters extends BaseFilters 'before' => [ // 'cors', 'auth' => [ 'except' => [ - 'auth/*', 'lqms/*', 'key/*', 'api/*' + 'auth/*', 'lqms/*', 'key/*', 'api/*', 'certificates/number/*' ]] // 'honeypot', // 'csrf', diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 24bfcbb..1130749 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -211,24 +211,30 @@ $routes->group('certificates', function($routes) { // Untuk Index Tiap Menu $routes->get('maintenance', 'Certificates::maintenanceIndex'); // OK - $routes->get('installation', 'Certificates::installationIndex'); - $routes->get('training', 'Certificates::trainingIndex'); - $routes->get('calibration', 'Certificates::calibrateIndex'); - + // $routes->get('installation', 'Certificates::installationIndex'); // OK + // $routes->get('training', 'Certificates::trainingIndex'); // OK + // $routes->get('calibration', 'Certificates::calibrateIndex'); // OK + // Untuk Get API $routes->get('api/getindexmaintenance', 'Certificates::getDataIndexMaintenance'); // OK - $routes->get('api/getindextraining', 'Certificates::getDataIndexTraining'); + // $routes->get('api/getindexinstallation', 'Certificates::getDataIndexInstallation'); // OK + // $routes->get('api/getindextraining', 'Certificates::getDataIndexTraining'); // OK + // $routes->get('api/getindexcalibrate', 'Certificates::getDataIndexCalibrate'); + $routes->post('api/showmaintenance', 'Certificates::showDataMaintenance'); // OK - $routes->post('api/validateCertificate', 'Certificates::validateCertificate'); // OK - $routes->post('api/generatepdf', 'Certificates::generatePdf'); // OK - - - // Untuk View Cerificate + // Untuk Preview Cerificate $routes->get('maintenance/show/(:any)', 'Certificates::createMaintenancePreview/$1'); // OK - $routes->get('training/show/(:any)', 'Certificates::createTraining/$1'); - $routes->get('calibration/show/(:any)/(:any)', 'Certificates::createCalibrate/$1/$2'); + // $routes->get('installation/show/(:any)', 'Certificates::createInstallationPreview/$1'); // OK + // $routes->get('training/show/(:any)', 'Certificates::createTrainingPreview/$1'); // OK + // $routes->get('calibration/show/(:any)/(:any)', 'Certificates::createCalibratePreview/$1/$2'); + $routes->post('api/validatecertificate', 'Certificates::validateCertificate'); // OK + + $routes->get('maintenance/number/$1', 'Certificates::getMaintenanceCertificate'); // OK + + $routes->get('number/(:segment)', 'Certificates::view/$1'); + }); diff --git a/app/Controllers/Activities.php b/app/Controllers/Activities.php index d1260f7..9ffc600 100644 --- a/app/Controllers/Activities.php +++ b/app/Controllers/Activities.php @@ -2,6 +2,7 @@ namespace App\Controllers; +use App\Models\CertificateModel; use App\Models\ActivitiesModel; use App\Models\ActdetailModel; use App\Models\InvTransModel; @@ -441,7 +442,6 @@ class Activities extends Controller { $sql = "INSERT INTO actstatus_log (activityid, activitystatus, userid, logdate) VALUES ($actid,'$actstatus', $userid, NOW())"; $query = $db->query($sql); - $acttextid = $data['new_value']['acttextid']; $textvalue = $data['new_value']['textvalue']; foreach($data['new_value']['acttextid'] as $qindex => $qacttextid) { @@ -449,6 +449,33 @@ class Activities extends Controller { $sql = "insert into actdetail (actid, acttextid, textvalue, createdate) values ('$actid','$qacttextid', '$qtextvalue', NOW())"; $query = $db->query($sql); } + + // UNTUK CERTIFICATES + $sql = "SELECT prl.productaliastext as productname, pr.productnumber as snnumber, st.sitename + FROM `activities` act + LEFT JOIN sites st ON st.siteid = act.siteid + LEFT JOIN products pr ON pr.productid = act.productid + LEFT JOIN productcatalog prc ON prc.catalogid = pr.catalogid + LEFT JOIN productalias prl ON prl.productaliasid = prc.productaliasid + WHERE act.actid = $actid"; + $query = $db->query($sql); + $result = $query->getRowArray(); + if ($this->request->getVar('maintenance')) { // Maintenance + $issuedDate = $data['new_value']['closedate'] ?? null; + $expiredDate = date('Y-m-d', strtotime($issuedDate . ' + 6 months')); + $insertCert = [ + 'cert_name' => "MC_" . ($result['productname'] ?? 'UNKNOWN') . "_" . ($result['snnumber'] ?? '0') . "_" . $actid, + 'cert_type' => "MC", + 'actid' => $actid, + 'issued_date' => $issuedDate, + 'expired_date' => $expiredDate, + 'user_id' => $data['new_value']['userid_owner'] + ]; + $certificateModel = new CertificateModel(); + $certificateModel->insert($insertCert); + } + + } else { // update edit $activitiesModel = new ActivitiesModel(); diff --git a/app/Controllers/Certificates.php b/app/Controllers/Certificates.php index 89c2a29..6945779 100644 --- a/app/Controllers/Certificates.php +++ b/app/Controllers/Certificates.php @@ -6,6 +6,8 @@ use App\Controllers\BaseController; use Dompdf\Dompdf; use Dompdf\Options; +use App\Models\CertificateModel; + class Certificates extends BaseController { protected array $data; @@ -16,17 +18,6 @@ class Certificates extends BaseController { } public function getDataIndexInstallation() { // Untuk API Get Data // $actid = $this->request->getVar('actid'); Siapa Tahu Buat - } - public function createinstallationPreview($certid = null) { // Untuk Preview Sertifikat - //Melakukan search data dari database - } - - // Untuk Sertifikat Maintenance [2] - public function maintenanceIndex() { // Index - return view('certificate_maintenance_index'); - } - public function getDataIndexMaintenance() { // Untuk API Get Data - // $actid = $this->request->getVar('actid'); Siapa Tahu Buat $certificates = [ [ @@ -45,10 +36,30 @@ class Certificates extends BaseController { 'productname' => 'GE Healthcare VIVID', 'productnumber' => 'GE-VIV-Q992', 'issuedate' => '2024-06-12', - 'expirydate' => '2026-10-01', + 'expirydate' => '2026-03-01', 'vendor' => 'Pramita Medika Service', 'isval' => '2026-03-01' - ] + ], + [ + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11915', + 'certname' => 'Electrical Safety Test', + 'productname' => 'GE Healthcare VIVID', + 'productnumber' => 'GE-VIV-Q992', + 'issuedate' => '2024-06-12', + 'expirydate' => '2026-10-01', + 'vendor' => 'Pramita Medika Service', + 'isval' => '2026-09-01' + ], + [ + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11915', + 'certname' => 'Electrical Safety Test', + 'productname' => 'GE Healthcare VIVID', + 'productnumber' => 'GE-VIV-Q992', + 'issuedate' => '2024-06-12', + 'expirydate' => '2026-01-01', + 'vendor' => 'Pramita Medika Service', + 'isval' => '2026-09-01' + ], ]; // If no actid, return all certificates @@ -58,7 +69,7 @@ class Certificates extends BaseController { return $this->response->setJSON($certificates); } - public function createMaintenancePreview($certid = null) { // Untuk Preview Sertifikat + public function createinstallationPreview($certid = null) { // Untuk Preview Sertifikat //Melakukan search data dari database if (!$certid) { @@ -81,9 +92,167 @@ class Certificates extends BaseController { return $this->response->setStatusCode(404)->setJSON(['error' => 'Maintenance certificate not found']); } + return $this->previewPdf($certificate, 'installation'); // Preview PDF + } + + + // Untuk Sertifikat Maintenance [2] + public function maintenanceIndex() { // Index + return view('certificate_maintenance_index'); + } + public function getDataIndexMaintenance() { + $userPosId = session()->get('userposid'); + $userId = session()->get('userid'); + + $certificateModel = new CertificateModel(); + + // 1. Mulai Query Builder + $builder = $certificateModel->select(' + certificates.cert_id, + certificates.cert_number, + certificates.cert_name, + certificates.cert_type, + certificates.actid, + certificates.issued_date, + certificates.expired_date, + certificates.status, + certificates.user_validation_at, + certificates.spv_validation_at, + certificates.manager_validation_at, + activities.subject as activity_subject, + CONCAT(users.firstname, " ", users.lastname) as fullname + ') + ->join('activities', 'activities.actid = certificates.actid', 'left') + ->join('users', 'users.userid = certificates.user_id', 'left'); + + // 2. Filter berdasarkan Role + if (in_array($userPosId, [1, 3, 5])) { // Manager & IT: Tidak perlu filter tambahan (lihat semua) + } else if ($userPosId == 2) { // SPV: Melihat data user yang "reportto"-nya adalah ID supervisor ini + $builder->where('users.reportto', $userId); + } else if ($userPosId == 4) { // TSOIVD: Hanya melihat data milik sendiri + $builder->where('certificates.user_id', $userId); + } else {// Role lain: Tidak diberi akses + return $this->response->setJSON([]); + } + + // 3. Eksekusi Query + $allData = $builder->findAll(); + + if (empty($allData)) { + return $this->response->setJSON([]); // Kembalikan array kosong agar frontend tidak error + } + + return $this->response->setJSON($allData); + } + public function showDataMaintenance() { // Untuk API Get Data + $certid = $this->request->getPost('certid'); + if (!$certid) { + return $this->response->setStatusCode(400)->setJSON(['error' => 'Certificate ID Not Found']); + } + $certificateModel = new CertificateModel(); + $data = $certificateModel->select(' + certificates.cert_id, + certificates.cert_number, + certificates.cert_name, + certificates.status, + activities.actid, + activities.subject, + productcatalog.productname, + sites.sitename, + products.productnumber, + CASE + WHEN certificates.cert_type = "MC" THEN "Maintenance" + WHEN certificates.cert_type = "IC" THEN "Installation" + WHEN certificates.cert_type = "UTC" THEN "User Training" + WHEN certificates.cert_type = "BAI" THEN "Before After Inspection" + WHEN certificates.cert_type = "BAP" THEN "Berita Acara Pengerjaan" + ELSE certificates.cert_type + END AS certtype, + CONCAT(us.firstname, " ", us.lastname) AS username, + certificates.user_validation_at, + CONCAT(spv.firstname, " ", spv.lastname) AS spvname, + certificates.spv_validation_at, + CONCAT(mgr.firstname, " ", mgr.lastname) AS managername, + certificates.manager_validation_at, + certificates.issued_date, + certificates.expired_date + ', false) + ->join('activities', 'activities.actid = certificates.actid', 'inner') + ->join('products', 'products.productid = activities.productid', 'inner') + ->join('productcatalog', 'productcatalog.catalogid = products.catalogid', 'inner') + ->join('sites', 'sites.siteid = activities.siteid', 'left') + ->join('users as us', 'us.userid = certificates.user_id', 'left') + ->join('users as spv', 'spv.userid = certificates.spv_id', 'left') + ->join('users as mgr', 'mgr.userid = certificates.manager_id', 'left') + ->join('userposition', 'userposition.userposid = us.userposid', 'left') + ->where('certificates.cert_id', $certid) + ->first(); + + $data['issued_date'] = $data['issued_date'] ? date('d M Y', strtotime($data['issued_date'])) : null; + $data['user_validation_at'] = $data['user_validation_at'] ? date('d M Y H:i', strtotime($data['user_validation_at'])) : null; + $data['spv_validation_at'] = $data['spv_validation_at'] ? date('d M Y H:i', strtotime($data['spv_validation_at'])) : null; + $data['manager_validation_at'] = $data['manager_validation_at'] ? date('d M Y H:i', strtotime($data['manager_validation_at'])) : null; + + if (empty($data)) { // Jika Tidak Ada + return $this->response->setStatusCode(404)->setJSON(['error' => 'Maintenance certificate not found']); + } + return $this->response->setJSON($data); + } + public function createMaintenancePreview($certid = null) { // Untuk Preview Sertifikat + + if (!$certid) { + return $this->response->setStatusCode(400)->setJSON(['error' => 'Certificate ID Not Found']); + } + $certificateModel = new CertificateModel(); + $data = $certificateModel->select(' + certificates.cert_name, + certificates.issued_date, + certificates.expired_date, + productcatalog.productname as productname, + sites.sitename as sitename, + products.productnumber, + CASE + WHEN certificates.cert_type = "MC" THEN "Maintenance" + WHEN certificates.cert_type = "IC" THEN "Installation" + WHEN certificates.cert_type = "UTC" THEN "User Training" + WHEN certificates.cert_type = "BAI" THEN "Before After Inspection" + WHEN certificates.cert_type = "BAP" THEN "Berita Acara Pengerjaan" + ELSE certificates.cert_type + END AS cert_type, + CONCAT(users.firstname, " ", users.lastname) AS fullname, + userposition.texts AS user_position, + certificates.issued_date, + certificates.expired_date, + certificates.cert_number -- Penting agar callback UUID tetap jalan + ', false) + ->join('users', 'users.userid = certificates.user_id', 'left') + ->join('userposition', 'userposition.userposid = users.userposid', 'left') + ->join('activities', 'activities.actid = certificates.actid', 'left') + ->join('sites', 'sites.siteid = activities.siteid', 'left') + ->join('products', 'products.productid = activities.productid', 'left') + ->join('productcatalog', 'productcatalog.catalogid = products.catalogid', 'left') + ->where('certificates.cert_id', $certid) + ->first(); + $certificate = [ + 'certname' => $data['cert_name'], + 'sitename' => $data['sitename'], + 'certtype' => $data['cert_type'], + 'fullname' => $data['fullname'], + 'userposition' => $data['user_position'], + 'productname' => $data['productname'], + 'productnumber' => $data['productnumber'], + 'issueddate' => $data['issued_date'], + 'expireddate' => $data['expired_date'] + ]; + + if (empty($certificate)) { // Jika Tidak Ada + return $this->response->setStatusCode(404)->setJSON(['error' => 'Maintenance certificate not found']); + } + return $this->previewPdf($certificate, 'maintenance'); // Preview PDF } + // Untuk Sertifikat Training [3] public function trainingIndex() { return view('certificate_training_index'); @@ -94,40 +263,24 @@ class Certificates extends BaseController { // Sample data - replace with actual database query $certificates = [ [ - 'certid' => 'CERT001', + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11919', 'certname' => 'Jokoh Calibration Certificate', 'productname' => 'Jokoh', 'productnumber' => 'SN-2024-001', 'issuedate' => '2024-01-15', 'expirydate' => '2025-01-15', 'vendor' => 'Summit Calibration Lab', - 'type' => 'joko', - 'sitename' => 'Jakarta Office', - 'siteid' => 'SITE001' + 'isval' => null ], [ - 'certid' => 'CERT002', - 'certname' => 'TMS50i Calibration Certificate', - 'productname' => 'TMS50i', - 'productnumber' => 'SN-2024-002', - 'issuedate' => '2024-02-15', - 'expirydate' => '2025-02-15', - 'vendor' => 'Summit Calibration Lab', - 'type' => 'tms', - 'sitename' => 'Surabaya Office', - 'siteid' => 'SITE002' - ], - [ - 'certid' => 'CERT003', - 'certname' => 'Tokyo Boeki Calibration Certificate', - 'productname' => 'Tokyo Boeki', - 'productnumber' => 'SN-2024-003', - 'issuedate' => '2024-03-15', - 'expirydate' => '2025-03-15', - 'vendor' => 'Summit Calibration Lab', - 'type' => 'boeki', - 'sitename' => 'Bandung Office', - 'siteid' => 'SITE003' + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11919', + 'certname' => 'Electrical Safety Test', + 'productname' => 'GE Healthcare VIVID', + 'productnumber' => 'GE-VIV-Q992', + 'issuedate' => '2024-06-12', + 'expirydate' => '2026-10-01', + 'vendor' => 'Pramita Medika Service', + 'isval' => '2026-03-01' ] ]; @@ -138,15 +291,100 @@ class Certificates extends BaseController { return $this->response->setJSON($certificates); } + public function createTrainingPreview($certid = null) { // Untuk Preview Sertifikat + //Melakukan search data dari database + + if (!$certid) { + return $this->response->setStatusCode(400)->setJSON(['error' => 'Certificate ID is required']); + } + + // Get certificate data Berdasarkan certid + $certificate = [ + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11919', + 'certname' => 'Jokoh Calibration Certificate', + 'productname' => 'Jokoh', + 'productnumber' => 'SN-2024-001', + 'issuedate' => '2024-01-15', + 'expirydate' => '2025-01-15', + 'vendor' => 'Summit Calibration Lab', + 'isval' => null + ]; + + if (empty($certificate)) { // JIka Tidak Ada + return $this->response->setStatusCode(404)->setJSON(['error' => 'Maintenance certificate not found']); + } + + return $this->previewPdf($certificate, 'training'); // Preview PDF + } + // Untuk Sertifikat Calibrate [4] public function calibrateIndex() { - return view('certificate_calibration_index'); + return view('certificate_calibrate_index'); + } + public function getDataIndexCalibrate() { + // $actid = $this->request->getVar('actid'); + + // Sample data - replace with actual database query + $certificates = [ + [ + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11919', + 'certname' => 'Jokoh Calibration Certificate', + 'productname' => 'Jokoh', + 'productnumber' => 'SN-2024-001', + 'issuedate' => '2024-01-15', + 'expirydate' => '2025-01-15', + 'vendor' => 'Summit Calibration Lab', + 'isval' => null + ], + [ + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11919', + 'certname' => 'Electrical Safety Test', + 'productname' => 'GE Healthcare VIVID', + 'productnumber' => 'GE-VIV-Q992', + 'issuedate' => '2024-06-12', + 'expirydate' => '2026-10-01', + 'vendor' => 'Pramita Medika Service', + 'isval' => '2026-03-01' + ] + ]; + + // If no actid, return all certificates + if (empty($certificates)) { + return $this->response->setJSON(null); + } + + return $this->response->setJSON($certificates); + } + public function createCalibratePreview($certid = null) { // Untuk Preview Sertifikat + //Melakukan search data dari database + + if (!$certid) { + return $this->response->setStatusCode(400)->setJSON(['error' => 'Certificate ID is required']); + } + + // Get certificate data Berdasarkan certid + $certificate = [ + 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11919', + 'certname' => 'Jokoh Calibration Certificate', + 'productname' => 'Jokoh', + 'productnumber' => 'SN-2024-001', + 'issuedate' => '2024-01-15', + 'expirydate' => '2025-01-15', + 'vendor' => 'Summit Calibration Lab', + 'isval' => null + ]; + + if (empty($certificate)) { // JIka Tidak Ada + return $this->response->setStatusCode(404)->setJSON(['error' => 'Maintenance certificate not found']); + } + + return $this->previewPdf($certificate, 'calibrate', 'tms24i'); // Preview PDF } // Helper Function Preview dan Validate - private function previewPdf($certificate, $type, $productType=null) { // Untuk Show/Preview PDF + private function previewPdf($certificate, $type) { // Untuk Show/Preview PDF // Generate PDF $options = new Options(); $options->set('isRemoteEnabled', true); @@ -156,27 +394,11 @@ class Certificates extends BaseController { $dompdf = new Dompdf($options); // Format dates - $issuedate = date('F d, Y', strtotime($certificate['issuedate'])); - $expirydate = date('F d, Y', strtotime($certificate['expirydate'])); - - // Get status - $today = date('Y-m-d'); - $expiryCheck = date('Y-m-d', strtotime($certificate['expirydate'])); - $daysUntilExpiry = (strtotime($expiryCheck) - strtotime($today)) / (60 * 60 * 24); - - if ($daysUntilExpiry < 0) { - $status = 'Expired'; - $statusColor = '#dc3545'; - } elseif ($daysUntilExpiry <= 30) { - $status = 'Expiring Soon'; - $statusColor = '#ffc107'; - } else { - $status = 'Active'; - $statusColor = '#28a745'; - } + $certificate['issueddate'] = date('d-M-Y', strtotime($certificate['issueddate'])); + $certificate['expireddate'] = date('d-M-Y', strtotime($certificate['expireddate'])); // Select template and orientation based on type - $template = 'certificate_pdf'; + $template = ''; $orientation = 'portrait'; switch($type) { @@ -210,15 +432,13 @@ class Certificates extends BaseController { $template = 'certificates/certificate_maintenance'; $orientation = 'landscape'; break; + case 'installation': + $template = 'certificates/certificate_installation'; + $orientation = 'landscape'; + break; } - $html = view($template, [ - 'certificate' => $certificate, - 'issuedate' => $issuedate, - 'expirydate' => $expirydate, - 'status' => $status, - 'statusColor' => $statusColor - ]); + $html = view($template, [ 'certificate' => $certificate]); $dompdf->loadHtml($html); // $dompdf->set_option('isRemoteEnabled', true); @@ -226,203 +446,283 @@ class Certificates extends BaseController { $dompdf->render(); // Output PDF - $filename = 'Certificate_' . $certificate['certid'] . '.pdf'; + $filename = $certificate['certname']. '.pdf'; $dompdf->stream($filename, ['Attachment' => false]); } - public function validateCertificate() { // Untuk Validasi Certificate + public function validateCertificate() { $certid = $this->request->getPost('certid'); - $certificateType = $this->request->getPost('certificateType'); + $certificateType = $this->request->getPost('certificateType'); - if (!$certid) { - return $this->response->setJSON([ - 'success' => false, - 'message' => 'Certificate ID is required' - ]); + if (!$certid || !$certificateType) { + return $this->response->setJSON(['success' => false, 'message' => 'Parameter tidak lengkap.']); } - // Dummy data certificates - $certificates = [ - 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11914', - 'certname' => 'Jokoh Calibration Certificate', - 'productname' => 'Jokoh', - 'productnumber' => 'SN-2024-001', - 'issuedate' => '2024-01-15', - 'expirydate' => '2025-01-15', - 'vendor' => 'Summit Calibration Lab', - 'isval' => null - ]; + $userId = session()->get('userid'); + $userPosId = session()->get('userposid'); + $certificateModel = new CertificateModel(); - if ($certificates['certid'] === $certid) { - return $this->response->setJSON([ - 'success' => true, - 'message' => 'Certificate has already been validateda' - ]); - } else { - return $this->response->setJSON([ - 'success' => false, - 'message' => 'Certificate not found' - ]); + // 1. Ambil data dasar saja dulu untuk pengecekan awal + $currentCert = $certificateModel->find($certid); + + if (!$currentCert) { + return $this->response->setJSON(['success' => false, 'message' => 'Data tidak ditemukan.']); } - // $validationDate = date('Y-m-d'); + $updateData = []; + $currentTime = date('Y-m-d H:i:s'); - // return $this->response->setJSON([ - // 'success' => true, - // 'message' => 'Certificate validated ' . $certificateType, - // 'validationDate' => $validationDate - // ]); - } - - - // Helper Generate dan Save PDF - public function generatePdf() { - $certid = $this->request->getPost('certid'); - $certificateType = $this->request->getPost('certificateType'); - - if (!$certid) { - return $this->response->setJSON([ - 'success' => false, - 'message' => 'Certificate ID is required' - ]); + // 2. Filter Role & Cek Duplikasi + switch ($userPosId) { + case 1: // Manager + if (!empty($currentCert['manager_validation_at'])) + return $this->response->setJSON(['success' => false, 'message' => 'Anda sudah Melakukan Validasi.']); + $updateData = ['manager_id' => $userId, 'manager_validation_at' => $currentTime]; + break; + case 2: // SPV + if (!empty($currentCert['spv_validation_at'])) + return $this->response->setJSON(['success' => false, 'message' => 'Anda sudah Melakukan Validasi.']); + $updateData = ['spv_id' => $userId, 'spv_validation_at' => $currentTime]; + break; + case 4: // TSOIVD + if ($currentCert['user_id'] != $userId) + return $this->response->setJSON(['success' => false, 'message' => 'Bukan pemilik sertifikat.']); + if (!empty($currentCert['user_validation_at'])) + return $this->response->setJSON(['success' => false, 'message' => 'Anda sudah Melakukan Validasi.']); + $updateData = ['user_validation_at' => $currentTime]; + break; + default: + return $this->response->setJSON(['success' => false, 'message' => 'Akses ditolak.'], 403); } - // Dummy data certificates - $certificate = [ - 'certid' => 'f353ca91-4fc5-49f2-9b9e-304f83d11914', - 'certname' => 'Jokoh Calibration Certificate', - 'productname' => 'Jokoh', - 'productnumber' => 'SN-2024-001', - 'issuedate' => '2024-01-15', - 'expirydate' => '2025-01-15', - 'vendor' => 'Summit Calibration Lab', - 'isval' => null - ]; + // 3. Eksekusi Update Validasi Role + if ($certificateModel->update($certid, $updateData)) { + + // 4. Cek apakah ini validasi terakhir? + // Ambil ulang data terbaru (cukup kolom validation saja untuk efisiensi) + $checkFinal = $certificateModel->select('user_validation_at, spv_validation_at, manager_validation_at') + ->find($certid); - if (empty($certificate)) { - return $this->response->setJSON([ - 'success' => false, - 'message' => 'Certificate not found' - ]); - } + if (!empty($checkFinal['user_validation_at']) && + !empty($checkFinal['spv_validation_at']) && + !empty($checkFinal['manager_validation_at'])) { + + // Update Status Utama + $certificateModel->update($certid, ['status' => 'validated']); - try { - $filePath = $this->savePdf($certificate, $certificateType, null); + // Baru jalankan query berat JOIN di sini untuk keperluan PDF/Notifikasi + $latestData = $certificateModel->select(' + certificates.cert_name, + certificates.issued_date, + certificates.expired_date, + productcatalog.productname as productname, + sites.sitename as sitename, + products.productnumber, + CASE + WHEN certificates.cert_type = "MC" THEN "Maintenance" + WHEN certificates.cert_type = "IC" THEN "Installation" + WHEN certificates.cert_type = "UTC" THEN "Training" + WHEN certificates.cert_type = "BAI" THEN "Before After Inspection" + WHEN certificates.cert_type = "BAP" THEN "Berita Acara Pengerjaan" + ELSE certificates.cert_type + END AS cert_type, + CONCAT(users.firstname, " ", users.lastname) AS fullname, + userposition.texts AS user_position, + certificates.issued_date, + certificates.expired_date, + certificates.cert_number -- Penting agar callback UUID tetap jalan + ', false) + ->join('users', 'users.userid = certificates.user_id', 'left') + ->join('userposition', 'userposition.userposid = users.userposid', 'left') + ->join('activities', 'activities.actid = certificates.actid', 'left') + ->join('sites', 'sites.siteid = activities.siteid', 'left') + ->join('products', 'products.productid = activities.productid', 'left') + ->join('productcatalog', 'productcatalog.catalogid = products.catalogid', 'left') + ->where('certificates.cert_id', $certid) + ->first(); + $certificate = [ + 'certname' => $latestData['cert_name'], + 'sitename' => $latestData['sitename'], + 'certtype' => $latestData['cert_type'], + 'fullname' => $latestData['fullname'], + 'userposition' => $latestData['user_position'], + 'productname' => $latestData['productname'], + 'productnumber' => $latestData['productnumber'], + 'issueddate' => $latestData['issued_date'], + 'expireddate' => $latestData['expired_date'] + ]; + + try { + $pdfAfterValidation = $this->savePdf($certificate, $latestData['cert_type']); // Simpan ke PDF + + $certificateModel->update($certid, [ // Update ke tabel certificates + 'file_location' => $pdfAfterValidation['file_relative'], + 'metadata_title' => $pdfAfterValidation['metadata_title'], + 'metadata_keywords' => $pdfAfterValidation['metadata_keywords'], + 'file_url' => base_url('certificates/number/'.$latestData['cert_number']) + ]); + + return $this->response->setJSON([ + 'success' => true, + 'message' => 'Semua validasi telah dilakukan, PDF sudah di generate' + ]); + + } catch (\Throwable $e) { + + return $this->response->setStatusCode(500)->setJSON([ + 'success' => false, + 'message' => $e->getMessage() + ]); + } - if (!$filePath) { - return $this->response->setJSON([ - 'success' => false, - 'message' => 'Failed to generate PDF' - ]); } - - $relativePath = str_replace(FCPATH, '', $filePath); - return $this->response->setJSON([ 'success' => true, - 'message' => 'PDF generated and saved successfully', - 'filePath' => $relativePath - ]); - - } catch (\Exception $e) { - return $this->response->setJSON([ - 'success' => false, - 'message' => 'Failed to generate PDF: ' . $e->getMessage() + 'message' => "Sertifikat {$certificateType} berhasil divalidasi." ]); } + + return $this->response->setJSON(['success' => false, 'message' => 'Gagal memperbarui data.']); } - private function savePdf($certificate, $type, $productType=null) { - // Generate PDF + public function savePdf($certificate, $certificateType, $productType = null) { + $certificateType = strtolower($certificateType); + + switch ($certificateType) { + case 'training': + $template = 'certificates/certificate_training'; + $orientation = 'landscape'; + $subDir = 'training'; + break; + + case 'calibration': + $orientation = 'portrait'; + $subDir = 'calibration'; + + switch ($productType) { + case 'tms50i': + $template = 'certificates/callibrations_template/certificate_tms50i_calibration'; + break; + case 'tms24i': + $template = 'certificates/callibrations_template/certificate_tms24i_calibration'; + break; + case 'tms30i': + $template = 'certificates/callibrations_template/certificate_tms30i_calibration'; + break; + case 'bs430': + $template = 'certificates/callibrations_template/certificate_bs430_calibration'; + break; + case 'cl900i': + $template = 'certificates/callibrations_template/certificate_cl900i_calibration'; + break; + case 'jokoh': + $template = 'certificates/callibrations_template/certificate_jokoh_calibration'; + break; + case 'bc760r': + $template = 'certificates/callibrations_template/certificate_bc760r_calibration'; + break; + case 'bc5140': + $template = 'certificates/callibrations_template/certificate_bc5140_calibration'; + break; + default: + throw new \Exception('Product type calibration tidak valid'); + } + break; + + case 'maintenance': + $template = 'certificates/certificate_maintenance'; + $orientation = 'landscape'; + $subDir = 'maintenance'; + break; + + case 'installation': + $template = 'certificates/certificate_installation'; + $orientation = 'landscape'; + $subDir = 'installation'; + break; + + default: + throw new \Exception('Certificate type tidak valid'); + } + + if (empty($template)) { + throw new \Exception('Template tidak ditemukan'); + } + + // Dompdf $options = new Options(); $options->set('isRemoteEnabled', true); $options->set('isHtml5ParserEnabled', true); $dompdf = new Dompdf($options); - - // Format dates - $issuedate = date('F d, Y', strtotime($certificate['issuedate'])); - $expirydate = date('F d, Y', strtotime($certificate['expirydate'])); - - // Get status - $today = date('Y-m-d'); - $expiryCheck = date('Y-m-d', strtotime($certificate['expirydate'])); - $daysUntilExpiry = (strtotime($expiryCheck) - strtotime($today)) / (60 * 60 * 24); - - if ($daysUntilExpiry < 0) { - $status = 'Expired'; - $statusColor = '#dc3545'; - } elseif ($daysUntilExpiry <= 30) { - $status = 'Expiring Soon'; - $statusColor = '#ffc107'; - } else { - $status = 'Active'; - $statusColor = '#28a745'; - } - - // Select template and orientation based on type - $template = 'certificate_pdf'; - $orientation = 'portrait'; - - switch($type) { - case 'training': - $template = 'certificates/certificate_training'; - $orientation = 'landscape'; - break; - case 'calibration': - if ($productType == 'tms50i') { - $template = 'certificates/callibrations_template/certificate_tms50i_calibration'; - } else if ($productType == 'tms24i') { - $template = 'certificates/callibrations_template/certificate_tms24i_calibration'; - } else if ($productType == 'tms30i') { - $template = 'certificates/callibrations_template/certificate_tms30i_calibration'; - } else if ($productType == 'bs430') { - $template = 'certificates/callibrations_template/certificate_bs430_calibration'; - } else if ($productType == 'cl900i') { - $template = 'certificates/callibrations_template/certificate_cl900i_calibration'; - } else if ($productType == 'jokoh') { - $template = 'certificates/callibrations_template/certificate_jokoh_calibration'; - } else if ($productType == 'bc760r') { - $template = 'certificates/callibrations_template/certificate_bc760r_calibration'; - } else if ($productType == 'bc5140') { - $template = 'certificates/callibrations_template/certificate_bc5140_calibration'; - } else { - return $this->response->setStatusCode(404)->setJSON(['error' => 'Not Found']); - } - $orientation = 'portrait'; - break; - case 'maintenance': - $template = 'certificates/certificate_maintenance'; - $orientation = 'landscape'; - break; - } - - $html = view($template, [ - 'certificate' => $certificate, - 'issuedate' => $issuedate, - 'expirydate' => $expirydate, - 'status' => $status, - 'statusColor' => $statusColor - ]); + $html = view($template, ['certificate' => $certificate]); $dompdf->loadHtml($html); $dompdf->setPaper('A4', $orientation); $dompdf->render(); - // Generate filename with timestamp - $timestamp = date('YmdHis'); - $filename = 'Certificate_' . $certificate['certid'] . '_' . $timestamp . '.pdf'; + // Metadata + $dompdf->addInfo('Title', $certificate['certname']); + $dompdf->addInfo('Keywords', $certificate['certtype'] . ' Certificate'); + + // Folder + $uploadDir = FCPATH . 'upload/documents/' . $subDir; - // Ensure upload directory exists - $uploadDir = FCPATH . 'upload' . DIRECTORY_SEPARATOR . 'documents'; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } - // Save PDF to file + // Safe filename + $cleanName = preg_replace('/[^A-Za-z0-9_\-]/', '_', $certificate['certname']); + $timestamp = date('YmdHis'); + $filename = $cleanName . '_' . $timestamp . '.pdf'; + $filePath = $uploadDir . DIRECTORY_SEPARATOR . $filename; - file_put_contents($filePath, $dompdf->output()); - // Return file path - return $filePath; + if (!file_put_contents($filePath, $dompdf->output())) { + throw new \Exception('Gagal menyimpan file PDF'); + } + + return [ + 'file_name' => $filename, + 'file_path' => $filePath, + 'file_relative' => 'upload/documents/' . $subDir . '/' . $filename, + 'metadata_title' => $certificate['certname'], + 'metadata_keywords' => $certificate['certtype'] . ' Certificate' + ]; } + public function view($uuid) { + try { + $certificateModel = new CertificateModel(); + // Ambil dari model (UUID → binary otomatis) + $certificate = $certificateModel->getByUuid($uuid); + + if (!$certificate) { + throw new \Exception('Certificate tidak ditemukan'); + } + + if (empty($certificate['file_location'])) { + throw new \Exception('File PDF belum tersedia'); + } + + $filePath = FCPATH . $certificate['file_location']; + + if (!file_exists($filePath)) { + throw new \Exception('File tidak ditemukan di server'); + } + + return $this->response + ->setHeader('Content-Type', 'application/pdf') + ->setHeader( + 'Content-Disposition', + 'inline; filename="' . basename($filePath) . '"' + ) + ->setBody(file_get_contents($filePath)); + + } catch (\Throwable $e) { + return $this->response->setStatusCode(404)->setJSON([ + // 'success' => false, + 'message' => "404 Not Found" + ]); + } + } } \ No newline at end of file diff --git a/app/Models/CertificateModel.php b/app/Models/CertificateModel.php new file mode 100644 index 0000000..ed3b77d --- /dev/null +++ b/app/Models/CertificateModel.php @@ -0,0 +1,76 @@ +getBytes(); + } + return $data; + } + + /** + * Otomatis ubah Binary ke String UUID setelah data diambil (Get) + */ + protected function convertBinaryToUuidString(array $data) + { + if (empty($data['data'])) return $data; + + // Cek apakah data berupa single row atau multiple rows + if (isset($data['data']['cert_number'])) { + // Single row (find/first) + $data['data']['cert_number'] = Uuid::fromBytes($data['data']['cert_number'])->toString(); + } else { + // Multiple rows (findAll) + foreach ($data['data'] as &$row) { + if (isset($row['cert_number'])) { + $row['cert_number'] = Uuid::fromBytes($row['cert_number'])->toString(); + } + } + } + + return $data; + } + + /** + * Helper untuk mencari data berdasarkan String UUID + */ + public function getByUuid(string $uuidString) + { + $binary = Uuid::fromString($uuidString)->getBytes(); + return $this->where('cert_number', $binary)->first(); + } +} \ No newline at end of file diff --git a/app/Views/activities_editor.php b/app/Views/activities_editor.php index 3b07d47..e96681f 100644 --- a/app/Views/activities_editor.php +++ b/app/Views/activities_editor.php @@ -609,6 +609,144 @@ if(isset($data)) { + + +
+
+
+
+

+ +

+
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+
+ + + + + + + + + + ". + "". + ""; + } + } + ?> + +
Nama AnalisTanggalAction
$qname $qdate
+
+
+
+
+
+
+
+ + +
+
+
+
+

+ +

+
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
+
+ + + + + + + + + + + ". + "". + ""; + } + } + ?> + +
Nama ParameterNilaiSatuanAction
$qname $qvalue $qunit
+
+
+
+
+
+
+
@@ -634,6 +772,14 @@ if(isset($data)) { endSection() ?> section('script') ?> + endSection() ?> + diff --git a/app/Views/certificate_calibrate_index.php b/app/Views/certificate_calibrate_index.php new file mode 100644 index 0000000..34777a9 --- /dev/null +++ b/app/Views/certificate_calibrate_index.php @@ -0,0 +1,382 @@ +extend('layouts/main.php') ?> + +section('content') ?> + +
+
+
+
+

Certificates Training Management

+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ + +
+ + + + + + + + + + + + + + +
Certificate NameProduct/EquipmentActivity ReportIssue DateExpiry DateStatusAction
+
+
+
+
+
+
+
+ + + + +endSection() ?> + +section('style') ?> + +endSection() ?> + +section('script') ?> + +endSection() ?> \ No newline at end of file diff --git a/app/Views/certificate_installation_index.php b/app/Views/certificate_installation_index.php new file mode 100644 index 0000000..4a199a4 --- /dev/null +++ b/app/Views/certificate_installation_index.php @@ -0,0 +1,381 @@ +extend('layouts/main.php') ?> + +section('content') ?> + +
+
+
+
+

Certificates Installation Management

+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ + +
+ + + + + + + + + + + + + + +
Certificate NameProduct/EquipmentActivity ReportIssue DateExpiry DateStatusAction
+
+
+
+
+
+
+
+ + + + +endSection() ?> + +section('style') ?> + +endSection() ?> + +section('script') ?> + +endSection() ?> \ No newline at end of file diff --git a/app/Views/certificate_maintenance_index.php b/app/Views/certificate_maintenance_index.php index 2441cf1..038621a 100644 --- a/app/Views/certificate_maintenance_index.php +++ b/app/Views/certificate_maintenance_index.php @@ -22,24 +22,30 @@ placeholder="Search certificates by name, product, type, vendor, or dates...">
-
+
-
+
+ +
+
-
+
@@ -51,13 +57,14 @@ - - - - - - - + + + + + + + + @@ -77,63 +84,88 @@
Certificate NameProduct/EquipmentActivity ReportIssue DateExpiry DateStatusActionNoCertificateAct ReportIssue DateExpiry DateStatusValidationAction
- - - - - - - - - - - - - -
NoCertificate NameProduct/EquipmentIssue DateVendorAction
+ +
+ + + + + + + + + + + + + + +
Certificate NameProduct/EquipmentActivity ReportIssue DateExpiry DateStatusAction
@@ -69,366 +71,371 @@
- -