model = model('App\Models\SqlModel\BaseModelSql'); $this->wablast = new DhivaProdevWa\ProdevMessages(ProdevToken); $this->initControllers(); if (class_exists(\Config\ExternalApi::class)) { $this->externalApi = new \Config\ExternalApi(); } else { $this->externalApi = null; } // Preload any models, libraries, etc, here. // E.g.: $this->session = \Config\Services::session(); } // public function initControllers() // { // // $this->response = ResponseTrait::response(); // // $this->get_client_ip(); // // $this->response(GET, phpinfo()); // /** // * @var array $bypassed isi array di variable $bypassed untuk mengabaikan pengecekan endpoint di database. // * @example string $bypassed = ["/auth" => 'POST', "/data" => 'GET']; // */ // $bypassed = []; // /** // * @var string $publickey terdapat di encodeloop header dan master db // * @example : $publickey = 'bHJ0LmFuZHJvd2ViaG9zdC5jb20='; // */ // $publickey = ''; // $this->initHeader(); // // print_r('asd');die; // $this->initDb('postgre', $publickey); // if (!$this->checkEndpoint($this->getEndpointInfo(), $bypassed)) { // if (isset($this->ClientSecret) && isset($this->Authorization)) { // if ((DhivaAES::jwtvalidator($this->ClientSecret, $this->Authorization))) { // $decodedToken = DhivaAES::validateTimestampWtihUserAccess($this->Authorization); // if (isset($decodedToken->super_user_id)) { // $update['access_at'] = date('Y-m-d H:i:s', time()); // $this->userDatas = $decodedToken; // $this->model->super_user->update($update, $decodedToken->super_user_id); // } else { // $this->response(UNAUTHORIZED, 2); // } // } else { // $this->response(UNAUTHORIZED, 3); // } // } else { // $this->response(UNAUTHORIZED, 3); // } // } // } public function initControllers() { $bypassed = [ '/auth' => 'POST', '/auth/v2' => 'POST', '/user' => 'POST', '/user-verifications/request-otp' => 'POST', '/user-verifications/verification-otp-code' => 'POST', '/user-verifications/resend-otp-code' => 'POST', '/user-verifications/upload-face-photos' => 'POST', '/forgot-password' => 'POST', '/reset-password' => 'POST', '/pelanggaran/feedback' => 'POST', '/list-pelanggaran' => 'GET', '/pelanggaran/get-feedback-by-id' => 'POST', '/pencarian-pelanggaran-2' => 'POST', '/check-bypass-liveness' => 'GET', '/koperasi' => 'GET', '/koperasi/' => 'GET', '/get-distribusi-shu-mingguan/' => 'GET', '/get-distribusi-shu-mingguan' => 'GET', '/live-gold-price' => 'GET', '/update-profile' => 'POST', '/profile' => 'GET', ]; $publickey = ''; $this->initHeader(); $this->initDb('postgre', $publickey); if ($this->checkEndpoint($this->getEndpointInfo(), $bypassed)) { return; } if (!isset($this->ClientSecret) || !isset($this->Authorization)) { $this->response(UNAUTHORIZED, 3); } try { if (!DhivaAES::jwtvalidator($this->ClientSecret, $this->Authorization)) { $this->response(UNAUTHORIZED, 3); } $decodedToken = DhivaAES::validateTimestampWtihUserAccess($this->Authorization); if (!isset($decodedToken->super_user_id)) { $this->response(UNAUTHORIZED, 3); } // Update accessed_at time $update['access_at'] = date('Y-m-d H:i:s', time()); $this->userDatas = $decodedToken; $this->model->super_user->update($update, $decodedToken->super_user_id); } catch (\Firebase\JWT\ExpiredException $e) { log_message('error', 'Token expired: ' . $e->getMessage()); $this->response(UNAUTHORIZED, 3); } catch (\Exception $e) { log_message('error', 'Token validation error: ' . $e->getMessage()); $this->response(UNAUTHORIZED, 3); } } public function decodeDb($dataDb) { $underscore = ''; $decodedDbResult = ''; //get encoded domain,username,password $domain = $dataDb->domain_db; $usernameDb = ""; $passwordDb = ""; if (isset($dataDb->username_db)) $usernameDb = $dataDb->username_db; if (isset($dataDb->password_db)) $passwordDb = $dataDb->password_db; //decode domain,username,password $decodeDomain = decodeloop($domain); $decodeUsernameDb = decodeloop($usernameDb); $decodePasswordDb = decodeloop($passwordDb); $splittedWords = explode('_', $decodeDomain); foreach ($splittedWords as $key => $value) { if ($key != (count($splittedWords) - 1)) { $underscore = "_"; } $decodedDbResult .= deshuffle_word($value) . $underscore; $underscore = ''; } //change value domain,userbaneDb,passwordDb into decoded string $dataDb->domain_db = $decodedDbResult; $dataDb->username_db = $decodeUsernameDb; $dataDb->password_db = $decodePasswordDb; return $dataDb; } public function response($code, $data = NULL) { $HTTP_CODE = HTTP_BAD_REQUEST; $message = ''; if (!$data) { if (ENVIRONMENT == 'development') { if ($code == 4) { $message = UNAUTHORIZED_CODE[0]; $success = false; $HTTP_CODE = HTTP_UNAUTHORIZED; } else { $success = false; $message = ERROR_CODE[$code] . " !"; } } else if (ENVIRONMENT == 'production') { if ($code == 4) { $message = UNAUTHORIZED_CODE[0]; $success = false; $HTTP_CODE = HTTP_UNAUTHORIZED; } else { $success = false; $message = ERROR_CODE[$code] . " !"; } } else { $success = false; } } else { if ($code == 3) { $success = true; if (ENVIRONMENT == 'development') { $message = $data; } else if (ENVIRONMENT == 'production') { $message = $data; } else { $message = self::encryptPayload(json_encode($data)); if (!$message) { $message = 'Token Invalid!'; } } $HTTP_CODE = HTTP_OK; } elseif ($code == 0) { if (!$data) { $success = false; $message = ERROR_CODE[$code] . " !"; } else { $message = SUCCESS_CODE[$code] . " !";; $success = true; $HTTP_CODE = HTTP_OK; } } else { if ($code == 4) { $message = UNAUTHORIZED_CODE[$data]; $success = false; $HTTP_CODE = HTTP_UNAUTHORIZED; } else { $message = SUCCESS_CODE[$code] . " !"; $success = true; $HTTP_CODE = HTTP_OK; } } } $response = [ 'success' => $success, 'code' => $HTTP_CODE, 'message' => $message, ]; header('Content-Type: application/json'); echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); die; } function post($key = null, $clean = true) { // print_r([$_POST[$key], $key]);die; if (ENVIRONMENT == 'development') { if ($key) { if (isset($_POST[$key])) { return $_POST[$key]; } } else { return $_POST; } } else if (ENVIRONMENT == 'production') { if ($key) { if (isset($_POST[$key])) { return $_POST[$key]; } } else { return $_POST; } } else { $encrypt_method = "AES-256-CBC"; if ($this->isbypassed) { $secret_key = SSL_KEY['Payload_Key']; $secret_iv = SSL_KEY['Payload_IV']; } else { $secret_key = md5($this->userDatas->token); $secret_iv = md5($this->userDatas->access_at); } $keys = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv), 0, 16); $decodedEncryptedData = $_POST['data']; $decrypted = openssl_decrypt($decodedEncryptedData, $encrypt_method, $keys, 0, $iv); $result = (array) json_decode(base64_decode($decrypted)); if ($result == false) { $response = [ 'code' => 203, 'success' => false, 'message' => 'Invalid Code', ]; header('Content-Type: application/json'); echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); die; } if (!$key) { $tr = []; foreach ($result as $k => $val) { $g = self::setArrayValue($k, $val); if ($g) { $tr[[array_keys($g)[0]][0]][0][array_keys($g[array_keys($g)[0]][0])[0]] = array_values($g[array_keys($g)[0]][0])[0]; } else { $tr[$k] = $val; } } return $tr; } if (isset($result[$key])) { return $result[$key]; } return false; } return false; } protected function searchpost($post): bool | array { if (isset($post["from"]) && isset($post["to"]) && isset($post["page"]) && isset($post["limit"])) { $data['from'] = $post["from"]; $data['to'] = $post["to"]; $data['page'] = $post["page"]; $data['limit'] = $post["limit"]; unset($post["from"]); unset($post["to"]); unset($post["page"]); unset($post["limit"]); return $data; } return $post; } function setArrayValue($string, $value) { if (preg_match('/^(\w+)\[(\d+)\]\[(\w+)\]$/', $string, $matches)) { $root = $matches[1]; $index = (int)$matches[2]; $key = $matches[3]; $array = [ $root => [ $index => [ $key => $value ] ] ]; return $array; } else { return false; } } protected function encryptPayload($string) { $encrypt_method = "aes-256-cbc"; if ($this->isbypassed) { $secret_key = SSL_KEY['Payload_Key']; $secret_iv = SSL_KEY['Payload_IV']; } else { $secret_key = md5($this->userDatas->token); $secret_iv = md5($this->userDatas->access_at); } $key = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv), 0, 16); $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv); $output = base64_encode($output); return $output; } protected function decryptPayload($string) { $encrypt_method = "aes-256-cbc"; if ($this->isbypassed) { $secret_key = SSL_KEY['Payload_Key']; $secret_iv = SSL_KEY['Payload_IV']; } else { $secret_key = md5($this->userDatas->token); $secret_iv = md5($this->userDatas->access_at); } $keys = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv), 0, 16); $decodedEncryptedData = $string; $decrypted = openssl_decrypt($decodedEncryptedData, $encrypt_method, $keys, 0, $iv); $result = json_decode(base64_decode($decrypted)); print_r($result); die; } protected function getEndpointInfo() { if (!$_SERVER['PATH_INFO']) { $PATH_INFO = str_replace($_SERVER['DOCUMENT_URI'], "", $_SERVER['REQUEST_URI']); $endpointAccessed = $PATH_INFO; } else { $endpointAccessed = $_SERVER['PATH_INFO']; } $endpointRequestMethod = $_SERVER['REQUEST_METHOD']; $endpointRequest = "/"; $endpointUpdate = "update"; $endpointDelete = "delete"; $endpointPage = "pages"; $endpointPages = "pagesbydate"; $endpointAccessExploded = explode('/', $endpointAccessed ?? ''); $countEndpointAccessExploded = count($endpointAccessExploded); $separator = '/'; $lastSegment = $countEndpointAccessExploded - 1; for ($i = 0; $i <= $lastSegment; $i++) { if ($countEndpointAccessExploded - 1 == $i) { $separator = ""; } // print_r($endpointAccessExploded);die(); if ($endpointAccessExploded[$i] != "") { $endpointRequest .= $endpointAccessExploded[$i] . $separator; } if (($endpointAccessExploded[$i] == $endpointUpdate) || ($endpointAccessExploded[$i] == $endpointDelete) || ($endpointAccessExploded[$i] == $endpointPage) || ($endpointAccessExploded[$i] == $endpointPages)) { break; } if (str_contains($endpointAccessExploded[$i], '_by')) { $lastSegment = $i + 1; } } // $endpointRequest = str_replace('/kamdev', '', $endpointRequest); // $endpointRequest = str_replace('/propamntt_api', '', $endpointRequest); $endpointRequest = str_replace('/api_tambang_ntb', '', $endpointRequest); // print_r([$endpointRequest]);die(); return [$endpointRequestMethod, $endpointRequest]; } public function DbInit($dbSelect, $query, $useMasterDb = true) { if ($query && $useMasterDb) { $dbSelect = [ 'DSN' => '', 'hostname' => 'localhost', 'username' => encodeloop($query->username_db), 'password' => encodeloop($query->password_db), 'database' => $query->domain_db, 'DBPrefix' => $query->domain_db . '.', 'pConnect' => false, 'DBDebug' => true, 'charset' => 'utf8', 'DBCollat' => 'utf8_bin', 'swapPre' => '', 'encrypt' => true, 'compress' => false, 'strictOn' => false, 'failover' => [], ]; } // if ($PGDBPrefix) { // $db = new \Config\Database; // $dbSelect = $db->postgre; // $dbSelect['DBPrefix'] = ''; // $this->table = $PGDBPrefix; // } $this->db = \Config\Database::connect($dbSelect); } // private function get_client_ip() // { // if (isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['SERVER_NAME'])) { // if (($_SERVER['SERVER_NAME'] == NAMA_SERVER) && ($_SERVER['REMOTE_ADDR'] != REMOT) && (ENVIRONMENT == "production")) { // $this->response(UNAUTHORIZED, 3); // } // } else { // $this->response(UNAUTHORIZED, 3); // } // } /** * Inisialisasi tipe database yang dijalankan * * @param string $selectedDb @var| postgre | mysql. * @return void */ private function initDB(string $selectedDb, string $publickey) { $this->DbInit($selectedDb, false); if (isset($this->PublicKey)) { $builder = $this->db ->table('super_domain') ->where('name', decodeloop($this->PublicKey)); $dbInfo = $builder->get()->getRow(); if (!$dbInfo) { $this->response(UNAUTHORIZED, 1); } $this->DbInit($selectedDb, $this->decodeDb($dbInfo)); } else { if ($publickey) { $builder = $this->db ->table('super_domain') ->where('name', decodeloop($this->PublicKey)); $dbInfo = $builder->get()->getRow(); if ($dbInfo) { $this->response(UNAUTHORIZED, 1); } $this->DbInit($selectedDb, false, false); } else { $this->DbInit($selectedDb, false, false); } } } private function initHeader() { $this->headers = getallheaders(); if (isset($this->headers['Publickey'])) { $this->PublicKey = $this->headers['Publickey']; } else if (isset($this->headers['publickey'])) { $this->PublicKey = $this->headers['publickey']; } if (isset($this->headers['ClientSecret'])) { $this->ClientSecret = $this->headers['ClientSecret']; } else if (isset($this->headers['clientSecret'])) { $this->ClientSecret = $this->headers['clientSecret']; } else if (isset($this->headers['clientsecret'])) { $this->ClientSecret = $this->headers['clientsecret']; } else if (isset($this->headers['Clientsecret'])) { $this->ClientSecret = $this->headers['Clientsecret']; } if (isset($this->headers['Authorization'])) { $this->Authorization = $this->headers['Authorization']; } else if (isset($this->headers['authorization'])) { $this->Authorization = $this->headers['authorization']; } } // private function checkEndpoint($endpoint, $bypassed = []) // { // $status = false; // if (!$bypassed) { // // Bersihkan query parameter dari endpoint // $cleanEndpoint = strtok($endpoint[1], '?'); // Mengambil path tanpa query string // $builder = $this->db // ->table('endpoint') // ->where('method', $endpoint[0]) // ->like('value', $cleanEndpoint, 'after') // ->limit(1); // $result = $builder->get()->getRow(); // if (isset($result)) { // if ($result->bypass == true) { // $status = true; // $this->isbypassed = true; // } else { // $exp = explode('/', $cleanEndpoint); // if (in_array('pagination', $exp)) { // if (isset($_POST['where'])) { // $pp = explode('#', $result->pagination); // $wheres = explode(',', $_POST['where']); // if (!array_intersect($pp, $wheres)) { // $this->response(UNAUTHORIZED, 3); // } // } // } // $status = false; // } // } else { // $this->response(UNAUTHORIZED, 3); // } // } else { // if (in_array($endpoint[1], $bypassed) == false) { // if (!empty($bypassed[$endpoint[1]])) { // if ($bypassed[$endpoint[1]] == $endpoint[0]) { // $status = true; // $this->isbypassed = true; // PENTING: Set bypass flag // } // } // } // } // return $status; // } private function checkEndpoint($endpoint, $bypassed = []) { $status = false; if (!$bypassed) { // Logic untuk cek di database $cleanEndpoint = strtok($endpoint[1], '?'); $builder = $this->db ->table('endpoint') ->where('method', $endpoint[0]) ->like('value', $cleanEndpoint, 'after') ->limit(1); $result = $builder->get()->getRow(); if (isset($result)) { if ($result->bypass == true) { $status = true; $this->isbypassed = true; } else { $exp = explode('/', $cleanEndpoint); if (in_array('pagination', $exp)) { if (isset($_POST['where'])) { $pp = explode('#', $result->pagination); $wheres = explode(',', $_POST['where']); if (!array_intersect($pp, $wheres)) { $this->response(UNAUTHORIZED, 3); } } } $status = false; } } else { $this->response(UNAUTHORIZED, 3); } } else { $cleanEndpoint = strtok($endpoint[1], '?'); // Bersihkan query parameter // Log untuk debugging log_message('debug', "Checking endpoint - Method: {$endpoint[0]}, Path: {$cleanEndpoint}, Original: {$endpoint[1]}"); // Cek apakah endpoint ada di array $bypassed dengan method yang sesuai if (isset($bypassed[$cleanEndpoint]) && $bypassed[$cleanEndpoint] === $endpoint[0]) { $status = true; $this->isbypassed = true; log_message('info', "Endpoint bypassed: {$endpoint[0]} {$cleanEndpoint}"); } else { log_message('warning', "Endpoint not bypassed: {$endpoint[0]} {$cleanEndpoint}"); } } return $status; } protected function mabesGroup() { $group = $this->userDatas->super_group_id; if ($group == 1) { $where = [ 'satuan_id' => substr($this->userDatas->satuan_id, 0, 0), ]; } else if ($group == 2) { $where = [ 'satuan_id' => substr($this->userDatas->satuan_id, 0, 5), ]; } else if ($group == 3) { $where = [ 'satuan_id' => substr($this->userDatas->satuan_id, 0, 7), ]; } else if ($group == 4) { $where = [ 'satuan_id' => $this->userDatas->satuan_id, 'super_user_id' => $this->userDatas->super_user_id ]; } return $where; } /** * getKoperasiFilter * * Menentukan apakah user saat ini perlu difilter berdasarkan koperasi_id. * - Admin roles (1,2,3,6,20): tidak difilter, lihat semua data * - Role 8 (Koperasi): difilter berdasarkan koperasi_id dari super_user * - Default / bypassed endpoint: tidak difilter * * @return array ['filter' => bool, 'koperasi_id' => string|null] */ protected function getKoperasiFilter() { // Jika tidak ada user data (bypassed endpoint), tidak perlu filter if (!isset($this->userDatas) || !isset($this->userDatas->super_group_id)) { return ['filter' => false, 'koperasi_id' => null]; } $adminRoles = [1, 2, 3, 6, 20]; $group = $this->userDatas->super_group_id; // Admin roles: lihat semua data if (in_array((int)$group, $adminRoles)) { return ['filter' => false, 'koperasi_id' => null]; } // Role 8 = Koperasi: filter berdasarkan koperasi_id user if ((int)$group === 8) { $user = $this->db->table('public.super_user') ->where('super_user_id', $this->userDatas->super_user_id) ->get()->getRowArray(); return ['filter' => true, 'koperasi_id' => $user['koperasi_id'] ?? null]; } // Default: tidak difilter return ['filter' => false, 'koperasi_id' => null]; } public function index() { $data = $this->model->{$this->table}->index(); $this->response(GET, $data); } public function show($value) { $data = $this->model->{$this->table}->show($value); $this->response(GET, $data); } public function pagination($limit, $page) { $data = $this->model->{$this->table}->pagination($limit, $page); $this->response(GET, $data); } public function paginationpost() { $data = $this->model->{$this->table}->paginationpost(); $this->response(GET, $data); } public function paginationbyDate($limit, $page, $from, $to) { $data = $this->model->{$this->table}->paginationByDate($limit, $page, $from, $to); $this->response(GET, $data); } public function showBy($columnName, $value) { $data = $this->model->{$this->table}->showBy($columnName, $value); $this->response(GET, $data); } public function allBy($columnName, $value) { $data = $this->model->{$this->table}->allBy($columnName, $value); $this->response(GET, $data); } public function allByPost() { $data = $this->model->{$this->table}->allByAnd(postArray()); $this->response(GET, $data); } public function showByPost() { $data = $this->model->{$this->table}->findByAnd(postArray()); $this->response(GET, $data); } public function create() { $data = $this->request->getPost(); $result = $this->model->{$this->table}->insert($data); $this->response(INSERT, $result); } public function update($id) { $data = $this->request->getPost(); $result = $this->model->{$this->table}->update($data, $id); $this->response(UPDATE, $result); } public function destroy($id) { $data = $this->model->{$this->table}->destroy($id); $this->response(DELETE, $data); } /** * setPrefix * * @return void */ // public function createImage($path, $file_name, $post): string // { // if (!is_dir(ROOTPATH . $path . '/assets' . '/')) { // mkdir(ROOTPATH . $path . '/assets' . '/', 0775, TRUE); // } // if ($imagefile = $this->request->getFiles()) { // foreach ($imagefile['file'] as $img) { // if ($img->isValid() && !$img->hasMoved()) { // $newName = $img->getRandomName(); // $img->getTempName(); // $now = date('YmdHis'); // $fileName = md5($now) . '.webp'; // $destination = ROOTPATH . $path . '/assets' . '/' . $fileName; // $options = []; // WebPConvert::convert($img->getTempName(), $destination, $options); // $img->move(WRITEPATH . 'uploads', $newName); // } // } // } // } /** * createSingleImage * @param string $path * * Content-Disposition: form-data; * * name="file"; * * filename="/C:/Users/Test/OneDrive/Gambar/Rol Kamera/WIN_20230911_15_57_04_Pro.jpg" * */ public function createImage(string $path, string $fieldName = 'file') { $allowedImageExt = ['jpg', 'png', 'jpeg']; $allowedVideoExt = ['mp4']; $allowedVFileExt = ['pdf']; $imagefile = $this->request->getFiles(); $this->destination = []; // Pastikan array ini ada untuk menyimpan file // Ambil file berdasarkan nama field $files = $this->request->getFiles(); $fileInput = $files[$fieldName] ?? null; if (!$fileInput) { log_message('info', "File field '{$fieldName}' tidak ditemukan"); return null; } if (is_array($fileInput)) { foreach ($fileInput as $file) { if ($file->isValid() && !$file->hasMoved()) { $fileExt = $file->guessExtension(); // Buat folder jika belum ada if (!is_dir(PATH_IMAGES_SERVER . $path . '/')) { mkdir(PATH_IMAGES_SERVER . $path . '/', 0775, TRUE); } // **Jika file adalah gambar, konversi ke WebP** if (in_array($fileExt, $allowedImageExt)) { $fileName = DhivaAES::randomStr(5, true) . '.webp'; $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $options = []; WebPConvert::convert($file->getTempName(), $destination, $options); } // **Jika file adalah video, simpan sebagai MP4** elseif (in_array($fileExt, $allowedVideoExt)) { $fileName = DhivaAES::randomStr(5, true) . '.mp4'; $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $file->move(PATH_IMAGES_SERVER . $path . '/', $fileName); } // **Jika file adalah pdf, simpan sebagai PDF** elseif (in_array($fileExt, $allowedVideoExt)) { $fileName = DhivaAES::randomStr(5, true) . '.pdf'; $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $file->move(PATH_IMAGES_SERVER . $path . '/', $fileName); } // **Jika ekstensi tidak diperbolehkan, kirim respons error** else { log_message('error', "Extensi file tidak diperbolehkan: {$fileExt}"); continue; // // throw new \Exception('Extensi file tidak diperbolehkan!'); // $this->response(GET, 'NOT_ACCEPTABLE'); } $this->destination[] = DhivaAES::base64url_encode($uri); } else { throw new \Exception($file->getErrorString()); } } } else { $file = $fileInput; if ($file->isValid() && !$file->hasMoved()) { $fileExt = $file->guessExtension(); // Buat folder jika belum ada if (!is_dir(PATH_IMAGES_SERVER . $path . '/')) { mkdir(PATH_IMAGES_SERVER . $path . '/', 0775, TRUE); } // **Jika file adalah gambar, konversi ke WebP** if (in_array($fileExt, $allowedImageExt)) { $fileName = DhivaAES::randomStr(5, true) . '.webp'; $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $options = []; WebPConvert::convert($file->getTempName(), $destination, $options); } // **Jika file adalah video, simpan sebagai MP4** elseif (in_array($fileExt, $allowedVideoExt)) { $fileName = DhivaAES::randomStr(5, true) . '.mp4'; $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $file->move(PATH_IMAGES_SERVER . $path . '/', $fileName); } // **Jika file adalah pdf, simpan sebagai PDF** elseif (in_array($fileExt, $allowedVFileExt)) { $fileName = DhivaAES::randomStr(5, true) . '.pdf'; $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $file->move(PATH_IMAGES_SERVER . $path . '/', $fileName); } // **Jika ekstensi tidak diperbolehkan, kirim respons error** else { // $this->response(GET, 'NOT_ACCEPTABLE'); log_message('error', "Extensi file tidak diperbolehkan: {$fileExt}"); return $this->destination; } $this->destination = DhivaAES::base64url_encode($uri); } else { throw new \Exception($file->getErrorString()); } } return $this->destination; } // public function createImage(string $path) // { // $allowedImageExt = ['jpg', 'png', 'jpeg']; // $allowedVideoExt = ['mp4']; // $imagefile = $this->request->getFiles(); // $this->destination = []; // Pastikan array ini ada untuk menyimpan file // if (is_array($_FILES['file']['name'])) { // foreach ($imagefile['file'] as $v => $file) { // if ($file->isValid() && !$file->hasMoved()) { // $fileExt = $file->guessExtension(); // // Buat folder jika belum ada // if (!is_dir(PATH_IMAGES_SERVER . $path . '/')) { // mkdir(PATH_IMAGES_SERVER . $path . '/', 0775, TRUE); // } // // **Jika file adalah gambar, konversi ke WebP** // if (in_array($fileExt, $allowedImageExt)) { // $fileName = DhivaAES::randomStr(5, true) . '.webp'; // $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; // $uri = PATH_IMAGES . $path . '/' . $fileName; // $options = []; // WebPConvert::convert($file->getTempName(), $destination, $options); // } // // **Jika file adalah video, simpan sebagai MP4** // elseif (in_array($fileExt, $allowedVideoExt)) { // $fileName = DhivaAES::randomStr(5, true) . '.mp4'; // $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; // $uri = PATH_IMAGES . $path . '/' . $fileName; // $file->move(PATH_IMAGES_SERVER . $path . '/', $fileName); // } // // **Jika ekstensi tidak diperbolehkan, kirim respons error** // else { // log_message('error', "Extensi file tidak diperbolehkan: {$fileExt}"); // continue; // // // throw new \Exception('Extensi file tidak diperbolehkan!'); // // $this->response(GET, 'NOT_ACCEPTABLE'); // } // $this->destination[$v] = DhivaAES::base64url_encode($uri); // } else { // $this->response(GET, $file->getErrorString()); // } // } // } else { // $file = $this->request->getFile('file'); // if ($file->isValid() && !$file->hasMoved()) { // $fileExt = $file->guessExtension(); // // Buat folder jika belum ada // if (!is_dir(PATH_IMAGES_SERVER . $path . '/')) { // mkdir(PATH_IMAGES_SERVER . $path . '/', 0775, TRUE); // } // // **Jika file adalah gambar, konversi ke WebP** // if (in_array($fileExt, $allowedImageExt)) { // $fileName = DhivaAES::randomStr(5, true) . '.webp'; // $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; // $uri = PATH_IMAGES . $path . '/' . $fileName; // $options = []; // WebPConvert::convert($file->getTempName(), $destination, $options); // } // // **Jika file adalah video, simpan sebagai MP4** // elseif (in_array($fileExt, $allowedVideoExt)) { // $fileName = DhivaAES::randomStr(5, true) . '.mp4'; // $destination = PATH_IMAGES_SERVER . $path . '/' . $fileName; // $uri = PATH_IMAGES . $path . '/' . $fileName; // $file->move(PATH_IMAGES_SERVER . $path . '/', $fileName); // } // // **Jika ekstensi tidak diperbolehkan, kirim respons error** // else { // // $this->response(GET, 'NOT_ACCEPTABLE'); // log_message('error', "Extensi file tidak diperbolehkan: {$fileExt}"); // return $this->destination; // } // $this->destination = DhivaAES::base64url_encode($uri); // } else { // $this->response(GET, $file->getErrorString()); // } // } // return $this->destination; // } public function createAvatar(string $path, ?string $customFileName = null) { $allowedExt = ['jpg', 'jpeg', 'png', 'webp']; try { $img = $this->request->getFile('file'); if (!$img) { log_message('error', 'Tidak ada file yang diterima.'); return null; } // Cek error upload if ($img->hasMoved()) { log_message('error', 'File sudah dipindahkan sebelumnya.'); return null; } if ($img->getError() !== UPLOAD_ERR_OK) { log_message('error', 'Error upload: ' . $img->getError()); return null; } // Ambil ekstensi dari MIME type atau nama file $clientExt = strtolower($img->guessExtension()); $mimeExtMap = [ 'image/jpeg' => 'jpg', 'image/jpg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp' ]; $mimeExt = $mimeExtMap[strtolower($img->getMimeType())] ?? null; if (!in_array($clientExt, $allowedExt) && !in_array($mimeExt, $allowedExt)) { log_message('error', "Ekstensi tidak didukung: {$clientExt} (MIME: {$img->getMimeType()})"); return null; } // Buat direktori $finalDir = PATH_IMAGES_SERVER . $path; if (!is_dir($finalDir)) mkdir($finalDir, 0775, true); // Simpan sebagai webp $fileName = ($customFileName ?? Uuid::uuid6()->toString()) . '.webp'; $destination = $finalDir . '/' . $fileName; $uri = PATH_IMAGES . $path . '/' . $fileName; $options = []; WebPConvert::convert($img->getTempName(), $destination, $options); return DhivaAES::base64url_encode($uri); } catch (\Exception $e) { log_message('error', 'Gagal simpan gambar: ' . $e->getMessage() . ' ' . $e->getLine()); return null; } } protected function getExtensionFromMimeType($mimeType) { $mimeType = strtolower(trim($mimeType)); $map = [ 'image/jpeg' => 'jpg', 'image/jpg' => 'jpg', 'image/png' => 'png', 'image/x-png' => 'png', 'image/webp' => 'webp', 'application/pdf' => 'pdf' ]; return $map[$mimeType] ?? null; } protected function callExternalApi($endpoint, $payload) { // $url = "https://fr.tampilkan.com/{$endpoint}"; $url = $this->externalApi->linkFoto . $endpoint; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: application/json' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { throw new \Exception("cURL Error: {$error}"); } $decoded = json_decode($response, true); $isSuccess = false; if ($httpCode == 200) { // Cek struktur respons API if (isset($decoded['data']['status']) && $decoded['data']['status'] === 'success') { $isSuccess = true; } } return [ 'success' => $isSuccess, 'data' => $decoded, 'httpCode' => $httpCode ]; } protected function downloadSippPhotoToTemp($sippUrl) { if (!$sippUrl) { log_message('error', 'SIPP URL kosong'); return false; } $client = \Config\Services::curlrequest(); try { // Download dari SIPP $response = $client->get($sippUrl, ['timeout' => 10]); if ($response->getStatusCode() !== 200) { log_message('error', "Gagal download dari SIPP: {$sippUrl}"); return false; } // Buat temp directory $tempDir = WRITEPATH . 'temp/'; if (!is_dir($tempDir)) { mkdir($tempDir, 0775, true); } // Generate unique temp filename $tempFile = $tempDir . 'sipp_' . uniqid() . '_' . time() . '.jpg'; // Simpan ke temp file file_put_contents($tempFile, $response->getBody()); if (!file_exists($tempFile) || filesize($tempFile) === 0) { log_message('error', 'Temp file kosong atau gagal dibuat'); return false; } log_message('info', "Download SIPP photo ke temp berhasil: {$tempFile}"); return $tempFile; } catch (\Exception $e) { log_message('error', 'Download SIPP photo gagal: ' . $e->getMessage()); return false; } } protected function convertWebpToJpg($webpPath) { if (!file_exists($webpPath)) { log_message('error', 'File WebP tidak ditemukan: ' . $webpPath); return false; } // Simpan ke temporary directory $tempDir = WRITEPATH . 'temp/'; if (!is_dir($tempDir)) { mkdir($tempDir, 0775, true); } // Generate unique filename untuk temp file $jpgPath = $tempDir . 'avatar_' . uniqid() . '_' . time() . '.jpg'; // Coba load sebagai WebP $image = @imagecreatefromwebp($webpPath); if (!$image) { // Jika gagal, coba anggap sebagai JPG/PNG $info = @getimagesize($webpPath); if ($info && $info[2] === IMAGETYPE_JPEG) { copy($webpPath, $jpgPath); log_message('info', 'File sudah JPG, copy ke temp: ' . $jpgPath); return $jpgPath; } log_message('error', 'Gagal load image dari: ' . $webpPath); return false; } // Simpan sebagai JPG di temp directory $result = imagejpeg($image, $jpgPath, 90); imagedestroy($image); if (!$result) { log_message('error', 'Gagal simpan JPG ke: ' . $jpgPath); return false; } log_message('info', 'Konversi WebP ke JPG temp berhasil: ' . $jpgPath); return $jpgPath; } protected function saveAvatarFromUrl($url, $nrp, $customFileName = null) { if (!$url) return null; $client = \Config\Services::curlrequest(); try { $response = $client->get($url, ['timeout' => 10]); if ($response->getStatusCode() !== 200) { log_message('error', "Gagal download avatar: {$url}"); return null; } $contentType = $response->getHeaderLine('Content-Type'); log_message('debug', "Content-Type: {$contentType}"); $ext = $this->getExtensionFromMimeType($contentType); log_message('debug', "Ekstensi: {$ext}"); $allowedExt = ['jpg', 'jpeg', 'png', 'webp']; if (!$ext || !in_array($ext, $allowedExt)) { log_message('warning', "Format tidak didukung: {$contentType}"); return null; } $tempDir = WRITEPATH . 'temp/'; if (!is_dir($tempDir)) mkdir($tempDir, 0775, true); $tempFile = $tempDir . $customFileName . '.' . $ext; file_put_contents($tempFile, $response->getBody()); $uploadedFile = new UploadedFile( $tempFile, $customFileName . '.' . $ext, $contentType, filesize($tempFile), UPLOAD_ERR_OK, true ); $_FILES['file'] = [ 'name' => $uploadedFile->getName(), 'type' => $uploadedFile->getMimeType(), 'tmp_name' => $uploadedFile->getTempName(), 'error' => $uploadedFile->getError(), 'size' => $uploadedFile->getSize() ]; $this->request->setGlobal('files', $_FILES); $time = new \CodeIgniter\I18n\Time(); $monthYear = $time->format('F_Y'); $fullPath = "avatars/"; $fileName = "avatar_{$nrp}"; $result = $this->createAvatar($fullPath, $fileName); unlink($tempFile); return $result; } catch (\Exception $e) { log_message('error', 'Save avatar gagal: ' . $e->getMessage() . ' ' . $e->getLine()); if (isset($tempFile) && file_exists($tempFile)) { unlink($tempFile); } return null; } } public function cekToken() { $this->response(GET, 'active'); } public function paginationGet( $builder, $select, $defaultOrderBy, $defaultSort, $searchFields = [], $filters = [], $transformer = null ) { // $request = service('request'); $limit = $this->post('limit'); $page = (int) ($this->post('page') ?? 1); $search = $this->post('search'); $orderby = $this->post('orderBy') ?? $defaultOrderBy; $sort = strtoupper($this->post('sort') ?? $defaultSort); // Date range parameters $dateFrom = $this->post('date_from'); $dateTo = $this->post('date_to'); $dateField = $this->post('date_field'); // Validate sort direction if (!in_array($sort, ['ASC', 'DESC'])) { $sort = $defaultSort; } // Validate page $page = max(1, $page); // Jika limit tidak diberikan atau 0, maka ambil semua data $isUnlimited = ($limit === null || $limit === '' || (int)$limit === 0); if (!$isUnlimited) { $limit = max(1, min((int)$limit, 100)); } // Clone builder for counting $countBuilder = clone $builder; $builder->select($select); // Search logic - search across multiple fields if ($search && !empty($searchFields)) { $keywords = explode(' ', trim($search)); $safeKeywords = array_map([$this->db, 'escapeLikeString'], $keywords); $builder->groupStart(); $countBuilder->groupStart(); foreach ($safeKeywords as $keyword) { $builder->groupStart(); $countBuilder->groupStart(); foreach ($searchFields as $field) { $builder->orWhere("{$field} ILIKE '%{$keyword}%'", null, false); $countBuilder->orWhere("{$field} ILIKE '%{$keyword}%'", null, false); } $builder->groupEnd(); $countBuilder->groupEnd(); } $builder->groupEnd(); $countBuilder->groupEnd(); } // Date range filter if ($dateFrom || $dateTo) { // Default date field jika tidak dispesifikasi $defaultDateField = 'p.created_at'; $targetDateField = $dateField ?: $defaultDateField; if ($dateFrom && $dateTo) { $tglAwal = new DateTime($dateFrom); $tglAkhir = new DateTime($dateTo); $selisih = $tglAwal->diff($tglAkhir); if ($selisih->days > 13) { $this->response(GET, 'Range tanggal tidak boleh lebih dari 14 hari'); } // Range: dari tanggal A sampai tanggal B $builder->where("{$targetDateField} >=", $dateFrom); $builder->where("{$targetDateField} <=", $dateTo . ' 23:59:59'); $countBuilder->where("{$targetDateField} >=", $dateFrom); $countBuilder->where("{$targetDateField} <=", $dateTo . ' 23:59:59'); } elseif ($dateFrom) { // Dari tanggal tertentu ke atas $builder->where("{$targetDateField} >=", $dateFrom); $countBuilder->where("{$targetDateField} >=", $dateFrom); } elseif ($dateTo) { // Sampai tanggal tertentu $builder->where("{$targetDateField} <=", $dateTo . ' 23:59:59'); $countBuilder->where("{$targetDateField} <=", $dateTo . ' 23:59:59'); } } // Apply filters foreach ($filters as $filter) { if (is_array($filter)) { $field = $filter['field']; $value = $filter['value']; $operator = $filter['operator'] ?? '='; if ($value !== null && $value !== '') { $this->applyFilter($builder, $field, $value, $operator); $this->applyFilter($countBuilder, $field, $value, $operator); } } } // Apply ordering $builder->orderBy($orderby, $sort); // Count total records $total_data = $countBuilder->countAllResults(false); // Jika unlimited, ambil semua data if ($isUnlimited) { $data = $builder->get()->getResult(); // Apply transformer if provided if ($transformer && is_callable($transformer)) { $data = array_map($transformer, $data); } // Build pagination info untuk unlimited $paginationInfo = [ 'total_data' => $total_data, 'total_pages' => 1, 'current_page' => 1, 'per_page' => $total_data, 'prev_page' => null, 'next_page' => null, 'has_prev' => false, 'has_next' => false, 'from' => $total_data > 0 ? 1 : 0, 'to' => $total_data, 'is_unlimited' => true, 'date_filter' => [ 'date_from' => $dateFrom, 'date_to' => $dateTo, 'date_field' => $dateField ] ]; } else { // Logic pagination normal $total_pages = ceil($total_data / $limit); $offset = ($page - 1) * $limit; // Get paginated data // $builder->limit($limit, $offset); $data = $builder->get()->getResult(); // Apply transformer if provided if ($transformer && is_callable($transformer)) { $data = array_map($transformer, $data); } // Build pagination info $paginationInfo = [ 'total_data' => $total_data, 'total_pages' => $total_pages, 'current_page' => $page, 'per_page' => $limit, 'prev_page' => $page > 1 ? $page - 1 : null, 'next_page' => $page < $total_pages ? $page + 1 : null, 'has_prev' => $page > 1, 'has_next' => $page < $total_pages, 'from' => $total_data > 0 ? $offset + 1 : 0, 'to' => min($offset + $limit, $total_data), 'is_unlimited' => false, 'date_filter' => [ 'date_from' => $dateFrom, 'date_to' => $dateTo, 'date_field' => $dateField ] ]; } return [ 'data' => $data, 'pagination' => $paginationInfo ]; } public function customPagination($data, $page, $limit) { $total = count($data); $total_pages = ceil($total / $limit); $offset = ($page - 1) * $limit; $data = array_slice($data, $offset, $limit); $response = [ 'success' => true, 'code' => 200, 'message' => 'Data berhasil ditemukan.', 'pagination' => [ 'total_data' => $total, 'total_pages' => $total_pages, 'current_page' => $page, 'per_page' => $limit, 'prev_page' => $page > 1 ? $page - 1 : null, 'next_page' => $page < $total_pages ? $page + 1 : null, 'from' => $total > 0 ? $offset + 1 : 0, 'to' => min($offset + $limit, $total), ], 'data' => $data, ]; return $this->response->setJSON($response); } private function applyFilter($builder, $field, $value, $operator) { switch (strtoupper($operator)) { case 'LIKE': $builder->like($field, $value); break; case 'NOT LIKE': $builder->notLike($field, $value); break; case 'ILIKE': $builder->where("{$field} ILIKE", "%{$value}%"); break; case 'NOT ILIKE': $builder->where("{$field} NOT ILIKE", "%{$value}%"); break; case 'IN': $builder->whereIn($field, is_array($value) ? $value : explode(',', $value)); break; case 'NOT IN': $builder->whereNotIn($field, is_array($value) ? $value : explode(',', $value)); break; case 'BETWEEN': if (is_array($value) && count($value) === 2) { $builder->where("{$field} BETWEEN ? AND ?", [$value[0], $value[1]]); } break; case 'DATE_RANGE': // Khusus untuk date range dengan format array [start_date, end_date] if (is_array($value) && count($value) === 2) { $builder->where("{$field} >=", $value[0]); $builder->where("{$field} <=", $value[1] . ' 23:59:59'); } break; case 'OR': // Khusus untuk OR conditions dengan format array of conditions if (is_array($value) && !empty($value)) { $builder->groupStart(); $first = true; foreach ($value as $condition) { if (!$first) { $builder->orWhere($condition['field'], $condition['value']); } else { $builder->where($condition['field'], $condition['value']); $first = false; } } $builder->groupEnd(); } break; case 'IS NULL': $builder->where("{$field} IS NULL", null, false); break; case 'IS NOT NULL': $builder->where("{$field} IS NOT NULL", null, false); break; case '>': case '<': case '>=': case '<=': case '!=': case '<>': $builder->where("{$field} {$operator}", $value); break; default: $builder->where($field, $value); } } // protected function saveLogAktivitas($jenisLog, $deskripsi = null, $nrp = null) // { // try { // date_default_timezone_set('Asia/Makassar'); // $waktuSekarang = date('d-m-Y H:i:s'); // // Jika deskripsi tidak diberikan, generate otomatis // if ($deskripsi === null) { // switch ($jenisLog) { // case 'login': // $deskripsi = "User dengan NRP {$nrp} telah berhasil melakukan login pada pukul {$waktuSekarang}"; // break; // case 'register': // $deskripsi = "User dengan NRP {$nrp} telah berhasil melakukan registrasi pada pukul {$waktuSekarang}"; // break; // case 'verifikasi_wajah_manual': // $deskripsi = "Super Admin telah melakukan verifikasi wajah manual untuk user dengan NRP {$nrp} pada pukul {$waktuSekarang}"; // break; // case 'reset_verifikasi_wajah': // $deskripsi = "Super Admin telah melakukan reset verifikasi wajah untuk user dengan NRP {$nrp} pada pukul {$waktuSekarang}"; // break; // case 'tolak_verifikasi_wajah': // $deskripsi = "Super Admin telah menolak verifikasi wajah dan menghapus akun user dengan NRP {$nrp} pada pukul {$waktuSekarang}"; // break; // case 'cek_data_by_nik': // $deskripsi = "Pencarian data pelanggaran berdasarkan NIK dilakukan pada pukul {$waktuSekarang}"; // break; // default: // $deskripsi = "Aktivitas {$jenisLog} dilakukan pada pukul {$waktuSekarang}"; // break; // } // } // $dataLog = [ // 'jenis_log' => $jenisLog, // 'deskripsi' => $deskripsi, // 'created_at' => date('Y-m-d H:i:s') // ]; // $this->db->table('jenis_log_aktivitas')->insert($dataLog); // return true; // } catch (\Exception $e) { // log_message('error', 'Error save log aktivitas: ' . $e->getMessage()); // return false; // } // } protected function saveLogAktivitas($jenisLogId, $isiLog = null, $nrp = null, $nik = null, $deskripsi = null) { try { date_default_timezone_set('Asia/Makassar'); $tanggalSekarang = date('d-m-Y'); $jamSekarang = date('H:i:s'); // $waktuSekarang = date('d-m-Y H:i:s'); // Ambil jenis_log_aktivitas_id dari tabel jenis_log_aktivitas $jenisLogData = $this->db->table('jenis_log_aktivitas') ->where('jenis_log_aktivitas_id', $jenisLogId) ->get() ->getRow(); if (!$jenisLogData) { log_message('error', "Jenis log '{$jenisLogId}' tidak ditemukan di tabel jenis_log_aktivitas"); return false; } // Jika deskripsi tidak diberikan, generate otomatis if ($isiLog === null) { switch ($jenisLogId) { case 1: $isiLog = "User dengan NRP {$nrp} telah berhasil melakukan login pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 2: $isiLog = "User dengan NRP {$nrp} telah berhasil melakukan registrasi pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 3: $isiLog = "User dengan NRP {$nrp} telah berhasil melakukan verifikasi wajah dengan data wajah di SIPP pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 4: $isiLog = "Super Admin telah melakukan verifikasi wajah manual untuk user dengan NRP {$nrp} pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 5: $isiLog = "Super Admin telah melakukan reset verifikasi wajah untuk user dengan NRP {$nrp} pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 6: $isiLog = "Super Admin telah menolak verifikasi wajah dan menghapus akun user dengan NRP {$nrp} pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 7: $isiLog = "Pencarian data pelanggaran dengan NIK {$nik} telah berhasil dilakukan oleh user dengan NRP {$nrp} pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 8: $isiLog = "Pencarian data pelanggar dengan NIK {$nik} telah berhasil dilakukan oleh user dengan NRP {$nrp} pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 9: $isiLog = "User dengan NRP {$nrp} telah berhasil melakukan update password pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; case 10: $isiLog = "Super Admin telah melakukan verifikasi OTP manual untuk user dengan NRP {$nrp} pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; default: $isiLog = "Aktivitas {$jenisLogId} dilakukan pada tanggal {$tanggalSekarang} pukul {$jamSekarang}"; break; } } // Insert ke tabel log_aktivitas $dataLog = [ 'jenis_log_aktivitas_id' => $jenisLogData->jenis_log_aktivitas_id, 'isi_log' => $isiLog, 'deskripsi' => $deskripsi, 'created_at' => date('Y-m-d H:i:s') ]; $this->db->table('log_aktivitas')->insert($dataLog); return true; } catch (\Exception $e) { log_message('error', 'Error save log aktivitas: ' . $e->getMessage()); return false; } } protected function responseOK($payload = [], $code = 200) { return $this->response ->setStatusCode($code) ->setJSON(array_merge(['code' => $code, 'status' => 'success'], $payload)); } protected function responseError($payload = [], $code = 400) { return $this->response ->setStatusCode($code) ->setJSON(array_merge(['code' => $code, 'status' => 'error'], $payload)); } public function calculateDistribusi($penjualan_id) { try { $this->db->transBegin(); $dataPenjualan = $this->db->table('public.penjualan')->where('penjualan_id', $penjualan_id)->get()->getRowArray(); $rowBiayaOperasional = $this->db->table('public.biaya_operasional') ->select([ "SUM(total::numeric) AS total", "SUM(realisasi::numeric) AS realisasi", "SUM(sisa::numeric) AS sisa" ]) ->where('panen_id', $dataPenjualan['panen_id']) ->where('is_deleted', false) ->where('status', 'Approved') ->get() ->getRow(); $totalBiayaOperasional = $rowBiayaOperasional->total ?? 0; //panen $panen = $this->db->table('public.panen') ->join('public.produksi', 'panen.produksi_id = produksi.produksi_id', 'left') ->join('public.kolam', 'produksi.kolam_id = kolam.kolam_id', 'left') ->join('public.sub_blok', 'kolam.sub_blok_id = sub_blok.sub_blok_id', 'left') ->select(['panen.hasil_produksi', 'sub_blok.sub_blok_id']) ->where('panen.panen_id', $dataPenjualan['panen_id'])->get()->getRowArray(); $pemurnian = $this->db->table('public.pemurnian') ->where('panen_id', $dataPenjualan['panen_id']) ->orderBy('created_at', 'DESC') ->get()->getRowArray(); $listKoperasi = array_column($this->db->table('public.mapping_sub_blok_koperasi') ->where('sub_blok_id', $panen['sub_blok_id']) ->select('koperasi_id') ->get() ->getResultArray(), 'koperasi_id'); $anggotaPembina = $this->db->table('public.anggota_koperasi') ->whereIn('koperasi_id', $listKoperasi) ->where('jenis_anggota_id', 1) ->select(['nama', 'alamat']) ->get()->getResultArray(); $anggotaKoperasiPembina = count($anggotaPembina); $anggotaPengawas = $this->db->table('public.anggota_koperasi') ->whereIn('koperasi_id', $listKoperasi) ->where('jenis_anggota_id', 2) ->select(['nama', 'alamat']) ->get()->getResultArray(); $anggotaKoperasiPengawas = count($anggotaPengawas); $anggotaPengurus = $this->db->table('public.anggota_koperasi') ->whereIn('koperasi_id', $listKoperasi) ->where('jenis_anggota_id', 3) ->select(['nama', 'alamat']) ->get()->getResultArray(); $anggotaKoperasiPengurus = count($anggotaPengurus); $anggota = $this->db->table('public.anggota_koperasi') ->whereIn('koperasi_id', $listKoperasi) ->where('jenis_anggota_id', 4) ->select(['nama', 'alamat']) ->get()->getResultArray(); $anggotaKoperasi = count($anggota); $pemilik = $this->db->table('public.mapping_sub_blok_anggota') ->join('public.anggota_koperasi', 'mapping_sub_blok_anggota.anggota_koperasi_id = anggota_koperasi.anggota_koperasi_id', 'left') ->where('sub_blok_id', $panen['sub_blok_id']) ->select(['anggota_koperasi.nama', 'anggota_koperasi.alamat']) ->get()->getResultArray(); $pemilikLahan = count($pemilik); if (empty($anggotaKoperasiPembina) && empty($anggotaKoperasiPengawas) && empty($anggotaKoperasiPengurus) && empty($anggotaKoperasi) && empty($pemilikLahan)) { return "Tidak Ada Anggota Koperasi!"; } if(empty($pemurnian)){ $hasil_produksi = $panen['hasil_produksi']; }else { $hasil_produksi = $pemurnian['hasil_produksi']; } // rumus $pendapatan_bruto = $dataPenjualan['pendapatan_bruto']; $pajakPenjualanEmas = (PAJAK_PENJUALAN_EMAS / 100) * $pendapatan_bruto; //realisasi pajak $realisasiPajakPenjualan = $this->getPajak($dataPenjualan['panen_id'], 1); $iperaPengusahaan = (IPERA_PENGUSAHAAN / 100) * $pendapatan_bruto; $realisasiIperaPengusahaan = $this->getIperaPengusahaan($dataPenjualan['panen_id']); $sisaDana = $pendapatan_bruto - ((int)$realisasiPajakPenjualan['total'] ?? $pajakPenjualanEmas) - ((int)$realisasiIperaPengusahaan['total'] ?? $iperaPengusahaan); $labaKenaPajak = $sisaDana - $totalBiayaOperasional; //realisasi pajak $realisasiPphBadan = $this->getPajak($dataPenjualan['panen_id'], 2); $pphBadan = $labaKenaPajak * 22 / 100; $labaBersih = $labaKenaPajak - ((int)$realisasiPphBadan['total'] ?? $pphBadan); $koperasi = round(($labaBersih * (KOPERASI / 100)), 2); // SHU KOPERASI $zakat = round(($koperasi * (SHU_KOPERASI_ZAKAT / 100)), 2); $csr = round(($koperasi * (2.5 / 100)), 2); $sisaDanaKoperasi = round(($koperasi - $zakat - $csr), 2); $sisaDanaCadangan = round($sisaDanaKoperasi * (50 / 100), 2); $shuAgt = round($sisaDanaKoperasi * (50 / 100), 2); $jumlahAnggota = $anggotaKoperasiPembina + $anggotaKoperasiPengawas + $anggotaKoperasiPengurus + $anggotaKoperasi; $shu_koperasi_id = $this->db->table('public.shu_koperasi')->where('panen_id', $dataPenjualan['panen_id'])->get()->getRowArray(); if(!empty($shu_koperasi_id)){ $checkExistingShuKoperasiDetail = $this->db->table('public.shu_koperasi_detail') ->where('shu_koperasi_id', $shu_koperasi_id['shu_koperasi_id']) ->where('name', 'shu_agt') ->get()->getRowArray(); } if(empty($checkExistingShuKoperasiDetail)) { $shuAgt = $shuAgt; }else { $shuAgt = $checkExistingShuKoperasiDetail['realisasi']; } if ($jumlahAnggota < 500) { $indexShu = ($shuAgt / (500 + 3)) / 3; } else { $indexShu = ($shuAgt / ($jumlahAnggota)) / 3; // dibagi 3 karena shu itu per 3 bulan, nah index shu itu per bulan } $indexShu = round($indexShu,2); $data = [ 'pendapatan_bruto' => $pendapatan_bruto, 'ipera_pengusahaan' => [ 'total' => $iperaPengusahaan, 'realisasi' => (int)$realisasiIperaPengusahaan['total'] ?? $iperaPengusahaan, 'sisa' => $iperaPengusahaan - ((int)$realisasiIperaPengusahaan['total'] ?? $iperaPengusahaan) ], 'sisa_dana' => $sisaDana, 'biaya_operasional' => [ 'total' => $totalBiayaOperasional, 'realisasi' => $rowBiayaOperasional->realisasi ?? 0, 'sisa' => $totalBiayaOperasional - $rowBiayaOperasional->realisasi ?? 0 ], 'pajak_penjualan_emas' => [ 'total' => $pajakPenjualanEmas, 'realisasi' => (float)$realisasiPajakPenjualan['total'] ?? 0, 'sisa' => $pajakPenjualanEmas - ((float)$realisasiPajakPenjualan['total'] ?? 0) ], 'laba_kena_pajak' => $labaKenaPajak, 'pph_badan' => [ 'total' => $pphBadan, 'realisasi' => (float)$realisasiPphBadan['total'] ?? 0, 'sisa' => $pphBadan - ((float)$realisasiPphBadan['total'] ?? 0) ], 'laba_bersih' => $labaBersih, 'distribusi_shu' => [ 'mitra_kerja' => [ 'total' => round(($labaBersih * (MITRA_KERJA / 100)), 2), 'realisasi' => 0, 'sisa' => 0 ], 'manajemen' => [ 'total' => round(($labaBersih * (MANAJEMEN / 100)), 2), 'realisasi' => 0, 'sisa' => 0 ], 'pemda' => [ 'total' => round(($labaBersih * (PEMDA / 100)), 2), 'realisasi' => 0, 'sisa' => 0 ], 'koperasi' => [ 'total' => $koperasi, 'realisasi' => 0, 'sisa' => 0 ] ], 'shu_koperasi' => [ 'zakat' => [ 'total' => $zakat, 'realisasi' => $zakat, 'sisa' => 0 ], 'csr' => [ 'total' => $csr, 'realisasi' => $csr, 'sisa' => 0 ], 'sisa_dana_koperasi' => [ 'total' => $sisaDanaCadangan, 'realisasi' => $sisaDanaCadangan, 'sisa' => 0 ], 'dana_cadangan' => [ 'total' => $sisaDanaCadangan, 'realisasi' => $sisaDanaCadangan, 'sisa' => 0 ], 'shu_agt' => [ 'total' => $shuAgt, 'realisasi' => !empty($checkExistingShuKoperasiDetail) ? $checkExistingShuKoperasiDetail['realisasi'] : $shuAgt, 'sisa' => !empty($checkExistingShuKoperasiDetail) ? $checkExistingShuKoperasiDetail['sisa'] : 0 ], 'shu_koperasi_id' => $shu_koperasi_id['shu_koperasi_id'] ?? Uuid::uuid4()->toString() ], 'shu_agt_koperasi' => [ 'index_shu' => $indexShu, 'jumlah_total_anggota' => $anggotaKoperasiPembina + $anggotaKoperasiPengawas + $anggotaKoperasiPengurus + $anggotaKoperasi, 'pembina' => [ 'jumlah_anggota' => $anggotaKoperasiPembina, 'keterangan' => '3x Index SHU', 'jumlah_diperoleh' => $indexShu * 3, 'total_shu_pembina' => ($indexShu * 3) * $anggotaKoperasiPembina, 'anggota' => $anggotaPembina ], 'pengawas' => [ 'jumlah_anggota' => $anggotaKoperasiPengawas, 'keterangan' => '3x Index SHU', 'jumlah_diperoleh' => $indexShu * 3, 'total_shu_pengawas' => ($indexShu * 3) * $anggotaKoperasiPengawas, 'anggota' => $anggotaPengawas ], 'pengurus' => [ 'jumlah_anggota' => $anggotaKoperasiPengurus, 'keterangan' => '3x Index SHU', 'jumlah_diperoleh' => $indexShu * 3, 'total_shu_pengurus' => ($indexShu * 3) * $anggotaKoperasiPengurus, 'anggota' => $anggotaPengurus ], 'anggota' => [ 'jumlah_anggota' => $anggotaKoperasi, 'keterangan' => '', 'jumlah_diperoleh' => $indexShu < 2800000 ? 2500000 : 2800000, 'total_shu_anggota' => ($indexShu < 2800000 ? 2500000 : 2800000) * $anggotaKoperasi, 'anggota' => $anggota ], 'pemilik_lahan' => [ 'jumlah_anggota' => $pemilikLahan, 'keterangan' => '3x Index SHU', 'jumlah_diperoleh' => $indexShu * 3, 'total_shu_pemilik_lahan' => ($indexShu * 3) * ($pemilikLahan), 'anggota' => $pemilik ], ] ]; $totalShuTerbagi = $data['shu_agt_koperasi']['pembina']['total_shu_pembina'] + $data['shu_agt_koperasi']['pengawas']['total_shu_pengawas'] + $data['shu_agt_koperasi']['pengurus']['total_shu_pengurus'] + $data['shu_agt_koperasi']['anggota']['total_shu_anggota'] + $data['shu_agt_koperasi']['pemilik_lahan']['total_shu_pemilik_lahan']; $sisaShu = $shuAgt - $totalShuTerbagi; $jumlahPenerimaSisaShu = floor($sisaShu / 1000000); //masukan data masyarakat $data['shu_agt_koperasi']['masyarakat'] = [ 'jumlah_anggota' => $jumlahPenerimaSisaShu, 'keterangan' => '', 'jumlah_diperoleh' => 1000000, 'total_shu_masyarakat' => $jumlahPenerimaSisaShu * 1000000 ]; //UPDATE PENJUALAN $this->db->table('public.penjualan')->where('penjualan_id', $penjualan_id)->update([ 'pajak_penjualan_emas' => $data['pajak_penjualan_emas']['realisasi'], 'ipera_pengusahaan' => $data['ipera_pengusahaan']['realisasi'], 'sisa_dana' => $data['sisa_dana'], 'biaya_operasional' => $data['biaya_operasional']['realisasi'], 'laba_kena_pajak' => $data['laba_kena_pajak'], 'pph_badan' => $data['pph_badan']['realisasi'], 'laba_bersih' => $data['laba_bersih'], ]); //INSERT AREA $this->helperCalculateDistribusi($penjualan_id, $data, $dataPenjualan); $this->db->transCommit(); return $data; } catch (\Throwable $th) { $this->db->transRollback(); $this->response(GET, $th->getMessage().$th->getLine()); } } private function helperCalculateDistribusi($penjualan_id, $data, $dataPenjualan) { $checExistingDistribusi = $this->db->table('public.distribusi_shu')->where('penjualan_id', $penjualan_id)->get()->getRowArray(); if(empty($checExistingDistribusi)) { $distribusiShuId = Uuid::uuid4()->toString(); $this->db->table('public.distribusi_shu')->insert([ 'distribusi_shu_id' => $distribusiShuId, 'penjualan_id' => $penjualan_id, 'panen_id' => $dataPenjualan['panen_id'], 'mitra_kerja' => $data['distribusi_shu']['mitra_kerja']['total'], 'manajemen' => $data['distribusi_shu']['manajemen']['total'], 'pemda' => $data['distribusi_shu']['pemda']['total'], 'koperasi' => $data['distribusi_shu']['koperasi']['total'], 'jumlah_distribusi' => $data['laba_bersih'], 'created_at' => date('Y-m-d H:i:s') ]); //insert shu koperasi $shuKoperasiId = $data['shu_koperasi']['shu_koperasi_id']; $this->db->table('public.shu_koperasi')->insert([ 'shu_koperasi_id' => $shuKoperasiId, 'distribusi_shu_id' => $distribusiShuId, 'panen_id' => $dataPenjualan['panen_id'], 'zakat' => $data['shu_koperasi']['zakat']['realisasi'], 'csr' => $data['shu_koperasi']['csr']['realisasi'], 'sisa_dana_koperasi' => $data['shu_koperasi']['sisa_dana_koperasi']['realisasi'], 'dana_cadangan' => $data['shu_koperasi']['dana_cadangan']['realisasi'], 'shu_agt' => $data['shu_koperasi']['shu_agt']['realisasi'], 'created_at' => date('Y-m-d H:i:s') ]); $this->db->table('public.shu_koperasi_detail')->insert([ 'shu_koperasi_id' => $shuKoperasiId, 'name' => 'shu_agt', 'total' => $data['shu_koperasi']['shu_agt']['total'], 'realisasi' => $data['shu_koperasi']['shu_agt']['total'], 'sisa' => 0, ]); //insert shu agt koperasi $shuAgtKoperasiId = Uuid::uuid4()->toString(); $this->db->table('public.shu_agt_koperasi')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'shu_koperasi_id' => $shuKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'index_shu' => $data['shu_agt_koperasi']['index_shu'], 'jumlah_total_anggota' => $data['shu_agt_koperasi']['jumlah_total_anggota'], 'created_at' => date('Y-m-d H:i:s') ]); //insert shu agt koperasi detail $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => 'pembina', 'jumlah_anggota' => $data['shu_agt_koperasi']['pembina']['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi']['pembina']['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi']['pembina']['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi']['pembina']['total_shu_pembina'] ]); $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => 'pengawas', 'jumlah_anggota' => $data['shu_agt_koperasi']['pengawas']['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi']['pengawas']['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi']['pengawas']['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi']['pengawas']['total_shu_pengawas'] ]); $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => 'pengurus', 'jumlah_anggota' => $data['shu_agt_koperasi']['pengurus']['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi']['pengurus']['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi']['pengurus']['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi']['pengurus']['total_shu_pengurus'] ]); $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => 'pemilik_lahan', 'jumlah_anggota' => $data['shu_agt_koperasi']['pemilik_lahan']['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi']['pemilik_lahan']['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi']['pemilik_lahan']['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi']['pemilik_lahan']['total_shu_pemilik_lahan'] ]); $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => 'anggota', 'jumlah_anggota' => $data['shu_agt_koperasi']['anggota']['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi']['anggota']['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi']['anggota']['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi']['anggota']['total_shu_anggota'] ]); $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => 'masyarakat', 'jumlah_anggota' => $data['shu_agt_koperasi']['masyarakat']['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi']['masyarakat']['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi']['masyarakat']['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi']['masyarakat']['total_shu_masyarakat'] ]); //insert pajak $this->db->table('public.pajak')->insert([ 'penjualan_id' => $penjualan_id, 'pajak_type_id' => 1, 'total' => $data['pajak_penjualan_emas']['total'], 'sisa' => $data['pajak_penjualan_emas']['total'], 'realisasi' => 0, 'created_at' => date('Y-m-d H:i:s') ]); $this->db->table('public.pajak')->insert([ 'penjualan_id' => $penjualan_id, 'pajak_type_id' => 2, 'total' => $data['pph_badan']['total'], 'sisa' => $data['pph_badan']['total'], 'realisasi' => 0, 'created_at' => date('Y-m-d H:i:s') ]); }else { // ambil distribusi lama $existingDistribusi = $checExistingDistribusi; $distribusiShuId = $existingDistribusi['distribusi_shu_id']; // UPDATE distribusi_shu $this->db->table('public.distribusi_shu') ->where('distribusi_shu_id', $distribusiShuId) ->update([ 'mitra_kerja' => $data['distribusi_shu']['mitra_kerja']['total'], 'manajemen' => $data['distribusi_shu']['manajemen']['total'], 'pemda' => $data['distribusi_shu']['pemda']['total'], 'koperasi' => $data['distribusi_shu']['koperasi']['total'], 'jumlah_distribusi' => $data['laba_bersih'], 'updated_at' => date('Y-m-d H:i:s') ]); // ambil shu koperasi $shuKoperasi = $this->db->table('public.shu_koperasi') ->where('distribusi_shu_id', $distribusiShuId) ->get() ->getRowArray(); $shuKoperasiId = $shuKoperasi['shu_koperasi_id']; // UPDATE shu koperasi $this->db->table('public.shu_koperasi') ->where('shu_koperasi_id', $shuKoperasiId) ->update([ 'zakat' => $data['shu_koperasi']['zakat']['realisasi'], 'csr' => $data['shu_koperasi']['csr']['realisasi'], 'sisa_dana_koperasi' => $data['shu_koperasi']['sisa_dana_koperasi']['realisasi'], 'dana_cadangan' => $data['shu_koperasi']['dana_cadangan']['realisasi'], 'shu_agt' => $data['shu_koperasi']['shu_agt']['realisasi'], 'updated_at' => date('Y-m-d H:i:s') ]); // ambil shu agt koperasi $shuAgtKoperasi = $this->db->table('public.shu_agt_koperasi') ->where('shu_koperasi_id', $shuKoperasiId) ->get() ->getRowArray(); $shuAgtKoperasiId = $shuAgtKoperasi['shu_agt_koperasi_id']; // UPDATE header $this->db->table('public.shu_agt_koperasi') ->where('shu_agt_koperasi_id', $shuAgtKoperasiId) ->update([ 'index_shu' => $data['shu_agt_koperasi']['index_shu'], 'jumlah_total_anggota' => $data['shu_agt_koperasi']['jumlah_total_anggota'], 'updated_at' => date('Y-m-d H:i:s') ]); // 🔥 DELETE DETAIL (SUPER IMPORTANT) $this->db->table('public.shu_agt_koperasi_detail') ->where('shu_agt_koperasi_id', $shuAgtKoperasiId) ->delete(); // 🔥 INSERT ULANG DETAIL (BEST PRACTICE) $detailTypes = [ 'pembina' => 'total_shu_pembina', 'pengawas' => 'total_shu_pengawas', 'pengurus' => 'total_shu_pengurus', 'pemilik_lahan' => 'total_shu_pemilik_lahan', 'anggota' => 'total_shu_anggota', 'masyarakat' => 'total_shu_masyarakat' ]; foreach ($detailTypes as $type => $totalKey) { $this->db->table('public.shu_agt_koperasi_detail')->insert([ 'shu_agt_koperasi_id' => $shuAgtKoperasiId, 'panen_id' => $dataPenjualan['panen_id'], 'type' => $type, 'jumlah_anggota' => $data['shu_agt_koperasi'][$type]['jumlah_anggota'], 'keterangan' => $data['shu_agt_koperasi'][$type]['keterangan'], 'jumlah_diperoleh' => $data['shu_agt_koperasi'][$type]['jumlah_diperoleh'], 'total_shu' => $data['shu_agt_koperasi'][$type][$totalKey] ]); } } } private function getPajak($panen_id, $type) { return $this->db->table('public.pajak') ->join('public.penjualan', 'penjualan.penjualan_id = pajak.penjualan_id') ->where('penjualan.panen_id', $panen_id) ->where('pajak_type_id', $type) ->select('SUM(realisasi::numeric) as total') ->get()->getRowArray(); } private function getIperaPengusahaan($panen_id) { return $this->db->table('public.ipera') ->where('panen_id', $panen_id) ->where('ipera_type_id', 1) ->select('SUM(realisasi::numeric) as total') ->get()->getRowArray(); } }