client = new Google_Client(); $this->client->setApplicationName("Sibedas Google Sheets API"); $this->client->setScopes([Google_Service_Sheets::SPREADSHEETS_READONLY]); $this->client->setAuthConfig(storage_path("app/teak-banner-450003-s8-ea05661d9db0.json")); $this->client->setAccessType("offline"); $this->service = new Google_Service_Sheets($this->client); $this->spreadsheetID = env("SPREAD_SHEET_ID"); $this->service_sheets = new Google_Service_Sheets($this->client); } public function run_service(){ try{ $this->sync_big_data(); $this->sync_google_sheet_data(); }catch(Exception $e){ throw $e; } } public function sync_google_sheet_data() { try { $sheet_data = $this->get_data_by_sheet(0); if (empty($sheet_data) || count($sheet_data) < 2) { Log::warning("sync_google_sheet_data: No valid data found."); throw new Exception("sync_google_sheet_data: No valid data found."); } $cleanValue = function ($value) { return (isset($value) && trim($value) !== '') ? trim($value) : null; }; $mapUpsert = []; foreach(array_slice($sheet_data, 1) as $row){ if(!is_array($row)){ continue; } $no_registrasi = $cleanValue($row[2] ?? null); // Apply the same logic from your SQL UPDATE if (strpos($no_registrasi, 'PBG-') === 0) { $format_registrasi = $no_registrasi; } else { $format_registrasi = sprintf( "PBG-%s-%s-%s", substr($no_registrasi, 0, 6) ?: '', substr($no_registrasi, 7, 8) ?: '', substr($no_registrasi, -2) ?: '' ); } $mapUpsert[] = [ 'jenis_konsultasi' => $cleanValue($row[1] ?? null), 'no_registrasi' => $no_registrasi, 'formatted_registration_number' => $format_registrasi, 'nama_pemilik' => $cleanValue($row[3] ?? null), 'lokasi_bg' => $cleanValue($row[4] ?? null), 'fungsi_bg' => $cleanValue($row[5] ?? null), 'nama_bangunan' => $cleanValue($row[6] ?? null), 'tgl_permohonan' => $this->convertToDate($cleanValue($row[7] ?? null)), 'status_verifikasi' => $cleanValue($row[8] ?? null), 'status_permohonan' => $cleanValue($row[9] ?? null), 'alamat_pemilik' => $cleanValue($row[10] ?? null), 'no_hp' => $cleanValue($row[11] ?? null), 'email' => $cleanValue($row[12] ?? null), 'tanggal_catatan' => $this->convertToDate($cleanValue($row[13] ?? null)), 'catatan_kekurangan_dokumen' => $cleanValue($row[14] ?? null), 'gambar' => $cleanValue($row[15] ?? null), 'krk_kkpr' => $cleanValue($row[16] ?? null), 'no_krk' => $cleanValue($row[17] ?? null), 'lh' => $cleanValue($row[18] ?? null), 'ska' => $cleanValue($row[19] ?? null), 'keterangan' => $cleanValue($row[20] ?? null), 'helpdesk' => $cleanValue($row[21] ?? null), 'pj' => $cleanValue($row[22] ?? null), 'kepemilikan' => $cleanValue($row[24] ?? null), 'potensi_taru' => $cleanValue($row[25] ?? null), 'validasi_dinas' => $cleanValue($row[26] ?? null), 'kategori_retribusi' => $cleanValue($row[27] ?? null), 'no_urut_ba_tpt' => $cleanValue($row[28] ?? null), 'tanggal_ba_tpt' => $this->convertToDate($cleanValue($row[29] ?? null)), 'no_urut_ba_tpa' => $cleanValue($row[30] ?? null), 'tanggal_ba_tpa' => $this->convertToDate($cleanValue($row[31] ?? null)), 'no_urut_skrd' => $cleanValue($row[32] ?? null), 'tanggal_skrd' => $this->convertToDate($cleanValue($row[33] ?? null)), 'ptsp' => $cleanValue($row[34] ?? null), 'selesai_terbit' => $cleanValue($row[35] ?? null), 'tanggal_pembayaran' => $this->convertToDate($cleanValue($row[36] ?? null)), 'format_sts' => $cleanValue($row[37] ?? null), 'tahun_terbit' => (int) $cleanValue($row[38] ?? null), 'tahun_berjalan' => (int) $cleanValue($row[39] ?? null), 'kelurahan' => $cleanValue($row[40] ?? null), 'kecamatan' => $cleanValue($row[41] ?? null), 'lb' => $this->convertToDecimal($cleanValue($row[42] ?? 0)), 'tb' => $this->convertToDecimal($cleanValue($row[43] ?? 0)), 'jlb' => (int) $cleanValue($row[44] ?? null), 'unit' => (int) $cleanValue($row[45] ?? null), 'usulan_retribusi' => (int) $cleanValue($row[46] ?? null), 'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($cleanValue($row[47] ?? 0)), 'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($cleanValue($row[48] ?? 0)), 'denda' => $this->convertToDecimal($cleanValue($row[49] ?? 0)), 'latitude' => $cleanValue($row[50] ?? null), 'longitude' => $cleanValue($row[51] ?? null), 'nik_nib' => $cleanValue($row[52] ?? null), 'dok_tanah' => $cleanValue($row[53] ?? null), 'temuan' => $cleanValue($row[54] ?? null), 'updated_at' => now() ]; } // Count occurrences of each no_registrasi $registrasiCounts = array_count_values(array_column($mapUpsert, 'no_registrasi')); // Filter duplicates (those appearing more than once) $duplicates = array_filter($registrasiCounts, function ($count) { return $count > 1; }); if (!empty($duplicates)) { Log::warning("Duplicate no_registrasi found", ['duplicates' => array_keys($duplicates)]); } // Remove duplicates before upsert $mapUpsert = collect($mapUpsert)->unique('no_registrasi')->values()->all(); $batchSize = 1000; $chunks = array_chunk($mapUpsert, $batchSize); foreach ($chunks as $chunk) { PbgTaskGoogleSheet::upsert($chunk, ['no_registrasi']); } Log::info("sync google sheet done"); return true; } catch (\Exception $e) { Log::error("sync_google_sheet_data failed", ['error' => $e->getMessage()]); throw $e; } } public function sync_big_data(){ try { $sheet_big_data = $this->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; } } foreach ($data_setting_result as $key => $value) { DataSetting::updateOrInsert( ["key" => $key], // Find by key ["value" => $value] // Update or insert value ); } return true; } catch (\Exception $e) { // **Log error** Log::error("Error syncing Google Sheet data", ['error' => $e->getMessage()]); return false; } } public function get_big_resume_data(){ try { $sheet_big_data = $this->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; } } return $data_setting_result; }catch(Exception $exception){ Log::error("Error getting big resume data", ['error' => $exception->getMessage()]); throw $exception; } } private function get_data_by_sheet($no_sheet = 1){ $spreadsheet = $this->service->spreadsheets->get($this->spreadsheetID); $sheets = $spreadsheet->getSheets(); $sheetTitle = $sheets[$no_sheet]->getProperties()->getTitle(); $range = "{$sheetTitle}"; $response = $this->service->spreadsheets_values->get($this->spreadsheetID, $range); $values = $response->getValues(); return!empty($values)? $values : []; } private 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; } private 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; } private 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; } } }