✨ feat(lpj-seeders): Inisialisasi master referensi, migrasi MIG, dan SQL seeding
- Menata LpjDatabaseSeeder untuk orkestrasi batch, aktifkan MigrationGambarInspeksiSeeder . - Migrasi domain MIG → LPJ lengkap dengan parseTimestamp , initializeErrorLog , logError . - Tambah seeders MIG eksternal & tim penilai; normalisasi mapping checkTujuanPenilaian . - Perluasan master: JFK009–JFK014, TP0007–TP00010, hubungan pemilik, KJPP; TeamsSeeder via SQL. - MasterDataSurveyorSeeder eksekusi SQL referensi (20+ tabel) via DB::unprepared . - Tambah puluhan SQL referensi (jenis, kondisi, sarana, posisi, spek, dll). - Normalisasi data inspeksi (duplikasi key dinamis), serialisasi JSON rapi. - Logging seragam ke app log + CSV error untuk audit trail.
This commit is contained in:
255
database/seeders/MigrationDebitureSeeder.php
Normal file
255
database/seeders/MigrationDebitureSeeder.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Basicdata\Models\Branch;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
|
||||
|
||||
class MigrationDebitureSeeder extends Seeder
|
||||
{
|
||||
protected $errorLogFile = __DIR__ . '/csv/debitures/error_migration.csv';
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// Bersihkan/Inisialisasi file error log
|
||||
$this->initializeErrorLog();
|
||||
|
||||
// Path ke file csv
|
||||
$filePath = realpath(__DIR__ . '/csv/debitures/debitur.latest.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/debitures/debitur.latest.csv');
|
||||
$this->command->error('File csv tidak ditemukan.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (($handle = fopen($filePath, 'r')) === false) {
|
||||
Log::error('Gagal membuka file CSV: ' . $filePath);
|
||||
$this->command->error('Gagal membuka file CSV.');
|
||||
return;
|
||||
}
|
||||
|
||||
$header = fgetcsv($handle, 0, '|');
|
||||
$rows = [];
|
||||
$batchSize = 500;
|
||||
$userData = [];
|
||||
$branchCache = [];
|
||||
$totalData = 0;
|
||||
|
||||
// Hitung total baris
|
||||
while (($data = fgetcsv($handle, 0, '|')) !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
fgetcsv($handle, 0, '|'); // Lewati header
|
||||
|
||||
$currentRow = 0;
|
||||
$batchCount = 0;
|
||||
|
||||
while (($data = fgetcsv($handle, 0, '|')) !== false) {
|
||||
if (count($data) != count($header)) {
|
||||
$this->logError($data[0] ?? '-', 'Jumlah kolom tidak sesuai');
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = array_combine($header, $data);
|
||||
$currentRow++;
|
||||
|
||||
if (count($rows) >= $batchSize) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $branchCache, $userData);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
|
||||
// print_r($rows);
|
||||
if (!empty($rows)) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $branchCache, $userData);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
$this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}");
|
||||
}
|
||||
|
||||
private function processBatch(array $rows, array &$branchCache, array &$userData)
|
||||
{
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
$kode = $row['mig_kd_debitur_seq'] ?? '-';
|
||||
|
||||
// Cek apakah sudah diproses sebelumnya
|
||||
$existingRecord = Debiture::where('mig_kd_debitur_seq', $kode)->first();
|
||||
if ($existingRecord && $existingRecord->processed_at) {
|
||||
$this->command->info("Data sudah diproses sebelumnya: $kode");
|
||||
//continue;
|
||||
}
|
||||
|
||||
// Ambil branch_id
|
||||
$branchId = $this->getBranchId($row['mig_kd_cabang'] ?? null, $branchCache);
|
||||
if (!$branchId) {
|
||||
$this->logError($row['mig_kd_cabang'], 'Cabang tidak ditemukan');
|
||||
//continue;
|
||||
}
|
||||
|
||||
// Ambil user IDs berdasarkan NIK
|
||||
$userIdUpdate = $this->getUserId($row['mig_user_update'] ?? null, $userData)['id'];
|
||||
$userIdOtorisasi = $this->getUserId($row['mig_user_oto'] ?? null, $userData)['id'];
|
||||
|
||||
// if (!$userIdUpdate || !$userIdOtorisasi) {
|
||||
// $this->logError($kode, 'Salah satu user tidak ditemukan');
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Mapping field user
|
||||
$mapUser = [
|
||||
'created_by' => $userIdUpdate,
|
||||
'updated_by' => $userIdUpdate,
|
||||
'authorized_by' => $userIdOtorisasi,
|
||||
];
|
||||
|
||||
// Parsing nomor HP
|
||||
$nomorHp = !empty($row['mig_phone']) ? preg_replace('/[^0-9]/', '', $row['mig_phone']) : null;
|
||||
$email = !empty($row['email']) ? $row['email'] : null;
|
||||
$nomorId = !empty($row['nomor_id']) ? $row['nomor_id'] : null;
|
||||
|
||||
// Parsing waktu
|
||||
$authorizedAt = $this->parseTimestamp($row['mig_tgl_oto'] ?? null);
|
||||
$createdAt = $this->parseTimestamp($row['created_at'] ?? null);
|
||||
$updatedAt = $this->parseTimestamp($row['updated_at'] ?? null);
|
||||
|
||||
if (!$createdAt || !$updatedAt) {
|
||||
$this->logError($kode, 'Gagal parsing created_at / updated_at');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Simpan data
|
||||
Debiture::updateOrCreate([
|
||||
'mig_kd_debitur_seq' => (int) strtok($row['mig_kd_debitur_seq'], '.'),
|
||||
], [
|
||||
'branch_id' => $branchId,
|
||||
'name' => $row['name'] ?? '',
|
||||
'cif' => (int) strtok($row['cif'],'.') ?: '0000000000',
|
||||
'phone' => $nomorHp,
|
||||
'nomor_id' => $nomorId,
|
||||
'email' => $email,
|
||||
'npwp' => null,
|
||||
'address' => $row['address'] ?? null,
|
||||
'authorized_at' => $authorizedAt,
|
||||
'authorized_by' => $mapUser['authorized_by'],
|
||||
'created_by' => $mapUser['created_by'] ?? null,
|
||||
'updated_by' => $mapUser['updated_by'] ?? null,
|
||||
'created_at' => $createdAt,
|
||||
'updated_at' => $updatedAt,
|
||||
'mig_kd_debitur_seq' => (int) strtok($row['mig_kd_debitur_seq'], '.'),
|
||||
'processed_at' => now(),
|
||||
'mig_debitur' => json_encode($row),
|
||||
'is_mig' => 1
|
||||
]);
|
||||
|
||||
$this->command->info("Proses data debiture $kode (" . ($index + 1) . '/' . count($rows) . ')');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->logError($row['mig_kd_debitur_seq'] ?? '-', "Error eksepsi: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getBranchId(?string $code, array &$cache): ?int
|
||||
{
|
||||
if (!$code) return null;
|
||||
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$branch = Branch::where('code', $code)->first();
|
||||
|
||||
if ($branch) {
|
||||
$cache[$code] = $branch->id;
|
||||
return $branch->id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getUserId(?string $code, array &$cache): array
|
||||
{
|
||||
if (!$code) return ['id' => null, 'branch_id' => null];
|
||||
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$user = User::where('nik', $code)->first();
|
||||
|
||||
if ($user) {
|
||||
$cache[$code] = ['id' => $user->id, 'branch_id' => $user->branch_id];
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
return ['id' => null, 'branch_id' => null];
|
||||
}
|
||||
|
||||
private function parseTimestamp(?string $timestamp): ?string
|
||||
{
|
||||
try {
|
||||
if ($timestamp) {
|
||||
// Cek jika format hanya tanggal (Y-m-d)
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $timestamp)) {
|
||||
return \Carbon\Carbon::createFromFormat('Y-m-d', $timestamp)
|
||||
->startOfDay()
|
||||
->toDateTimeString();
|
||||
}
|
||||
// Format lengkap (Y-m-d H:i:s)
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
|
||||
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
|
||||
}
|
||||
// Format d/m/Y H:i:s (contoh: 28/4/2017 14:43:43)
|
||||
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
|
||||
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function initializeErrorLog()
|
||||
{
|
||||
// Jika file lama ada, hapus
|
||||
if (file_exists($this->errorLogFile)) {
|
||||
unlink($this->errorLogFile);
|
||||
}
|
||||
|
||||
// Buat file baru dengan header
|
||||
$handle = fopen($this->errorLogFile, 'w');
|
||||
fputcsv($handle, ['mig_kd_debitur_seq', 'Error']);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function logError(string $kode, string $message)
|
||||
{
|
||||
// Catat ke log
|
||||
Log::error("Error migrasi debiture [$kode]: $message");
|
||||
|
||||
// Tulis ke file error
|
||||
$handle = fopen($this->errorLogFile, 'a');
|
||||
fputcsv($handle, [$kode, $message]);
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user