From 53d12d67985a29a065b4e0cc958e4a6d7c1926ff Mon Sep 17 00:00:00 2001 From: arifal hidayat Date: Thu, 11 Sep 2025 02:06:34 +0700 Subject: [PATCH] add column full pbg task payments --- app/Console/Commands/SyncPbgTaskPayments.php | 31 +- app/Jobs/ScrapingDataJob.php | 2 +- app/Models/PbgTaskPayment.php | 76 ++- app/Services/ServiceGoogleSheet.php | 483 ++++++++++++------ ...full_column_in_pbg_task_payments_table.php | 149 ++++++ 5 files changed, 561 insertions(+), 180 deletions(-) create mode 100644 database/migrations/2025_09_10_233438_add_full_column_in_pbg_task_payments_table.php diff --git a/app/Console/Commands/SyncPbgTaskPayments.php b/app/Console/Commands/SyncPbgTaskPayments.php index d0f83dc..0e36382 100644 --- a/app/Console/Commands/SyncPbgTaskPayments.php +++ b/app/Console/Commands/SyncPbgTaskPayments.php @@ -19,7 +19,7 @@ class SyncPbgTaskPayments extends Command * * @var string */ - protected $description = 'Sync PBG task payments from Google Sheets REALISASI PAD'; + protected $description = 'Sync PBG task payments from Google Sheets Sheet Data'; /** * Execute the console command. @@ -42,36 +42,13 @@ class SyncPbgTaskPayments extends Command $this->newLine(); $this->table( - ['Metric', 'Count'], + ['Metric', 'Value'], [ - ['Total rows processed', $result['total_rows']], - ['Successful syncs', $result['successful_syncs']], - ['Failed syncs', $result['failed_syncs']], - ['Tasks not found', $result['not_found_tasks']], + ['Inserted rows', $result['inserted'] ?? 0], + ['Success', ($result['success'] ?? false) ? 'Yes' : 'No'], ] ); - // Show success rate - $success_rate = $result['total_rows'] > 0 - ? round(($result['successful_syncs'] / $result['total_rows']) * 100, 2) - : 0; - - $this->info("📈 Success rate: {$success_rate}%"); - - // Show errors if any - if (!empty($result['errors'])) { - $this->newLine(); - $this->warn('⚠️ Errors encountered:'); - foreach (array_slice($result['errors'], 0, 5) as $error) { - $this->line(" Row {$error['row']} ({$error['registration_number']}): {$error['error']}"); - } - - if (count($result['errors']) > 5) { - $remaining = count($result['errors']) - 5; - $this->line(" ... and {$remaining} more errors (check logs for details)"); - } - } - $this->newLine(); $this->info('📝 Check Laravel logs for detailed information.'); diff --git a/app/Jobs/ScrapingDataJob.php b/app/Jobs/ScrapingDataJob.php index 022cb9e..a96da3c 100644 --- a/app/Jobs/ScrapingDataJob.php +++ b/app/Jobs/ScrapingDataJob.php @@ -66,7 +66,7 @@ class ScrapingDataJob implements ShouldQueue Log::info("=== STEP 1: SCRAPING GOOGLE SHEET ==="); $import_datasource->update(['message' => 'Scraping Google Sheet data...']); - // $service_google_sheet->run_service(); + $service_google_sheet->run_service(); Log::info("Google Sheet scraping completed successfully"); // STEP 2: Scrape PBG Task to get parent data diff --git a/app/Models/PbgTaskPayment.php b/app/Models/PbgTaskPayment.php index 8a4685d..2e24452 100644 --- a/app/Models/PbgTaskPayment.php +++ b/app/Models/PbgTaskPayment.php @@ -11,15 +11,79 @@ class PbgTaskPayment extends Model protected $fillable = [ 'pbg_task_id', 'pbg_task_uid', - 'registration_number', - 'sts_form_number', - 'payment_date', - 'pad_amount' + // mapped fields + 'row_no', + 'consultation_type', + 'source_registration_number', + 'owner_name', + 'building_location', + 'building_function', + 'building_name', + 'application_date_raw', + 'verification_status', + 'application_status', + 'owner_address', + 'owner_phone', + 'owner_email', + 'note_date_raw', + 'document_shortage_note', + 'image_url', + 'krk_kkpr', + 'krk_number', + 'lh', + 'ska', + 'remarks', + 'helpdesk', + 'person_in_charge', + 'pbg_operator', + 'ownership', + 'taru_potential', + 'agency_validation', + 'retribution_category', + 'ba_tpt_number', + 'ba_tpt_date_raw', + 'ba_tpa_number', + 'ba_tpa_date_raw', + 'skrd_number', + 'skrd_date_raw', + 'ptsp_status', + 'issued_status', + 'payment_date_raw', + 'sts_format', + 'issuance_year', + 'current_year', + 'village', + 'district', + 'building_area', + 'building_height', + 'floor_count', + 'unit_count', + 'proposed_retribution', + 'retribution_total_simbg', + 'retribution_total_pad', + 'penalty_amount', + 'business_category', + 'created_at', + 'updated_at' ]; protected $casts = [ - 'payment_date' => 'date', - 'pad_amount' => 'decimal:2' + 'application_date_raw' => 'date', + 'note_date_raw' => 'date', + 'ba_tpt_date_raw' => 'date', + 'ba_tpa_date_raw' => 'date', + 'skrd_date_raw' => 'date', + 'payment_date_raw' => 'date', + 'issuance_year' => 'integer', + 'current_year' => 'integer', + 'floor_count' => 'integer', + 'unit_count' => 'integer', + 'building_area' => 'decimal:2', + 'building_height' => 'decimal:2', + 'proposed_retribution' => 'decimal:2', + 'retribution_total_simbg' => 'decimal:2', + 'retribution_total_pad' => 'decimal:2', + 'penalty_amount' => 'decimal:2' ]; /** diff --git a/app/Services/ServiceGoogleSheet.php b/app/Services/ServiceGoogleSheet.php index d04147b..fa4a0ed 100644 --- a/app/Services/ServiceGoogleSheet.php +++ b/app/Services/ServiceGoogleSheet.php @@ -13,6 +13,9 @@ use Exception; use Google\Client as Google_Client; use Google\Service\Sheets as Google_Service_Sheets; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; +use App\Models\PbgTask; class ServiceGoogleSheet { protected $client; @@ -36,8 +39,8 @@ class ServiceGoogleSheet public function run_service(){ try{ - // $this->sync_big_data(); $this->sync_google_sheet_data(); + $this->sync_pbg_task_payments(); }catch(Exception $e){ throw $e; } @@ -423,196 +426,384 @@ class ServiceGoogleSheet } } - public function get_realisasi_pad_data(){ + /** + * Get sheet data where the first row is treated as headers, and subsequent rows + * are returned as associative arrays keyed by header names. Supports selecting + * a contiguous column range plus additional specific columns. + * + * Example: get_sheet_data_with_headers_range('Data', 'A', 'AX', ['BX']) + * + * @param string $sheet_name + * @param string $start_column_letter Inclusive start column letter (e.g., 'A') + * @param string $end_column_letter Inclusive end column letter (e.g., 'AX') + * @param array $extra_column_letters Additional discrete column letters (e.g., ['BX']) + * @return array{headers: array, data: array>, selected_columns: array} + */ + public function get_sheet_data_with_headers_range(string $sheet_name, string $start_column_letter, string $end_column_letter, array $extra_column_letters = []) + { try { - // Get data from "REALISASI PAD" sheet - $sheet_data = $this->get_data_by_sheet_name("REALISASI PAD"); - + $sheet_data = $this->get_data_by_sheet_name($sheet_name); + if (empty($sheet_data)) { - Log::warning("No data found in REALISASI PAD sheet"); - return []; + Log::warning("No data found in sheet", ['sheet_name' => $sheet_name]); + return [ + 'headers' => [], + 'data' => [], + 'selected_columns' => [] + ]; } - // Column indices: C=2, AK=36, AL=37, AW=48 (0-based) - $columns = [ - 'C' => 2, - 'AK' => 36, - 'AL' => 37, - 'AW' => 48 - ]; + // Build selected column indices: range A..AX and extras like BX + $selected_indices = $this->expandColumnRangeToIndices($start_column_letter, $end_column_letter); + foreach ($extra_column_letters as $letter) { + $selected_indices[] = $this->columnLetterToIndex($letter); + } + // Ensure unique and sorted + $selected_indices = array_values(array_unique($selected_indices)); + sort($selected_indices); $result = [ 'headers' => [], - 'data' => [] + 'data' => [], + 'selected_columns' => $selected_indices ]; foreach ($sheet_data as $row_index => $row) { if (!is_array($row)) continue; if ($row_index === 0) { - // First row contains headers - foreach ($columns as $column_name => $column_index) { - $result['headers'][$column_name] = isset($row[$column_index]) ? trim($row[$column_index]) : ''; + // First row contains headers (by selected columns) + foreach ($selected_indices as $col_index) { + $raw = isset($row[$col_index]) ? trim((string) $row[$col_index]) : ''; + // Fallback to column letter if empty + $header = $raw !== '' ? $raw : $this->indexToColumnLetter($col_index); + $result['headers'][$col_index] = $this->normalizeHeader($header); } } else { - // Data rows - $row_data = [ - 'row' => $row_index + 1 // 1-based row number - ]; - + $row_assoc = []; $has_data = false; - foreach ($columns as $column_name => $column_index) { - $value = isset($row[$column_index]) ? trim($row[$column_index]) : ''; - $row_data[$column_name] = $value; + foreach ($selected_indices as $col_index) { + $header = $result['headers'][$col_index] ?? $this->normalizeHeader($this->indexToColumnLetter($col_index)); + $value = isset($row[$col_index]) ? trim((string) $row[$col_index]) : ''; + $row_assoc[$header] = ($value === '') ? null : $value; if ($value !== '') { $has_data = true; } } - - // Only add row if it has at least one non-empty value if ($has_data) { - $result['data'][] = $row_data; + $result['data'][] = $row_assoc; } } } - Log::info("REALISASI PAD Multiple Columns Data", [ - 'sheet_name' => 'REALISASI PAD', - 'columns' => array_keys($columns), - 'headers' => $result['headers'], - 'total_data_rows' => count($result['data']), - 'sample_data' => array_slice($result['data'], 0, 5), // Show first 5 rows as sample - 'column_indices' => $columns - ]); - return $result; - } catch (\Exception $e) { - Log::error("Error getting REALISASI PAD data", ['error' => $e->getMessage()]); + Log::error("Error getting sheet data with headers", [ + 'sheet_name' => $sheet_name, + 'error' => $e->getMessage() + ]); throw $e; } } + /** + * Convert a column letter (e.g., 'A', 'Z', 'AA', 'AX', 'BX') to a zero-based index (A=0) + */ + private function columnLetterToIndex(string $letter): int + { + $letter = strtoupper(trim($letter)); + $length = strlen($letter); + $index = 0; + for ($i = 0; $i < $length; $i++) { + $index = $index * 26 + (ord($letter[$i]) - ord('A') + 1); + } + return $index - 1; // zero-based + } + + /** + * Convert zero-based column index to column letter (0='A') + */ + private function indexToColumnLetter(int $index): string + { + $index += 1; // make 1-based for calculation + $letters = ''; + while ($index > 0) { + $mod = ($index - 1) % 26; + $letters = chr($mod + ord('A')) . $letters; + $index = intdiv($index - 1, 26); + } + return $letters; + } + + /** + * Expand a column range like 'A'..'AX' to zero-based indices array + */ + private function expandColumnRangeToIndices(string $start_letter, string $end_letter): array + { + $start = $this->columnLetterToIndex($start_letter); + $end = $this->columnLetterToIndex($end_letter); + if ($start > $end) { + [$start, $end] = [$end, $start]; + } + return range($start, $end); + } + + /** + * Normalize header: trim, lowercase, replace spaces with underscore, remove non-alnum/underscore + */ + private function normalizeHeader(string $header): string + { + $header = trim($header); + $header = strtolower($header); + $header = preg_replace('/\s+/', '_', $header); + $header = preg_replace('/[^a-z0-9_]/', '', $header); + return $header; + } + public function sync_pbg_task_payments(){ try { - // Get payment data from REALISASI PAD sheet - $payment_data = $this->get_realisasi_pad_data(); - - if (empty($payment_data['data'])) { - Log::warning("No payment data found to sync"); - return ['success' => false, 'message' => 'No payment data found']; + $sheetName = 'Data'; + $startLetter = 'A'; + $endLetter = 'AX'; + $extraLetters = ['BF']; + + // Fetch header row only (row 1) across A..BF and build header/selection + $headerRange = sprintf('%s!%s1:%s1', $sheetName, $startLetter, 'BF'); + $headerResponse = $this->service->spreadsheets_values->get($this->spreadsheetID, $headerRange); + $headerRow = $headerResponse->getValues()[0] ?? []; + if (empty($headerRow)) { + Log::warning("No header row found in sheet", ['sheet' => $sheetName]); + return ['success' => false, 'message' => 'No header row found']; } - $successful_syncs = 0; - $failed_syncs = 0; - $not_found_tasks = 0; - $not_found_tasks_registration_number = []; - $failed_sync_registration_numbers = []; - $errors = []; + // Selected indices: A..AX plus BF + $selected_indices = $this->expandColumnRangeToIndices($startLetter, $endLetter); + foreach ($extraLetters as $letter) { + $selected_indices[] = $this->columnLetterToIndex($letter); + } + $selected_indices = array_values(array_unique($selected_indices)); + sort($selected_indices); - foreach ($payment_data['data'] as $row) { - try { - // Clean registration number from column C - $registration_number = \App\Models\PbgTaskPayment::cleanRegistrationNumber($row['C']); - - if (empty($registration_number)) { - $failed_syncs++; - $failed_sync_registration_numbers[] = [ - 'row' => $row['row'], - 'registration_number' => $row['C'] ?? 'EMPTY', - 'reason' => 'Empty registration number' - ]; - continue; - } - - // Find PBG task by registration number - $pbg_task = \App\Models\PbgTask::where('registration_number', $registration_number)->first(); - - if (!$pbg_task) { - $not_found_tasks_registration_number[] = [ - 'row' => $row['row'], - 'registration_number' => $registration_number - ]; - $not_found_tasks++; - Log::warning("PBG Task not found for registration number", [ - 'registration_number' => $registration_number, - 'row' => $row['row'] - ]); - continue; - } - - // Convert data types - $payment_date = \App\Models\PbgTaskPayment::convertPaymentDate($row['AK']); - $pad_amount = \App\Models\PbgTaskPayment::convertPadAmount($row['AW']); - $sts_form_number = !empty($row['AL']) ? trim($row['AL']) : null; - - // Create or update payment record - \App\Models\PbgTaskPayment::updateOrCreate( - [ - 'pbg_task_id' => $pbg_task->id, - 'registration_number' => $registration_number - ], - [ - 'pbg_task_uid' => $pbg_task->uuid, - 'sts_form_number' => $sts_form_number, - 'payment_date' => $payment_date, - 'pad_amount' => $pad_amount - ] - ); - - $successful_syncs++; - - } catch (\Exception $e) { - $failed_syncs++; - $registration_number = $row['C'] ?? 'N/A'; - $failed_sync_registration_numbers[] = [ - 'row' => $row['row'], - 'registration_number' => $registration_number, - 'reason' => $e->getMessage() - ]; - $errors[] = [ - 'row' => $row['row'], - 'registration_number' => $registration_number, - 'error' => $e->getMessage() - ]; - - Log::error("Error syncing payment data for row", [ - 'row' => $row['row'], - 'registration_number' => $registration_number, - 'error' => $e->getMessage() - ]); - } + // Build normalized headers map (index -> header) + $headers = []; + foreach ($selected_indices as $colIdx) { + $raw = isset($headerRow[$colIdx]) ? trim((string) $headerRow[$colIdx]) : ''; + $header = $raw !== '' ? $raw : $this->indexToColumnLetter($colIdx); + $headers[$colIdx] = $this->normalizeHeader($header); } - $result = [ - 'success' => true, - 'total_rows' => count($payment_data['data']), - 'successful_syncs' => $successful_syncs, - 'failed_syncs' => $failed_syncs, - 'not_found_tasks' => $not_found_tasks, - 'not_found_tasks_registration_number' => $not_found_tasks_registration_number, - 'failed_sync_registration_numbers' => $failed_sync_registration_numbers, - 'errors' => $errors + // Truncate table and restart identity + Schema::disableForeignKeyConstraints(); + DB::table('pbg_task_payments')->truncate(); + Schema::enableForeignKeyConstraints(); + + // Map header -> db column + $map = [ + 'no' => 'row_no', + 'jenis_konsultasi' => 'consultation_type', + 'no_registrasi' => 'source_registration_number', + 'nama_pemilik' => 'owner_name', + 'lokasi_bg' => 'building_location', + 'fungsi_bg' => 'building_function', + 'nama_bangunan' => 'building_name', + 'tgl_permohonan' => 'application_date_raw', + 'status_verifikasi' => 'verification_status', + 'status_permohonan' => 'application_status', + 'alamat_pemilik' => 'owner_address', + 'no_hp' => 'owner_phone', + 'email' => 'owner_email', + 'tanggal_catatan' => 'note_date_raw', + 'catatan_kekurangan_dokumen' => 'document_shortage_note', + 'gambar' => 'image_url', + 'krkkkpr' => 'krk_kkpr', + 'no_krk' => 'krk_number', + 'lh' => 'lh', + 'ska' => 'ska', + 'keterangan' => 'remarks', + 'helpdesk' => 'helpdesk', + 'pj' => 'person_in_charge', + 'operator_pbg' => 'pbg_operator', + 'kepemilikan' => 'ownership', + 'potensi_taru' => 'taru_potential', + 'validasi_dinas' => 'agency_validation', + 'kategori_retribusi' => 'retribution_category', + 'no_urut_ba_tpt_20250001' => 'ba_tpt_number', + 'tanggal_ba_tpt' => 'ba_tpt_date_raw', + 'no_urut_ba_tpa' => 'ba_tpa_number', + 'tanggal_ba_tpa' => 'ba_tpa_date_raw', + 'no_urut_skrd_20250001' => 'skrd_number', + 'tanggal_skrd' => 'skrd_date_raw', + 'ptsp' => 'ptsp_status', + 'selesai_terbit' => 'issued_status', + 'tanggal_pembayaran_yyyymmdd' => 'payment_date_raw', + 'format_sts' => 'sts_format', + 'tahun_terbit' => 'issuance_year', + 'tahun_berjalan' => 'current_year', + 'kelurahan' => 'village', + 'kecamatan' => 'district', + 'lb' => 'building_area', + 'tb' => 'building_height', + 'jlb' => 'floor_count', + 'unit' => 'unit_count', + 'usulan_retribusi' => 'proposed_retribution', + 'nilai_retribusi_keseluruhan_simbg' => 'retribution_total_simbg', + 'nilai_retribusi_keseluruhan_pad' => 'retribution_total_pad', + 'denda' => 'penalty_amount', + 'usaha__non_usaha' => 'business_category', ]; - Log::info("PBG Task Payments sync completed", $result); + // We'll build registration map lazily per chunk to limit memory + $regToTask = []; - // Log detailed arrays for failed syncs and not found registration numbers - if (!empty($failed_sync_registration_numbers)) { - Log::warning("Failed Sync Registration Numbers", [ - 'count' => count($failed_sync_registration_numbers), - 'details' => $failed_sync_registration_numbers - ]); + // Build and insert in small batches to avoid high memory usage + $batch = []; + $batchSize = 500; + $inserted = 0; + // Stream rows in chunks from API to avoid loading full sheet + $rowStart = 2; // data starts from row 2 + $chunkRowSize = 800; // number of rows per chunk + $inserted = 0; + while (true) { + $rowEnd = $rowStart + $chunkRowSize - 1; + $range = sprintf('%s!%s%d:%s%d', $sheetName, $startLetter, $rowStart, 'BF', $rowEnd); + $resp = $this->service->spreadsheets_values->get($this->spreadsheetID, $range); + $values = $resp->getValues() ?? []; + if (empty($values)) { + break; // no more rows + } + + // Preload registration map for this chunk + $chunkRegs = []; + foreach ($values as $row) { + foreach ($selected_indices as $colIdx) { + // find normalized header for this index + $h = $headers[$colIdx] ?? null; + if ($h === 'no_registrasi') { + $val = isset($row[$colIdx]) ? trim((string) $row[$colIdx]) : ''; + if ($val !== '') { $chunkRegs[$val] = true; } + } + } + } + if (!empty($chunkRegs)) { + $keys = array_keys($chunkRegs); + $tasks = PbgTask::whereIn('registration_number', $keys)->get(['id','uuid','registration_number']); + foreach ($tasks as $task) { + $regToTask[trim($task->registration_number)] = ['id' => $task->id, 'uuid' => $task->uuid]; + } + } + + // Build and insert this chunk + $batch = []; + foreach ($values as $row) { + $record = [ + 'created_at' => now(), + 'updated_at' => now(), + ]; + + // Map row values by headers + $rowByHeader = []; + foreach ($selected_indices as $colIdx) { + $h = $headers[$colIdx] ?? null; + if ($h === null) continue; + $rowByHeader[$h] = isset($row[$colIdx]) ? trim((string) $row[$colIdx]) : null; + if ($rowByHeader[$h] === '') $rowByHeader[$h] = null; + } + + // Skip if this row looks like a header row + $headerCheckKeys = ['no','jenis_konsultasi','no_registrasi']; + $headerMatches = 0; + foreach ($headerCheckKeys as $hk) { + if (!array_key_exists($hk, $rowByHeader)) { continue; } + $val = $rowByHeader[$hk]; + if ($val === null) { continue; } + if ($this->normalizeHeader($val) === $hk) { + $headerMatches++; + } + } + if ($headerMatches >= 2) { + continue; // looks like a repeated header row, skip + } + + // Skip if the entire row is empty (no values) + $hasAnyData = false; + foreach ($rowByHeader as $v) { + if ($v !== null && $v !== '') { $hasAnyData = true; break; } + } + if (!$hasAnyData) { continue; } + + foreach ($map as $header => $column) { + $value = $rowByHeader[$header] ?? null; + + switch ($column) { + case 'row_no': + case 'floor_count': + case 'unit_count': + case 'issuance_year': + case 'current_year': + $record[$column] = ($value === null || $value === '') ? null : (int) $value; + break; + case 'application_date_raw': + case 'note_date_raw': + case 'ba_tpt_date_raw': + case 'ba_tpa_date_raw': + case 'skrd_date_raw': + case 'payment_date_raw': + $record[$column] = $this->convertToDate($value); + break; + case 'building_area': + case 'building_height': + case 'proposed_retribution': + case 'retribution_total_simbg': + case 'retribution_total_pad': + case 'penalty_amount': + $record[$column] = $this->convertToDecimal($value); + break; + default: + if (is_string($value)) { $value = trim($value); } + $record[$column] = ($value === '' ? null : $value); + } + } + + // Final trim pass + foreach ($record as $k => $v) { + if (is_string($v)) { + $t = trim($v); + $record[$k] = ($t === '') ? null : $t; + } + } + + // Resolve relation + $sourceReg = $rowByHeader['no_registrasi'] ?? null; + if (is_string($sourceReg)) { $sourceReg = trim($sourceReg); } + if (!empty($sourceReg) && isset($regToTask[$sourceReg])) { + $record['pbg_task_id'] = $regToTask[$sourceReg]['id']; + $record['pbg_task_uid'] = $regToTask[$sourceReg]['uuid']; + } else { + $record['pbg_task_id'] = null; + $record['pbg_task_uid'] = null; + } + + $batch[] = $record; + } + + if (!empty($batch)) { + \App\Models\PbgTaskPayment::insert($batch); + $inserted += count($batch); + } + + // next chunk + $rowStart = $rowEnd + 1; + if (function_exists('gc_collect_cycles')) { gc_collect_cycles(); } } - if (!empty($not_found_tasks_registration_number)) { - Log::warning("Not Found Registration Numbers", [ - 'count' => count($not_found_tasks_registration_number), - 'details' => $not_found_tasks_registration_number - ]); + if (!empty($batch)) { + \App\Models\PbgTaskPayment::insert($batch); + $inserted += count($batch); } - return $result; + Log::info('PBG Task Payments reloaded from sheet', ['inserted' => $inserted]); + + return ['success' => true, 'inserted' => $inserted]; } catch (\Exception $e) { Log::error("Error syncing PBG task payments", ['error' => $e->getMessage()]); diff --git a/database/migrations/2025_09_10_233438_add_full_column_in_pbg_task_payments_table.php b/database/migrations/2025_09_10_233438_add_full_column_in_pbg_task_payments_table.php new file mode 100644 index 0000000..ad8fafe --- /dev/null +++ b/database/migrations/2025_09_10_233438_add_full_column_in_pbg_task_payments_table.php @@ -0,0 +1,149 @@ +dropForeign(['pbg_task_id']); + // Make column nullable + $table->unsignedBigInteger('pbg_task_id')->nullable()->change(); + // Recreate foreign key + $table->foreign('pbg_task_id')->references('id')->on('pbg_task')->cascadeOnDelete(); + } + + // Drop legacy columns if no longer needed + if (Schema::hasColumn('pbg_task_payments', 'registration_number')) { + $table->dropIndex(['registration_number']); + $table->dropColumn('registration_number'); + } + if (Schema::hasColumn('pbg_task_payments', 'sts_form_number')) { + $table->dropColumn('sts_form_number'); + } + if (Schema::hasColumn('pbg_task_payments', 'payment_date')) { + $table->dropColumn('payment_date'); + } + if (Schema::hasColumn('pbg_task_payments', 'pad_amount')) { + $table->dropColumn('pad_amount'); + } + + // Make pbg_task_uid nullable + if (Schema::hasColumn('pbg_task_payments', 'pbg_task_uid')) { + $table->string('pbg_task_uid')->nullable()->change(); + } + + // Add new columns (renamed for table conventions) + $table->integer('row_no')->nullable(); + $table->string('consultation_type')->nullable(); + $table->string('source_registration_number')->nullable(); + $table->string('owner_name')->nullable(); + $table->text('building_location')->nullable(); + $table->string('building_function')->nullable(); + $table->string('building_name')->nullable(); + $table->date('application_date_raw')->nullable(); + $table->string('verification_status')->nullable(); + $table->string('application_status')->nullable(); + $table->text('owner_address')->nullable(); + $table->string('owner_phone')->nullable(); + $table->string('owner_email')->nullable(); + $table->date('note_date_raw')->nullable(); + $table->text('document_shortage_note')->nullable(); + $table->string('image_url')->nullable(); + $table->string('krk_kkpr')->nullable(); + $table->string('krk_number')->nullable(); + $table->string('lh')->nullable(); + $table->string('ska')->nullable(); + $table->string('remarks')->nullable(); + $table->string('helpdesk')->nullable(); + $table->string('person_in_charge')->nullable(); + $table->string('pbg_operator')->nullable(); + $table->string('ownership')->nullable(); + $table->string('taru_potential')->nullable(); + $table->string('agency_validation')->nullable(); + $table->string('retribution_category')->nullable(); + $table->string('ba_tpt_number')->nullable(); + $table->date('ba_tpt_date_raw')->nullable(); + $table->string('ba_tpa_number')->nullable(); + $table->date('ba_tpa_date_raw')->nullable(); + $table->string('skrd_number')->nullable(); + $table->date('skrd_date_raw')->nullable(); + $table->string('ptsp_status')->nullable(); + $table->string('issued_status')->nullable(); + $table->date('payment_date_raw')->nullable(); + $table->string('sts_format')->nullable(); + $table->integer('issuance_year')->nullable(); + $table->integer('current_year')->nullable(); + $table->string('village')->nullable(); + $table->string('district')->nullable(); + $table->decimal('building_area', 18, 2)->nullable()->default(0); + $table->decimal('building_height', 18, 2)->nullable()->default(0); + $table->integer('floor_count')->nullable(); + $table->integer('unit_count')->nullable(); + $table->decimal('proposed_retribution', 18, 2)->nullable()->default(0); + $table->decimal('retribution_total_simbg', 18, 2)->nullable()->default(0); + $table->decimal('retribution_total_pad', 18, 2)->nullable()->default(0); + $table->decimal('penalty_amount', 18, 2)->nullable()->default(0); + $table->string('business_category')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pbg_task_payments', function (Blueprint $table) { + if (Schema::hasColumn('pbg_task_payments', 'pbg_task_id')) { + // Drop FK, revert to not nullable, recreate FK + $table->dropForeign(['pbg_task_id']); + $table->unsignedBigInteger('pbg_task_id')->nullable(false)->change(); + $table->foreign('pbg_task_id')->references('id')->on('pbg_task')->cascadeOnDelete(); + } + + // Revert pbg_task_uid to not nullable + if (Schema::hasColumn('pbg_task_payments', 'pbg_task_uid')) { + $table->string('pbg_task_uid')->nullable(false)->change(); + } + + // Drop the added columns + $columns = [ + 'row_no','consultation_type','source_registration_number','owner_name','building_location','building_function','building_name','application_date_raw', + 'verification_status','application_status','owner_address','owner_phone','owner_email','note_date_raw','document_shortage_note', + 'image_url','krk_kkpr','krk_number','lh','ska','remarks','helpdesk','person_in_charge','pbg_operator','ownership','taru_potential', + 'agency_validation','retribution_category','ba_tpt_number','ba_tpt_date_raw','ba_tpa_number','ba_tpa_date_raw', + 'skrd_number','skrd_date_raw','ptsp_status','issued_status','payment_date_raw','sts_format','issuance_year', + 'current_year','village','district','building_area','building_height','floor_count','unit_count','proposed_retribution','retribution_total_simbg', + 'retribution_total_pad','penalty_amount','business_category' + ]; + foreach ($columns as $col) { + if (Schema::hasColumn('pbg_task_payments', $col)) { + $table->dropColumn($col); + } + } + + // Restore legacy columns + if (!Schema::hasColumn('pbg_task_payments', 'registration_number')) { + $table->string('registration_number'); + $table->index('registration_number'); + } + if (!Schema::hasColumn('pbg_task_payments', 'sts_form_number')) { + $table->string('sts_form_number')->nullable(); + } + if (!Schema::hasColumn('pbg_task_payments', 'payment_date')) { + $table->date('payment_date')->nullable(); + } + if (!Schema::hasColumn('pbg_task_payments', 'pad_amount')) { + $table->decimal('pad_amount', 18, 2)->default(0); + } + }); + } +};