pluck('value', 'key'); $this->email = trim((string) ($settings['SIMBG_EMAIL'] ?? "")); $this->password = trim((string) ($settings['SIMBG_PASSWORD'] ?? "")); $this->simbg_host = trim((string) ($settings['SIMBG_HOST'] ?? "")); $this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? "")); $this->service_client = new ServiceClient($this->simbg_host); $this->googleSheetService = $googleSheetService; } public function getToken(){ try{ $url = "/api/user/v1/auth/login/"; $body = [ 'email' => $this->email, 'password' => $this->password, ]; $res = $this->service_client->post($url, $body); if(!$res->original['success']){ Log::error("Token not retrieved ", ['response' => $res]); throw new Exception("Token not retrieved."); } return $res; }catch(Exception $e){ Log::error("Error on method get token ", ['response' => $e->getMessage()]); throw $e; } } public function syncIndexIntegration($uuids) { try{ if(empty($uuids)){ return false; } $initResToken = $this->getToken(); if (empty($initResToken->original['data']['token']['access'])) { Log::error("API response indicates failure", ['token' => 'Failed to retrieve token']); return false; } $token = $initResToken->original['data']['token']['access']; $integrations = []; foreach($uuids as $uuid){ $url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/"; $headers = [ 'Authorization' => "Bearer " . $token, ]; $res = $this->service_client->get($url, $headers); if (empty($res->original['success']) || !$res->original['success']) { // Log error Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]); continue; } $data = $res->original['data']['data'] ?? null; if (!$data) { Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]); continue; } $integrations[] = [ 'pbg_task_uid' => $uuid, 'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null, 'indeks_parameter_kompleksitas' => $data['indeks_parameter_kompleksitas'] ?? null, 'indeks_parameter_permanensi' => $data['indeks_parameter_permanensi'] ?? null, 'indeks_parameter_ketinggian' => $data['indeks_parameter_ketinggian'] ?? null, 'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null, 'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null, 'total' => $data['total'] ?? null, ]; } PbgTaskIndexIntegrations::upsert($integrations, ['pbg_task_uid'], ['indeks_fungsi_bangunan', 'indeks_parameter_kompleksitas', 'indeks_parameter_permanensi', 'indeks_parameter_ketinggian', 'faktor_kepemilikan', 'indeks_terintegrasi', 'total']); return true; }catch (Exception $e){ Log::error('error when sync index integration ', ['index integration'=> $e->getMessage()]); throw $e; } } public function syncTaskPBG() { try { Log::info("Processing google sheet sync"); $importDatasource = ImportDatasource::create([ 'status' => ImportDatasourceStatus::Processing->value, ]); // sync google sheet first $totalRowCount = $this->googleSheetService->getLastRowByColumn("C"); $sheetData = $this->googleSheetService->getSheetDataCollection($totalRowCount); $sheet_big_data = $this->googleSheetService->get_data_by_sheet(); $data_setting_result = []; // Initialize result storage $found_section = null; // Track which section is found foreach ($sheet_big_data as $row) { // Check for section headers if (in_array("•PROSES PENERBITAN:", $row)) { $found_section = "MENUNGGU_KLIK_DPMPTSP"; } elseif (in_array("•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2024:", $row)) { $found_section = "REALISASI_TERBIT_PBG"; } elseif (in_array("•TERPROSES DI DPUTR: belum selesai rekomtek'", $row)) { $found_section = "PROSES_DINAS_TEKNIS"; } // If a section is found and we reach "Grand Total", save the corresponding values if ($found_section && isset($row[0]) && trim($row[0]) === "Grand Total") { if ($found_section === "MENUNGGU_KLIK_DPMPTSP") { $data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $this->convertToInteger($row[2]) ?? null; $data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $this->convertToDecimal($row[3]) ?? null; } elseif ($found_section === "REALISASI_TERBIT_PBG") { $data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $this->convertToInteger($row[2]) ?? null; $data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $this->convertToDecimal($row[4]) ?? null; } elseif ($found_section === "PROSES_DINAS_TEKNIS") { $data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $this->convertToInteger($row[2]) ?? null; $data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $this->convertToDecimal($row[3]) ?? null; } // Reset section tracking after capturing "Grand Total" $found_section = null; } } Log::info("data setting result", ['result' => $data_setting_result]); foreach ($data_setting_result as $key => $value) { DataSetting::updateOrInsert( ["key" => $key], // Find by key ["value" => $value] // Update or insert value ); } $mapToUpsert = []; foreach ($sheetData as $data) { $mapToUpsert[] = [ 'no_registrasi' => $this->cleanString($data['no__registrasi'] ?? null), 'jenis_konsultasi' => $this->cleanString($data['jenis_konsultasi'] ?? null), 'fungsi_bg' => $this->cleanString($data['fungsi_bg'] ?? null), 'tgl_permohonan' => $this->convertToDate($this->cleanString($data['tgl_permohonan'] ?? null)), 'status_verifikasi' => $this->cleanString($data['status_verifikasi'] ?? null), 'status_permohonan' => $this->convertToDate($this->cleanString($data['status_permohonan'] ?? null)), 'alamat_pemilik' => $this->cleanString($data['alamat_pemilik'] ?? null), 'no_hp' => $this->cleanString($data['no__hp'] ?? null), 'email' => $this->cleanString($data['e_mail'] ?? null), 'tanggal_catatan' => $this->convertToDate($this->cleanString($data['tanggal_catatan'] ?? null)), 'catatan_kekurangan_dokumen' => $this->cleanString($data['catatan_kekurangan_dokumen'] ?? null), 'gambar' => $this->cleanString($data['gambar'] ?? null), 'krk_kkpr' => $this->cleanString($data['krk_kkpr'] ?? null), 'no_krk' => $this->cleanString($data['no__krk'] ?? null), 'lh' => $this->cleanString($data['lh'] ?? null), 'ska' => $this->cleanString($data['ska'] ?? null), 'keterangan' => $this->cleanString($data['keterangan'] ?? null), 'helpdesk' => $this->cleanString($data['helpdesk'] ?? null), 'pj' => $this->cleanString($data['pj'] ?? null), 'kepemilikan' => $this->cleanString($data['kepemilikan'] ?? null), 'potensi_taru' => $this->cleanString($data['potensi_taru'] ?? null), 'validasi_dinas' => $this->cleanString($data['validasi_dinas'] ?? null), 'kategori_retribusi' => $this->cleanString($data['kategori_retribusi'] ?? null), 'no_urut_ba_tpt' => $this->cleanString($data['no__urut_ba_tpt__2024_0001_'] ?? null), 'tanggal_ba_tpt' => $this->convertToDate($this->cleanString($data['tanggal_ba_tpt'] ?? null)), 'no_urut_ba_tpa' => $this->cleanString($data['no__urut_ba_tpa'] ?? null), 'tanggal_ba_tpa' => $this->convertToDate($this->cleanString($data['tanggal_ba_tpa'] ?? null)), 'no_urut_skrd' => $this->cleanString($data['no__urut_skrd__2024_0001_'] ?? null), 'tanggal_skrd' => $this->convertToDate($this->cleanString($data['tanggal_skrd'] ?? null)), 'ptsp' => $this->cleanString($data['ptsp'] ?? null), 'selesai_terbit' => $this->cleanString($data['selesai_terbit'] ?? null), 'tanggal_pembayaran' => $this->convertToDate($this->cleanString($data['tanggal_pembayaran__yyyy_mm_dd_'] ?? null)), 'format_sts' => $this->cleanString($data['format_sts'] ?? null), 'tahun_terbit' => (int) ($data['tahun_terbit'] ?? null), 'tahun_berjalan' => (int) ($data['tahun_berjalan'] ?? null), 'kelurahan' => $this->cleanString($data['kelurahan'] ?? null), 'kecamatan' => $this->cleanString($data['kecamatan'] ?? null), 'lb' => $this->convertToDecimal($data['lb'] ?? null), 'tb' => $this->convertToDecimal($data['tb'] ?? null), 'jlb' => (int) ($data['jlb'] ?? null), 'unit' => (int) ($data['unit'] ?? null), 'usulan_retribusi' => (int) ($data['usulan_retribusi'] ?? null), 'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__simbg_'] ?? null), 'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__pad_'] ?? null), 'denda' => $this->convertToDecimal($data['denda'] ?? null), 'latitude' => $this->cleanString($data['latitude'] ?? null), 'longitude' => $this->cleanString($data['longitude'] ?? null), 'nik_nib' => $this->cleanString($data['nik_nib'] ?? null), 'dok_tanah' => $this->cleanString($data['dok__tanah'] ?? null), 'temuan' => $this->cleanString($data['temuan'] ?? null), ]; } $batchSize = 1000; $chunks = array_chunk($mapToUpsert, $batchSize); foreach($chunks as $chunk){ PbgTaskGoogleSheet::upsert($chunk, ["no_registrasi"],[ 'jenis_konsultasi', 'nama_pemilik', 'lokasi_bg', 'fungsi_bg', 'nama_bangunan', 'tgl_permohonan', 'status_verifikasi', 'status_permohonan', 'alamat_pemilik', 'no_hp', 'email', 'tanggal_catatan', 'catatan_kekurangan_dokumen', 'gambar', 'krk_kkpr', 'no_krk', 'lh', 'ska', 'keterangan', 'helpdesk', 'pj', 'kepemilikan', 'potensi_taru', 'validasi_dinas', 'kategori_retribusi', 'no_urut_ba_tpt', 'tanggal_ba_tpt', 'no_urut_ba_tpa', 'tanggal_ba_tpa', 'no_urut_skrd', 'tanggal_skrd', 'ptsp', 'selesai_terbit', 'tanggal_pembayaran', 'format_sts', 'tahun_terbit', 'tahun_berjalan', 'kelurahan', 'kecamatan', 'lb', 'tb', 'jlb', 'unit', 'usulan_retribusi', 'nilai_retribusi_keseluruhan_simbg', 'nilai_retribusi_keseluruhan_pad', 'denda', 'latitude', 'longitude', 'nik_nib', 'dok_tanah', 'temuan', ]); } $initResToken = $this->getToken(); if (empty($initResToken->original['data']['token']['access'])) { $importDatasource->update([ 'status' => ImportDatasourceStatus::Failed->value, 'response_body' => 'Failed to retrieve token' ]); return $this->resError("Failed to retrieve token"); } $apiToken = $initResToken->original['data']['token']['access']; $headers = ['Authorization' => "Bearer " . $apiToken]; $url = "/api/pbg/v1/list/?page=1&size={$this->fetch_per_page}&sort=ASC"; $initialResponse = $this->service_client->get($url, $headers); $totalPage = $initialResponse->original['data']['total_page'] ?? 0; if ($totalPage == 0) { $importDatasource->update([ 'status' => ImportDatasourceStatus::Failed->value, 'response_body' => 'Invalid response: no total_page' ]); return $this->resError("Invalid response from API"); } $savedCount = $failedCount = 0; Log::info("Fetching tasks", ['total page' => $totalPage]); for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) { try { $pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC"; Log::info("Fetching tasks", ['currentPage' => $currentPage]); $headers = [ 'Authorization' => "Bearer " . $apiToken, // Update headers ]; for ($attempt = 0; $attempt < 2; $attempt++) { // Try twice (original + retry) $response = $this->service_client->get($pageUrl, $headers); if ($response instanceof \Illuminate\Http\JsonResponse) { $decodedResponse = json_decode($response->getContent(), true); if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') { $initResToken = $this->getToken(); if (!empty($initResToken->original['data']['token']['access'])) { $new_token = $initResToken->original['data']['token']['access']; $headers['Authorization'] = "Bearer " . $new_token; continue; } else { Log::error("Failed to refresh token"); return $this->resError("Failed to refresh token"); } } } // Success case, break loop break; } $tasks = $response->original['data']['data'] ?? []; if (empty($tasks)) { Log::warning("No data found on page", ['page' => $currentPage]); continue; } $tasksCollective = []; foreach ($tasks as $item) { try { $tasksCollective[] = [ 'uuid' => $item['uid'], 'name' => $item['name'], 'owner_name' => $item['owner_name'], 'application_type' => $item['application_type'], 'application_type_name' => $item['application_type_name'], 'condition' => $item['condition'], 'registration_number' => $item['registration_number'], 'document_number' => $item['document_number'], 'address' => $item['address'], 'status' => $item['status'], 'status_name' => $item['status_name'], 'slf_status' => $item['slf_status'] ?? null, 'slf_status_name' => $item['slf_status_name'] ?? null, 'function_type' => $item['function_type'], 'consultation_type' => $item['consultation_type'], 'due_date' => $item['due_date'], 'land_certificate_phase' => $item['land_certificate_phase'], 'task_created_at' => isset($item['created_at']) ? Carbon::parse($item['created_at'])->format('Y-m-d H:i:s') : null, 'updated_at' => now(), 'created_at' => now(), ]; $this->syncTaskDetailSubmit($item['uid'], $apiToken); $this->syncTaskAssignments($item['uid']); $savedCount++; } catch (Exception $e) { $failedCount++; Log::error("Failed to process task", [ 'error' => $e->getMessage(), 'task' => $item, ]); continue; // Skip failed task, continue processing the rest } } if (!empty($tasksCollective)) { PbgTask::upsert($tasksCollective, ['uuid'], [ 'name', 'owner_name', 'application_type', 'application_type_name', 'condition', 'registration_number', 'document_number', 'address', 'status', 'status_name', 'slf_status', 'slf_status_name', 'function_type', 'consultation_type', 'due_date', 'land_certificate_phase', 'task_created_at', 'updated_at' ]); $uuids = array_column($tasksCollective, 'uuid'); $this->syncIndexIntegration($uuids); } } catch (Exception $e) { Log::error("Failed to process page", [ 'error' => $e->getMessage(), 'page' => $currentPage, ]); continue; // Skip the failed page and move to the next } } BigdataResume::generateResumeData($importDatasource->id, "all", $data_setting_result); BigdataResume::generateResumeData($importDatasource->id, now()->year, $data_setting_result); // Final update after processing all pages $importDatasource->update([ 'status' => ImportDatasourceStatus::Success->value, 'message' => "Successfully processed: $savedCount, Failed: $failedCount" ]); Log::info("syncTaskList completed", ['savedCount' => $savedCount, 'failedCount' => $failedCount]); return $this->resSuccess(['savedCount' => $savedCount, 'failedCount' => $failedCount]); } catch (Exception $e) { Log::error("syncTaskList failed", ['error' => $e->getMessage()]); if (isset($importDatasource)) { $importDatasource->update([ 'status' => ImportDatasourceStatus::Failed->value, 'response_body' => 'Critical failure: ' . $e->getMessage() ]); } return $this->resError("Critical failure occurred: " . $e->getMessage()); } } public function syncTaskDetailSubmit($uuid, $token) { try{ $url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/"; $headers = [ 'Authorization' => "Bearer " . $token, ]; for ($attempt = 0; $attempt < 2; $attempt++) { $res = $this->service_client->get($url, $headers); // Check if response is JsonResponse and decode it if ($res instanceof \Illuminate\Http\JsonResponse) { $decodedResponse = json_decode($res->getContent(), true); if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') { $initResToken = $this->getToken(); if (!empty($initResToken->original['data']['token']['access'])) { $new_token = $initResToken->original['data']['token']['access']; $headers['Authorization'] = "Bearer " . $new_token; continue; } else { Log::error("Failed to refresh token"); return $this->resError("Failed to refresh token"); } } } break; } // Ensure response is valid before accessing properties $responseData = $res->original ?? []; $data = $responseData['data']['data'] ?? []; if (empty($data)) { return false; } $detailCreatedAt = isset($data['created_at']) ? Carbon::parse($data['created_at'])->format('Y-m-d H:i:s') : null; $detailUpdatedAt = isset($data['updated_at']) ? Carbon::parse($data['updated_at'])->format('Y-m-d H:i:s') : null; $pbg_task_retributions = PbgTaskRetributions::updateOrCreate( ['detail_id' => $data['id']], [ 'detail_uid' => $data['uid'] ?? null, 'detail_created_at' => $detailCreatedAt ?? null, 'detail_updated_at' => $detailUpdatedAt ?? null, 'luas_bangunan' => $data['luas_bangunan'] ?? null, 'indeks_lokalitas' => $data['indeks_lokalitas'] ?? null, 'wilayah_shst' => $data['wilayah_shst'] ?? null, 'kegiatan_id' => $data['kegiatan']['id'] ?? null, 'kegiatan_name' => $data['kegiatan']['name'] ?? null, 'nilai_shst' => $data['nilai_shst'] ?? null, 'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null, 'indeks_bg_terbangun' => $data['indeks_bg_terbangun'] ?? null, 'nilai_retribusi_bangunan' => $data['nilai_retribusi_bangunan'] ?? null, 'nilai_prasarana' => $data['nilai_prasarana'] ?? null, 'created_by' => $data['created_by'] ?? null, 'pbg_document' => $data['pbg_document'] ?? null, 'underpayment' => $data['underpayment'] ?? null, 'skrd_amount' => $data['skrd_amount'] ?? null, 'pbg_task_uid' => $uuid, ] ); $pbg_task_retribution_id = $pbg_task_retributions->id; $prasaranaData = $data['prasarana'] ?? []; if (!empty($prasaranaData)) { $insertData = array_map(fn($item) => [ 'pbg_task_uid' => $uuid, 'pbg_task_retribution_id' => $pbg_task_retribution_id, 'prasarana_id' => $item['id'] ?? null, 'prasarana_type' => $item['prasarana_type'] ?? null, 'building_type' => $item['building_type'] ?? null, 'total' => $item['total'] ?? null, 'quantity' => $item['quantity'] ?? null, 'unit' => $item['unit'] ?? null, 'index_prasarana' => $item['index_prasarana'] ?? null, ], $prasaranaData); // Use bulk insert or upsert for faster database operation PbgTaskPrasarana::upsert($insertData, ['prasarana_id']); } return true; }catch(Exception $e){ Log::error("Failed to sync task detail submit", ['error' => $e->getMessage(), 'uuid' => $uuid]); throw $e; } } public function syncTaskAssignments($uuid){ try{ $init_token = $this->getToken(); $token = $init_token->original['data']['token']['access']; $url = "/api/pbg/v1/list-tim-penilai/". $uuid . "/?page=1&size=10"; $headers = [ 'Authorization' => "Bearer " . $token, ]; $response = $this->service_client->get($url, $headers); $datas = $response->original['data']['data'] ?? []; if(empty($datas)){ return false; } $task_assignments = []; foreach ($datas as $data) { $task_assignments[] = [ 'pbg_task_uid' => $uuid, 'user_id' => $data['user_id'], 'name' => $data['name'], 'username' => $data['username'], 'email' => $data['email'], 'phone_number' => $data['phone_number'], 'role' => $data['role'], 'role_name' => $data['role_name'], 'is_active' => $data['is_active'], 'file' => !empty($data['file']) ? json_encode($data['file']) : null, 'expertise' => !empty($data['expertise']) ? json_encode($data['expertise']) : null, 'experience' => !empty($data['experience']) ? json_encode($data['experience']) : null, 'is_verif' => $data['is_verif'], 'uid' => $data['uid'], 'status' => $data['status'], 'status_name' => $data['status_name'], 'note' => $data['note'], 'ta_id' => $data['id'], 'created_at' => now(), 'updated_at' => now(), ]; } TaskAssignment::upsert( $task_assignments, ['uid'], ['ta_id','name', 'username', 'email', 'phone_number', 'role', 'role_name', 'is_active', 'file', 'expertise', 'experience', 'is_verif', 'status', 'status_name', 'note', 'updated_at'] ); return true; }catch(Exception $e){ Log::error("Failed to sync task assignments", ['error' => $e->getMessage()]); throw $e; } } protected function convertToDecimal(?string $value): ?float { if (empty($value)) { return null; // Return null if the input is empty } // Remove all non-numeric characters except comma and dot $value = preg_replace('/[^0-9,\.]/', '', $value); // If the number contains both dot (.) and comma (,) if (strpos($value, '.') !== false && strpos($value, ',') !== false) { $value = str_replace('.', '', $value); // Remove thousands separator $value = str_replace(',', '.', $value); // Convert decimal separator to dot } // If only a dot is present (assumed as thousands separator) elseif (strpos($value, '.') !== false) { $value = str_replace('.', '', $value); // Remove all dots (treat as thousands separators) } // If only a comma is present (assumed as decimal separator) elseif (strpos($value, ',') !== false) { $value = str_replace(',', '.', $value); // Convert comma to dot (decimal separator) } // Ensure the value is numeric before returning return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null; } protected function convertToInteger($value) { // Check if the value is an empty string, and return null if true if (trim($value) === "") { return null; } $cleaned = str_replace('.','', $value); // Otherwise, cast to integer return (int) $cleaned; } protected function convertToDate($dateString) { try { // Check if the string is empty if (empty($dateString)) { return null; } // Try to parse the date string $date = Carbon::parse($dateString); // Return the Carbon instance return $date->format('Y-m-d'); } catch (Exception $e) { // Return null if an error occurs during parsing return null; } } private function cleanString($value) { return isset($value) ? trim(strip_tags($value)) : null; } }