111 lines
3.7 KiB
PHP
111 lines
3.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\DrawResult;
|
|
use App\Models\ImportLog;
|
|
use App\Models\Member;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class MemberImportService
|
|
{
|
|
/**
|
|
* Import baris anggota. Hanya 3 medan diproses: nama, no_anggota, no_kp.
|
|
* Setiap import akan BUANG semua anggota lama dahulu (ganti penuh).
|
|
* Tidak crash bila ada duplicate/ralat — pulangkan ringkasan + simpan ImportLog.
|
|
*
|
|
* @param array<int, array<string, string>> $rows
|
|
*/
|
|
public function import(array $rows, string $filename, ?int $userId = null): ImportLog
|
|
{
|
|
$success = 0;
|
|
$duplicate = 0;
|
|
$failed = 0;
|
|
$errors = [];
|
|
|
|
// Lindung integriti cabutan: jangan buang anggota jika sudah ada pemenang disahkan.
|
|
if (DrawResult::where('status', DrawResult::STATUS_CONFIRMED)->exists()) {
|
|
return ImportLog::create([
|
|
'type' => 'members',
|
|
'filename' => $filename,
|
|
'total_rows' => count($rows),
|
|
'success_count' => 0,
|
|
'duplicate_count' => 0,
|
|
'failed_count' => count($rows),
|
|
'errors' => ['Import dibatalkan: sudah ada pemenang cabutan yang disahkan. Data anggota tidak boleh dibuang.'],
|
|
'imported_by' => $userId,
|
|
]);
|
|
}
|
|
|
|
// Set pra-muat untuk kesan duplicate dalam fail itu sendiri
|
|
$seenKp = [];
|
|
$seenAnggota = [];
|
|
|
|
DB::transaction(function () use ($rows, &$success, &$duplicate, &$failed, &$errors, &$seenKp, &$seenAnggota) {
|
|
// Buang semua anggota lama (ganti penuh setiap import)
|
|
Member::query()->delete();
|
|
|
|
foreach ($rows as $i => $row) {
|
|
$line = $i + 2; // baris fail (header = 1)
|
|
|
|
$nama = trim($row['nama'] ?? '');
|
|
$noKp = $this->clean($row['no_kp'] ?? '');
|
|
$noAnggota = $this->clean($row['no_anggota'] ?? '');
|
|
|
|
if ($nama === '') {
|
|
$failed++;
|
|
$errors[] = "Baris {$line}: nama wajib diisi.";
|
|
continue;
|
|
}
|
|
|
|
// Duplicate dalam fail
|
|
if ($noKp && isset($seenKp[$noKp])) {
|
|
$duplicate++;
|
|
$errors[] = "Baris {$line}: no_kp '{$noKp}' duplicate dalam fail.";
|
|
continue;
|
|
}
|
|
if ($noAnggota && isset($seenAnggota[$noAnggota])) {
|
|
$duplicate++;
|
|
$errors[] = "Baris {$line}: no_anggota '{$noAnggota}' duplicate dalam fail.";
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
Member::create([
|
|
'no_anggota' => $noAnggota ?: null,
|
|
'no_kp' => $noKp ?: null,
|
|
'nama' => $nama,
|
|
]);
|
|
|
|
if ($noKp) {
|
|
$seenKp[$noKp] = true;
|
|
}
|
|
if ($noAnggota) {
|
|
$seenAnggota[$noAnggota] = true;
|
|
}
|
|
$success++;
|
|
} catch (\Throwable $e) {
|
|
$failed++;
|
|
$errors[] = "Baris {$line}: {$e->getMessage()}";
|
|
}
|
|
}
|
|
});
|
|
|
|
return ImportLog::create([
|
|
'type' => 'members',
|
|
'filename' => $filename,
|
|
'total_rows' => count($rows),
|
|
'success_count' => $success,
|
|
'duplicate_count' => $duplicate,
|
|
'failed_count' => $failed,
|
|
'errors' => $errors ?: null,
|
|
'imported_by' => $userId,
|
|
]);
|
|
}
|
|
|
|
private function clean(string $value): string
|
|
{
|
|
return trim($value);
|
|
}
|
|
}
|