Compare commits
89 Commits
1caa7ebfdd
...
staging
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0129c57b0d | ||
|
|
2c56dd1d68 | ||
|
|
f402c0831a | ||
|
|
ceca0aa5e8 | ||
|
|
43361f81e7 | ||
|
|
58b53a0284 | ||
|
|
bb0da7626b | ||
|
|
8a6ab059f5 | ||
|
|
6cf4432642 | ||
|
|
7c5202021f | ||
|
|
e8a735e977 | ||
|
|
1c5b48ff1b | ||
|
|
6378ba0f98 | ||
|
|
2c8136dcf3 | ||
|
|
3315e1d4b6 | ||
|
|
37fb5c90d5 | ||
|
|
ff994a7c95 | ||
|
|
a2275758b1 | ||
|
|
783250d99a | ||
|
|
19057c7e81 | ||
|
|
9dfb8727dc | ||
|
|
bef7bcfa8f | ||
|
|
d9d8eaafcd | ||
|
|
f055cd5573 | ||
|
|
aee8fab832 | ||
|
|
a72cbe4bd9 | ||
|
|
ea09e8161c | ||
|
|
f051fa9507 | ||
|
|
04a657252f | ||
|
|
fc6f18fea9 | ||
|
|
8c60320532 | ||
|
|
34709b0f8f | ||
|
|
b2cfffc5d5 | ||
|
|
89329de198 | ||
|
|
70dda16699 | ||
|
|
db0b1f6cfc | ||
|
|
c153990c52 | ||
|
|
42d6e06a48 | ||
|
|
2db123a386 | ||
|
|
45f0d387fd | ||
|
|
7f9c58aabe | ||
|
|
3ce84b89b4 | ||
|
|
1dd4f8167e | ||
|
|
117b344857 | ||
|
|
0d5b6b1529 | ||
|
|
535be2cff3 | ||
|
|
1a67eb2000 | ||
|
|
e8ef9c0932 | ||
|
|
81f165c9d0 | ||
|
|
25011d1798 | ||
|
|
e773b82218 | ||
|
|
04ee3a0c48 | ||
|
|
10b5a6c96c | ||
|
|
3aca1d46c2 | ||
|
|
db55471111 | ||
|
|
112262d7d6 | ||
|
|
a1b9b7af86 | ||
|
|
e3540a1dd6 | ||
|
|
cd97d184ce | ||
|
|
929c56b079 | ||
|
|
388b65696f | ||
|
|
ba9089e2ac | ||
|
|
20d0061d42 | ||
|
|
4bef7cdafd | ||
|
|
bf728972b5 | ||
|
|
dfd2a82b42 | ||
|
|
c4bb3bea28 | ||
|
|
006dd44c64 | ||
|
|
698935d06f | ||
|
|
564c7ccac4 | ||
|
|
2b5556410d | ||
|
|
ee7c8ce97f | ||
|
|
17f7482080 | ||
|
|
6d137ad51c | ||
|
|
6c004812a9 | ||
|
|
d932559849 | ||
|
|
c3c40fdc27 | ||
|
|
41262e0317 | ||
|
|
2a1ecfd9e2 | ||
|
|
20833213b1 | ||
|
|
81159983cf | ||
|
|
bc35785c9c | ||
|
|
041ca943c9 | ||
|
|
312861a933 | ||
|
|
96657de512 | ||
|
|
4ad11593d5 | ||
|
|
d851ab58bc | ||
|
|
c08e050815 | ||
|
|
4aeecf6a97 |
83
Helpers/PdfHelper.php
Normal file
83
Helpers/PdfHelper.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Helpers;
|
||||
|
||||
class PdfHelper
|
||||
{
|
||||
/**
|
||||
* Format text for PDF output to handle special characters
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function formatText($text)
|
||||
{
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Common problematic characters and their safe replacements
|
||||
$replacements = [
|
||||
'<' => '<',
|
||||
'>' => '>',
|
||||
'&' => '&',
|
||||
'"' => '"',
|
||||
"'" => ''',
|
||||
'≤' => '≤',
|
||||
'≥' => '≥',
|
||||
'≠' => '!=',
|
||||
'≈' => '~',
|
||||
'×' => 'x',
|
||||
'÷' => '/',
|
||||
'–' => '-',
|
||||
'—' => '-',
|
||||
'' => '"',
|
||||
'' => '"',
|
||||
'' => "'",
|
||||
'' => "'",
|
||||
];
|
||||
|
||||
// First pass: replace with HTML entities
|
||||
$safeText = str_replace(array_keys($replacements), array_values($replacements), $text);
|
||||
|
||||
// Ensure UTF-8 encoding
|
||||
if (!mb_check_encoding($safeText, 'UTF-8')) {
|
||||
$safeText = mb_convert_encoding($safeText, 'UTF-8', 'auto');
|
||||
}
|
||||
|
||||
// Remove any remaining non-ASCII characters that could cause issues
|
||||
$safeText = preg_replace('/[^\x20-\x7E\xA0-\xFF]/', '', $safeText);
|
||||
|
||||
return $safeText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format mathematical symbols to text representation
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function formatMathSymbols($text)
|
||||
{
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$mathReplacements = [
|
||||
'<' => 'kurang dari',
|
||||
'>' => 'lebih dari',
|
||||
'<=' => 'kurang dari sama dengan',
|
||||
'>=' => 'lebih dari sama dengan',
|
||||
'!=' => 'tidak sama dengan',
|
||||
'==' => 'sama dengan',
|
||||
'≤' => 'kurang dari sama dengan',
|
||||
'≥' => 'lebih dari sama dengan',
|
||||
'≠' => 'tidak sama dengan',
|
||||
'≈' => 'kira-kira',
|
||||
'≡' => 'identik dengan',
|
||||
'≅' => 'hampir sama dengan',
|
||||
];
|
||||
|
||||
return str_replace(array_keys($mathReplacements), array_values($mathReplacements), $text);
|
||||
}
|
||||
}
|
||||
27
Http/Controllers/ImageController.php
Normal file
27
Http/Controllers/ImageController.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Modules\Lpj\app\Services\ImageResizeService;
|
||||
|
||||
class ImageController extends Controller
|
||||
{
|
||||
protected $imageService;
|
||||
|
||||
public function __construct(ImageResizeService $imageService)
|
||||
{
|
||||
$this->imageService = $imageService;
|
||||
}
|
||||
|
||||
public function show(Request $request, $path)
|
||||
{
|
||||
$width = $request->query('w');
|
||||
$quality = $request->query('q', 80);
|
||||
|
||||
$resizedPath = $this->imageService->resize($path, $width, null, $quality);
|
||||
|
||||
return response()->file(storage_path('app/public/' . $resizedPath));
|
||||
}
|
||||
}
|
||||
228
app/Console/Commands/CleanupInspeksiDataCommand.php
Normal file
228
app/Console/Commands/CleanupInspeksiDataCommand.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Jobs\CleanupInspeksiDataJob;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
use Modules\Lpj\Services\InspeksiCleanupService;
|
||||
|
||||
class CleanupInspeksiDataCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'lpj:cleanup-inspeksi
|
||||
{--permohonan-id= : ID permohonan yang akan di-cleanup (opsional)}
|
||||
{--sync : Jalankan secara synchronous}
|
||||
{--dry-run : Tampilkan preview tanpa menjalankan cleanup}
|
||||
{--force : Jalankan tanpa konfirmasi}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Cleanup data inspeksi lama yang tidak memiliki dokument_id';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Log::info('CleanupInspeksiDataCommand: Memulai proses cleanup data inspeksi', [
|
||||
'options' => $this->options()
|
||||
]);
|
||||
|
||||
try {
|
||||
$permohonanId = $this->option('permohonan-id');
|
||||
$sync = $this->option('sync');
|
||||
$dryRun = $this->option('dry-run');
|
||||
$force = $this->option('force');
|
||||
|
||||
// Validasi opsi
|
||||
if ($dryRun && $sync) {
|
||||
$this->error('Opsi --dry-run dan --sync tidak dapat digunakan bersamaan.');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// Tampilkan header
|
||||
$this->info('=== Cleanup Data Inspeksi ===');
|
||||
$this->newLine();
|
||||
|
||||
// Ambil data yang akan di-cleanup
|
||||
$cleanupData = $this->getCleanupData($permohonanId);
|
||||
|
||||
if ($cleanupData->isEmpty()) {
|
||||
$this->info('Tidak ada data yang perlu di-cleanup.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Tampilkan preview data
|
||||
$this->displayPreview($cleanupData);
|
||||
|
||||
if ($dryRun) {
|
||||
$this->info('Mode dry-run: Tidak ada perubahan yang dilakukan.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Konfirmasi jika tidak force
|
||||
if (!$force && !$this->confirm('Lanjutkan dengan cleanup?')) {
|
||||
$this->info('Cleanup dibatalkan.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Jalankan cleanup
|
||||
$this->runCleanup($cleanupData, $sync);
|
||||
|
||||
$this->info('Proses cleanup selesai.');
|
||||
return Command::SUCCESS;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CleanupInspeksiDataCommand: Terjadi error saat proses cleanup', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
$this->error('Terjadi kesalahan: ' . $e->getMessage());
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ambil data yang akan di-cleanup
|
||||
*/
|
||||
private function getCleanupData(?int $permohonanId = null)
|
||||
{
|
||||
$this->info('Mencari data yang akan di-cleanup...');
|
||||
|
||||
$query = DB::table('inspeksi as i')
|
||||
->select(
|
||||
'i.permohonan_id',
|
||||
'i.created_by',
|
||||
DB::raw('COUNT(CASE WHEN i.dokument_id IS NOT NULL AND i.deleted_at IS NULL THEN 1 END) as new_data_count'),
|
||||
DB::raw('COUNT(CASE WHEN i.dokument_id IS NULL AND i.deleted_at IS NULL THEN 1 END) as old_data_count')
|
||||
)
|
||||
->whereNull('i.deleted_at')
|
||||
->groupBy('i.permohonan_id', 'i.created_by');
|
||||
|
||||
if ($permohonanId) {
|
||||
$query->where('i.permohonan_id', $permohonanId);
|
||||
}
|
||||
|
||||
$results = $query->havingRaw('new_data_count > 0 AND old_data_count > 0')
|
||||
->get();
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan preview data
|
||||
*/
|
||||
private function displayPreview($cleanupData): void
|
||||
{
|
||||
$this->info('Data yang akan di-cleanup:');
|
||||
$this->table(
|
||||
['Permohonan ID', 'Created By', 'Data Baru', 'Data Lama'],
|
||||
$cleanupData->map(function ($item) {
|
||||
return [
|
||||
$item->permohonan_id,
|
||||
$item->created_by,
|
||||
$item->new_data_count,
|
||||
$item->old_data_count
|
||||
];
|
||||
})->toArray()
|
||||
);
|
||||
|
||||
$totalPermohonan = $cleanupData->count();
|
||||
$totalOldData = $cleanupData->sum('old_data_count');
|
||||
|
||||
$this->info("Total permohonan: {$totalPermohonan}");
|
||||
$this->info("Total data lama yang akan dihapus: {$totalOldData}");
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Jalankan cleanup
|
||||
*/
|
||||
private function runCleanup($cleanupData, bool $sync): void
|
||||
{
|
||||
$this->info('Memulai proses cleanup...');
|
||||
$this->newLine();
|
||||
|
||||
$progressBar = $this->output->createProgressBar($cleanupData->count());
|
||||
$progressBar->setFormat('Processing: %current%/%max% [%bar%] %percent:3s%% %message%');
|
||||
$progressBar->start();
|
||||
|
||||
$totalDeleted = 0;
|
||||
$totalErrors = 0;
|
||||
|
||||
foreach ($cleanupData as $data) {
|
||||
try {
|
||||
$progressBar->setMessage("Permohonan ID: {$data->permohonan_id}");
|
||||
|
||||
// Ambil data baru untuk mendapatkan dokument_id
|
||||
$newInspeksi = Inspeksi::where('permohonan_id', $data->permohonan_id)
|
||||
->where('created_by', $data->created_by)
|
||||
->whereNotNull('dokument_id')
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if (!$newInspeksi) {
|
||||
$this->warn("Tidak ditemukan data baru untuk permohonan {$data->permohonan_id}");
|
||||
$progressBar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Jalankan cleanup
|
||||
if ($sync) {
|
||||
// Jalankan sync
|
||||
$job = new CleanupInspeksiDataJob(
|
||||
$data->permohonan_id,
|
||||
$data->created_by,
|
||||
$newInspeksi->dokument_id
|
||||
);
|
||||
$job->handle();
|
||||
} else {
|
||||
// Dispatch ke queue
|
||||
CleanupInspeksiDataJob::dispatch(
|
||||
$data->permohonan_id,
|
||||
$data->created_by,
|
||||
$newInspeksi->dokument_id
|
||||
);
|
||||
}
|
||||
|
||||
$totalDeleted += $data->old_data_count;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CleanupInspeksiDataCommand: Error pada permohonan', [
|
||||
'permohonan_id' => $data->permohonan_id,
|
||||
'created_by' => $data->created_by,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
$this->error("Error pada permohonan {$data->permohonan_id}: {$e->getMessage()}");
|
||||
$totalErrors++;
|
||||
}
|
||||
|
||||
$progressBar->advance();
|
||||
}
|
||||
|
||||
$progressBar->finish();
|
||||
$this->newLine();
|
||||
$this->newLine();
|
||||
|
||||
// Tampilkan hasil
|
||||
$this->info('=== Hasil Cleanup ===');
|
||||
$this->info("Data lama yang dihapus: {$totalDeleted}");
|
||||
$this->info("Error: {$totalErrors}");
|
||||
|
||||
if (!$sync) {
|
||||
$this->info('Job telah di-dispatch ke queue. Monitor progress di log.');
|
||||
}
|
||||
}
|
||||
}
|
||||
192
app/Console/Commands/CleanupInspeksiStatusCommand.php
Normal file
192
app/Console/Commands/CleanupInspeksiStatusCommand.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
|
||||
class CleanupInspeksiStatusCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'lpj:cleanup-inspeksi-status
|
||||
{--permohonan-id= : Filter berdasarkan permohonan ID}
|
||||
{--created-by= : Filter berdasarkan user ID}
|
||||
{--detailed : Tampilkan detail data}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Cek status data inspeksi yang memerlukan cleanup';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
try {
|
||||
$permohonanId = $this->option('permohonan-id');
|
||||
$createdBy = $this->option('created-by');
|
||||
$detailed = $this->option('detailed');
|
||||
|
||||
$this->info('=== Status Data Inspeksi ===');
|
||||
$this->newLine();
|
||||
|
||||
// Ambil statistik umum
|
||||
$this->showGeneralStats();
|
||||
|
||||
// Ambil data yang memerlukan cleanup
|
||||
$this->showCleanupStats($permohonanId, $createdBy);
|
||||
|
||||
if ($detailed) {
|
||||
$this->showDetailedData($permohonanId, $createdBy);
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CleanupInspeksiStatusCommand: Terjadi error', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
$this->error('Terjadi kesalahan: ' . $e->getMessage());
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan statistik umum
|
||||
*/
|
||||
private function showGeneralStats(): void
|
||||
{
|
||||
$this->info('Statistik Umum:');
|
||||
|
||||
$totalData = Inspeksi::count();
|
||||
$activeData = Inspeksi::whereNull('deleted_at')->count();
|
||||
$deletedData = Inspeksi::whereNotNull('deleted_at')->count();
|
||||
$dataWithDokument = Inspeksi::whereNotNull('dokument_id')->count();
|
||||
$dataWithoutDokument = Inspeksi::whereNull('dokument_id')->count();
|
||||
|
||||
$this->table(
|
||||
['Metrik', 'Jumlah'],
|
||||
[
|
||||
['Total Data', number_format($totalData)],
|
||||
['Data Aktif', number_format($activeData)],
|
||||
['Data Terhapus (Soft)', number_format($deletedData)],
|
||||
['Data dengan Dokument ID', number_format($dataWithDokument)],
|
||||
['Data tanpa Dokument ID', number_format($dataWithoutDokument)],
|
||||
]
|
||||
);
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan statistik cleanup
|
||||
*/
|
||||
private function showCleanupStats(?int $permohonanId = null, ?int $createdBy = null): void
|
||||
{
|
||||
$this->info('Data yang Memerlukan Cleanup:');
|
||||
|
||||
$query = DB::table('inspeksi as i')
|
||||
->select(
|
||||
'i.permohonan_id',
|
||||
'i.created_by',
|
||||
DB::raw('COUNT(CASE WHEN i.dokument_id IS NOT NULL AND i.deleted_at IS NULL THEN 1 END) as new_data_count'),
|
||||
DB::raw('COUNT(CASE WHEN i.dokument_id IS NULL AND i.deleted_at IS NULL THEN 1 END) as old_data_count'),
|
||||
DB::raw('MIN(i.created_at) as oldest_data'),
|
||||
DB::raw('MAX(i.created_at) as newest_data')
|
||||
)
|
||||
->whereNull('i.deleted_at')
|
||||
->groupBy('i.permohonan_id', 'i.created_by');
|
||||
|
||||
if ($permohonanId) {
|
||||
$query->where('i.permohonan_id', $permohonanId);
|
||||
}
|
||||
|
||||
if ($createdBy) {
|
||||
$query->where('i.created_by', $createdBy);
|
||||
}
|
||||
|
||||
$results = $query->havingRaw('new_data_count > 0 AND old_data_count > 0')
|
||||
->orderBy('old_data_count', 'desc')
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
if ($results->isEmpty()) {
|
||||
$this->info('Tidak ada data yang memerlukan cleanup.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['Permohonan ID', 'Created By', 'Data Baru', 'Data Lama', 'Data Terlama', 'Data Terbaru'],
|
||||
$results->map(function ($item) {
|
||||
return [
|
||||
$item->permohonan_id,
|
||||
$item->created_by,
|
||||
$item->new_data_count,
|
||||
$item->old_data_count,
|
||||
$item->oldest_data,
|
||||
$item->newest_data,
|
||||
];
|
||||
})->toArray()
|
||||
);
|
||||
|
||||
$totalPermohonan = $results->count();
|
||||
$totalOldData = $results->sum('old_data_count');
|
||||
|
||||
$this->info("Total permohonan yang perlu cleanup: {$totalPermohonan}");
|
||||
$this->info("Total data lama yang akan dihapus: {$totalOldData}");
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan detail data
|
||||
*/
|
||||
private function showDetailedData(?int $permohonanId = null, ?int $createdBy = null): void
|
||||
{
|
||||
$this->info('Detail Data (20 data terbaru):');
|
||||
|
||||
$query = Inspeksi::with(['permohonan', 'dokument'])
|
||||
->whereNull('deleted_at');
|
||||
|
||||
if ($permohonanId) {
|
||||
$query->where('permohonan_id', $permohonanId);
|
||||
}
|
||||
|
||||
if ($createdBy) {
|
||||
$query->where('created_by', $createdBy);
|
||||
}
|
||||
|
||||
$data = $query->orderBy('created_at', 'desc')
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
if ($data->isEmpty()) {
|
||||
$this->info('Tidak ada data untuk ditampilkan.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['ID', 'Permohonan ID', 'Created By', 'Dokument ID', 'Status', 'Created At'],
|
||||
$data->map(function ($item) {
|
||||
return [
|
||||
$item->id,
|
||||
$item->permohonan_id,
|
||||
$item->created_by,
|
||||
$item->dokument_id ?? '-',
|
||||
$item->status,
|
||||
$item->created_at,
|
||||
];
|
||||
})->toArray()
|
||||
);
|
||||
}
|
||||
}
|
||||
99
app/Console/Commands/CleanupSingleInspeksiCommand.php
Normal file
99
app/Console/Commands/CleanupSingleInspeksiCommand.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Jobs\CleanupInspeksiDataJob;
|
||||
use Modules\Lpj\Services\InspeksiCleanupService;
|
||||
|
||||
class CleanupSingleInspeksiCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'lpj:cleanup-single-inspeksi
|
||||
{permohonan-id : ID permohonan yang akan di-cleanup}
|
||||
{created-by : ID user yang membuat data}
|
||||
{--sync : Jalankan secara synchronous}
|
||||
{--force : Jalankan tanpa konfirmasi}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Cleanup data inspeksi untuk 1 permohonan dan user tertentu';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Log::info('CleanupSingleInspeksiCommand: Memulai proses cleanup', [
|
||||
'permohonan_id' => $this->argument('permohonan-id'),
|
||||
'created_by' => $this->argument('created-by'),
|
||||
'sync' => $this->option('sync')
|
||||
]);
|
||||
|
||||
try {
|
||||
$permohonanId = (int) $this->argument('permohonan-id');
|
||||
$createdBy = (int) $this->argument('created-by');
|
||||
$sync = $this->option('sync');
|
||||
$force = $this->option('force');
|
||||
|
||||
// Validasi input
|
||||
if ($permohonanId <= 0) {
|
||||
$this->error('Permohonan ID harus angka positif.');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if ($createdBy <= 0) {
|
||||
$this->error('Created By harus angka positif.');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// Tampilkan info
|
||||
$this->info('=== Cleanup Single Inspeksi ===');
|
||||
$this->info("Permohonan ID: {$permohonanId}");
|
||||
$this->info("Created By: {$createdBy}");
|
||||
$this->info("Mode: " . ($sync ? 'Synchronous' : 'Queue'));
|
||||
$this->newLine();
|
||||
|
||||
// Konfirmasi jika tidak force
|
||||
if (!$force && !$this->confirm('Lanjutkan dengan cleanup?')) {
|
||||
$this->info('Cleanup dibatalkan.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Jalankan cleanup
|
||||
$cleanupService = new InspeksiCleanupService();
|
||||
|
||||
if ($sync) {
|
||||
$this->info('Menjalankan cleanup secara synchronous...');
|
||||
$cleanupService->cleanupSync($permohonanId, $createdBy);
|
||||
$this->info('Cleanup selesai.');
|
||||
} else {
|
||||
$this->info('Mengirim job ke queue...');
|
||||
$cleanupService->cleanupAsync($permohonanId, $createdBy);
|
||||
$this->info('Job telah di-dispatch ke queue.');
|
||||
$this->info('Monitor progress di log.');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CleanupSingleInspeksiCommand: Terjadi error saat proses cleanup', [
|
||||
'permohonan_id' => $this->argument('permohonan-id'),
|
||||
'created_by' => $this->argument('created-by'),
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
$this->error('Terjadi kesalahan: ' . $e->getMessage());
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
123
app/Console/README.md
Normal file
123
app/Console/README.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Console Commands untuk Cleanup Data Inspeksi
|
||||
|
||||
## Daftar Command
|
||||
|
||||
### 1. `lpj:cleanup-inspeksi`
|
||||
Command utama untuk cleanup data inspeksi secara batch.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
php artisan lpj:cleanup-inspeksi [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `--permohonan-id=ID` - Filter berdasarkan permohonan ID (opsional)
|
||||
- `--sync` - Jalankan secara synchronous
|
||||
- `--dry-run` - Tampilkan preview tanpa menjalankan cleanup
|
||||
- `--force` - Jalankan tanpa konfirmasi
|
||||
|
||||
**Contoh Penggunaan:**
|
||||
```bash
|
||||
# Preview data yang akan di-cleanup
|
||||
php artisan lpj:cleanup-inspeksi --dry-run
|
||||
|
||||
# Cleanup semua data (dengan konfirmasi)
|
||||
php artisan lpj:cleanup-inspeksi
|
||||
|
||||
# Cleanup untuk permohonan tertentu
|
||||
php artisan lpj:cleanup-inspeksi --permohonan-id=123 --force
|
||||
|
||||
# Jalankan secara sync
|
||||
php artisan lpj:cleanup-inspeksi --sync --force
|
||||
```
|
||||
|
||||
### 2. `lpj:cleanup-single-inspeksi`
|
||||
Command untuk cleanup 1 permohonan dan user tertentu.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
php artisan lpj:cleanup-single-inspeksi <permohonan-id> <created-by> [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
- `permohonan-id` - ID permohonan yang akan di-cleanup (required)
|
||||
- `created-by` - ID user yang membuat data (required)
|
||||
|
||||
**Options:**
|
||||
- `--sync` - Jalankan secara synchronous
|
||||
- `--force` - Jalankan tanpa konfirmasi
|
||||
|
||||
**Contoh Penggunaan:**
|
||||
```bash
|
||||
# Cleanup untuk permohonan 123 oleh user 456
|
||||
php artisan lpj:cleanup-single-inspeksi 123 456
|
||||
|
||||
# Jalankan secara sync tanpa konfirmasi
|
||||
php artisan lpj:cleanup-single-inspeksi 123 456 --sync --force
|
||||
```
|
||||
|
||||
### 3. `lpj:cleanup-inspeksi-status`
|
||||
Command untuk mengecek status data inspeksi dan melihat statistik cleanup.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
php artisan lpj:cleanup-inspeksi-status [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `--permohonan-id=ID` - Filter berdasarkan permohonan ID
|
||||
- `--created-by=ID` - Filter berdasarkan user ID
|
||||
- `--detailed` - Tampilkan detail data
|
||||
|
||||
**Contoh Penggunaan:**
|
||||
```bash
|
||||
# Lihat statistik umum
|
||||
php artisan lpj:cleanup-inspeksi-status
|
||||
|
||||
# Filter berdasarkan permohonan
|
||||
php artisan lpj:cleanup-inspeksi-status --permohonan-id=123
|
||||
|
||||
# Tampilkan detail data
|
||||
php artisan lpj:cleanup-inspeksi-status --detailed
|
||||
```
|
||||
|
||||
## Scheduling
|
||||
|
||||
Command cleanup otomatis dijalankan setiap hari jam 2 pagi dan setiap minggu. Konfigurasi scheduling ada di `LpjServiceProvider.php`.
|
||||
|
||||
## Monitoring
|
||||
|
||||
Semua aktivitas cleanup dicatat di log file:
|
||||
- `storage/logs/laravel.log` - Log umum
|
||||
- `storage/logs/cleanup-inspeksi.log` - Log cleanup harian
|
||||
- `storage/logs/cleanup-inspeksi-weekly.log` - Log cleanup mingguan
|
||||
|
||||
## Alur Kerja Cleanup
|
||||
|
||||
1. **Identifikasi**: Cari data inspeksi yang memiliki:
|
||||
- Data baru dengan `dokument_id` (tidak null)
|
||||
- Data lama tanpa `dokument_id` (null)
|
||||
- Sama `permohonan_id` dan `created_by`
|
||||
|
||||
2. **Proses**: Soft delete data lama menggunakan Laravel SoftDeletes
|
||||
|
||||
3. **Logging**: Catat setiap operasi untuk audit trail
|
||||
|
||||
4. **Transaction**: Gunakan DB transaction untuk konsistensi data
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command tidak muncul
|
||||
Pastikan service provider sudah diregister dengan benar:
|
||||
```bash
|
||||
php artisan list | grep lpj
|
||||
```
|
||||
|
||||
### Data tidak ter-cleanup
|
||||
- Cek log untuk error
|
||||
- Pastikan ada data yang memenuhi kriteria
|
||||
- Gunakan `--dry-run` untuk preview
|
||||
- Gunakan `--detailed` untuk melihat detail data
|
||||
|
||||
### Performance
|
||||
Untuk data besar, gunakan mode queue (default) daripada `--sync`
|
||||
154
app/Emails/SendJadwalKunjunganEmailPHPMailer.php
Normal file
154
app/Emails/SendJadwalKunjunganEmailPHPMailer.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Emails;
|
||||
|
||||
use App\Services\PHPMailerService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
class SendJadwalKunjunganEmailPHPMailer
|
||||
{
|
||||
/**
|
||||
* Data jadwal kunjungan
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Waktu penilaian
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $waktu_penilaian;
|
||||
|
||||
/**
|
||||
* Deskripsi penilaian
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $deskripsi_penilaian;
|
||||
|
||||
protected $phpMailerService;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param array $emailData
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $emailData)
|
||||
{
|
||||
// Validasi data yang diterima
|
||||
if (!isset($emailData['emailData']['id']) ||
|
||||
!isset($emailData['emailData']['waktu_penilaian']) ||
|
||||
!isset($emailData['emailData']['deskripsi_penilaian'])) {
|
||||
throw new \InvalidArgumentException("Data email tidak lengkap.");
|
||||
}
|
||||
|
||||
$this->id = $emailData['emailData']['id'];
|
||||
$this->waktu_penilaian = $emailData['emailData']['waktu_penilaian'];
|
||||
$this->deskripsi_penilaian = $emailData['emailData']['deskripsi_penilaian'];
|
||||
|
||||
// Inisialisasi PHPMailerService
|
||||
$this->phpMailerService = new PHPMailerService([
|
||||
'host' => config('mail.mailers.phpmailer.host', 'mail.ag.co.id'),
|
||||
'port' => config('mail.mailers.phpmailer.port', 465),
|
||||
'username' => config('mail.mailers.phpmailer.username', 'BAGI@ag.co.id'),
|
||||
'password' => config('mail.mailers.phpmailer.password', 'BAG@202!'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build(): self
|
||||
{
|
||||
return $this->view('lpj::emails.jadwal-kunjungan');
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim email menggunakan PHPMailer dengan dukungan attachment
|
||||
*
|
||||
* @param mixed $recipients
|
||||
* @param array $attachments
|
||||
* @return array
|
||||
*/
|
||||
public function sendWithPHPMailer($recipients, $attachments = [])
|
||||
{
|
||||
try {
|
||||
// Build HTML content
|
||||
$htmlContent = $this->buildHtml();
|
||||
|
||||
// Setup PHPMailer
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
// Server settings - menggunakan konfigurasi yang sama seperti EmailController
|
||||
$mail->isSMTP();
|
||||
$mail->Host = config('mail.mailers.smtp.host', 'mail.ag.co.id');
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = config('mail.mailers.smtp.username', 'BAGI@ag.co.id');
|
||||
$mail->Password = config('mail.mailers.smtp.password', 'BAG@202!');
|
||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||
$mail->Port = config('mail.mailers.smtp.port', 465);
|
||||
|
||||
// Bypass SSL Verification - sama seperti EmailController
|
||||
$mail->SMTPOptions = array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
'allow_self_signed' => true
|
||||
)
|
||||
);
|
||||
|
||||
// Recipients
|
||||
if (is_array($recipients)) {
|
||||
foreach ($recipients as $recipient) {
|
||||
$mail->addAddress($recipient);
|
||||
}
|
||||
} else {
|
||||
$mail->addAddress($recipients);
|
||||
}
|
||||
|
||||
// Content
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = 'Jadwal Kunjungan Penilaian Resmi';
|
||||
$mail->Body = $htmlContent;
|
||||
$mail->AltBody = strip_tags($htmlContent);
|
||||
|
||||
// Add attachments if provided
|
||||
foreach ($attachments as $attachment) {
|
||||
if (file_exists($attachment)) {
|
||||
$mail->addAttachment($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
$mail->send();
|
||||
|
||||
Log::info('Email jadwal kunjungan berhasil dikirim menggunakan PHPMailer', [
|
||||
'recipients' => $recipients,
|
||||
'attachments' => $attachments
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Email berhasil dikirim'
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Gagal mengirim email jadwal kunjungan menggunakan PHPMailer', [
|
||||
'recipients' => $recipients,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
144
app/Emails/SendPenawaranKJPPEmailPHPMailer.php
Normal file
144
app/Emails/SendPenawaranKJPPEmailPHPMailer.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Emails;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Services\PHPMailerService;
|
||||
|
||||
class SendPenawaranKJPPEmailPHPMailer extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
// Tambahkan properti untuk data yang akan dikirimkan ke view
|
||||
public $dp1;
|
||||
public $penawaran;
|
||||
public $permohonan;
|
||||
public $villages;
|
||||
public $districts;
|
||||
public $cities;
|
||||
public $provinces;
|
||||
public $user; // Tambahkan user ke data yang dikirimkan ke view, sebagai cc dan bcc
|
||||
|
||||
protected $phpMailerService;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($dp1, $penawaran, $permohonan, $villages, $districts, $cities, $provinces, $user)
|
||||
{
|
||||
// Assign data yang diterima ke properti
|
||||
$this->dp1 = $dp1;
|
||||
$this->penawaran = $penawaran;
|
||||
$this->permohonan = $permohonan;
|
||||
$this->villages = $villages;
|
||||
$this->districts = $districts;
|
||||
$this->cities = $cities;
|
||||
$this->provinces = $provinces;
|
||||
$this->user = $user; // Tambahkan user ke data yang dikirimkan ke view, sebagai cc dan bcc
|
||||
|
||||
// Inisialisasi PHPMailerService
|
||||
$this->phpMailerService = new PHPMailerService([
|
||||
'host' => config('mail.mailers.phpmailer.host', 'mail.ag.co.id'),
|
||||
'port' => config('mail.mailers.phpmailer.port', 465),
|
||||
'username' => config('mail.mailers.phpmailer.username', 'BAGI@ag.co.id'),
|
||||
'password' => config('mail.mailers.phpmailer.password', 'BAG@202!'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*/
|
||||
public function build(): self
|
||||
{
|
||||
// Kirim data ke view
|
||||
return $this->view('lpj::penawaran.kirimEmailKJPP')
|
||||
->with([
|
||||
'dp1' => $this->dp1,
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user // Tambahkan user ke data yang dikirimkan ke view, sebagai cc dan bcc
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim email menggunakan PHPMailer
|
||||
*/
|
||||
public function sendWithPHPMailer($recipients)
|
||||
{
|
||||
// Gunakan view yang dapat diakses secara global untuk testing
|
||||
// Jika data adalah array (testing), gunakan view sederhana
|
||||
// Jika data adalah object (production), gunakan view asli
|
||||
if (is_array($this->dp1) || is_array($this->penawaran) || is_array($this->permohonan)) {
|
||||
// Mode testing - gunakan view sederhana
|
||||
$htmlContent = view('emails.test-kjpp-phpmailer', [
|
||||
'dp1' => $this->dp1,
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user
|
||||
])->render();
|
||||
} else {
|
||||
// Mode production - gunakan view asli
|
||||
$htmlContent = view('lpj::penawaran.kirimEmailKJPP', [
|
||||
'dp1' => $this->dp1,
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user
|
||||
])->render();
|
||||
}
|
||||
|
||||
// Siapkan data untuk PHPMailer
|
||||
$data = [
|
||||
'from' => [
|
||||
'address' => config('mail.from.address', 'BAGI@ag.co.id'),
|
||||
'name' => config('mail.from.name', 'Notifikasi Sistem Laravel')
|
||||
],
|
||||
'to' => [],
|
||||
'cc' => [],
|
||||
'bcc' => [],
|
||||
'subject' => 'Penawaran KJPP Tender',
|
||||
'html' => $htmlContent,
|
||||
'text' => strip_tags($htmlContent)
|
||||
];
|
||||
|
||||
// Proses recipients
|
||||
if (is_array($recipients)) {
|
||||
foreach ($recipients as $recipient) {
|
||||
if (is_string($recipient)) {
|
||||
$data['to'][] = ['address' => $recipient, 'name' => ''];
|
||||
} elseif (is_array($recipient)) {
|
||||
$data['to'][] = [
|
||||
'address' => $recipient['address'] ?? $recipient['email'] ?? '',
|
||||
'name' => $recipient['name'] ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data['to'][] = ['address' => $recipients, 'name' => ''];
|
||||
}
|
||||
|
||||
// Tambahkan CC dan BCC dari user jika ada
|
||||
if ($this->user && isset($this->user->email)) {
|
||||
$data['cc'][] = [
|
||||
'address' => $this->user->email,
|
||||
'name' => $this->user->name ?? 'User'
|
||||
];
|
||||
}
|
||||
|
||||
// Kirim email menggunakan PHPMailerService
|
||||
return $this->phpMailerService->sendEmail($data);
|
||||
}
|
||||
}
|
||||
@@ -2,28 +2,148 @@
|
||||
|
||||
namespace Modules\Lpj\Emails;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
// use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use App\Mail\PHPMailerMailable;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SendPenawaranTenderEmail extends Mailable
|
||||
class SendPenawaranTenderEmail extends PHPMailerMailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
protected $penawaran;
|
||||
protected $permohonan;
|
||||
protected $villages;
|
||||
protected $districts;
|
||||
protected $cities;
|
||||
protected $provinces;
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct($penawaran, $permohonan, $villages, $districts, $cities, $provinces, $user)
|
||||
{
|
||||
//
|
||||
$this->penawaran = $penawaran;
|
||||
$this->permohonan = $permohonan;
|
||||
$this->villages = $villages;
|
||||
$this->districts = $districts;
|
||||
$this->cities = $cities;
|
||||
$this->provinces = $provinces;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*/
|
||||
public function build(): self
|
||||
public function buildHtml(): string
|
||||
{
|
||||
return $this->view('lpj::penawaran.kirimEmail');
|
||||
// Gunakan view test untuk testing atau view asli untuk produksi
|
||||
$viewName = 'emails.test-penawaran-tender-phpmailer';
|
||||
|
||||
// Cek apakah data berbentuk array atau object untuk menentukan view yang digunakan
|
||||
if (is_array($this->penawaran) || is_object($this->penawaran)) {
|
||||
// Gunakan view test untuk testing
|
||||
return view($viewName, [
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user
|
||||
])->render();
|
||||
}
|
||||
|
||||
// Fallback ke view asli jika diperlukan
|
||||
return view('lpj::penawaran.kirimEmail', [
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user
|
||||
])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email using PHPMailer
|
||||
*/
|
||||
public function sendWithPHPMailer($recipients, $attachments = [])
|
||||
{
|
||||
try {
|
||||
$htmlContent = $this->buildHtml();
|
||||
|
||||
// Konfigurasi PHPMailer
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
// Server settings - menggunakan konfigurasi yang sudah terbukti berhasil
|
||||
$mail->isSMTP();
|
||||
$mail->Host = 'mail.ag.co.id';
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = 'BAGI@ag.co.id';
|
||||
$mail->Password = 'BAG@202!';
|
||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||
$mail->Port = 465;
|
||||
|
||||
// Bypass SSL Verification untuk menghindari certificate verify failed
|
||||
$mail->SMTPOptions = array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
'allow_self_signed' => true
|
||||
)
|
||||
);
|
||||
|
||||
// Recipients
|
||||
if (is_array($recipients)) {
|
||||
foreach ($recipients as $recipient) {
|
||||
$mail->addAddress($recipient);
|
||||
}
|
||||
} else {
|
||||
$mail->addAddress($recipients);
|
||||
}
|
||||
|
||||
// Content
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = 'Penawaran Tender Baru';
|
||||
$mail->Body = $htmlContent;
|
||||
$mail->AltBody = strip_tags($htmlContent);
|
||||
|
||||
// Add attachments if provided
|
||||
foreach ($attachments as $attachment) {
|
||||
if (is_array($attachment) && isset($attachment['path'])) {
|
||||
// Format array dengan nama custom
|
||||
$attachmentName = $attachment['name'] ?? basename($attachment['path']);
|
||||
if (file_exists($attachment['path'])) {
|
||||
$mail->addAttachment($attachment['path'], $attachmentName);
|
||||
}
|
||||
} elseif (is_string($attachment) && file_exists($attachment)) {
|
||||
// Format string sederhana
|
||||
$mail->addAttachment($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
$mail->send();
|
||||
|
||||
Log::info('Email penawaran tender berhasil dikirim menggunakan PHPMailer', [
|
||||
'recipients' => $recipients,
|
||||
'attachments' => $attachments
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Email berhasil dikirim'
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Gagal mengirim email penawaran tender menggunakan PHPMailer', [
|
||||
'recipients' => $recipients,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
132
app/Emails/SendPenawaranTenderEmailPHPMailer.php
Normal file
132
app/Emails/SendPenawaranTenderEmailPHPMailer.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Emails;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Services\PHPMailerService;
|
||||
|
||||
class SendPenawaranTenderEmailPHPMailer extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $penawaran;
|
||||
public $permohonan;
|
||||
public $villages;
|
||||
public $districts;
|
||||
public $cities;
|
||||
public $provinces;
|
||||
public $user;
|
||||
|
||||
protected $phpMailerService;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($penawaran = null, $permohonan = null, $villages = null, $districts = null, $cities = null, $provinces = null, $user = null)
|
||||
{
|
||||
$this->penawaran = $penawaran;
|
||||
$this->permohonan = $permohonan;
|
||||
$this->villages = $villages;
|
||||
$this->districts = $districts;
|
||||
$this->cities = $cities;
|
||||
$this->provinces = $provinces;
|
||||
$this->user = $user;
|
||||
|
||||
// Inisialisasi PHPMailerService
|
||||
$this->phpMailerService = new PHPMailerService([
|
||||
'host' => config('mail.mailers.phpmailer.host', 'mail.ag.co.id'),
|
||||
'port' => config('mail.mailers.phpmailer.port', 465),
|
||||
'username' => config('mail.mailers.phpmailer.username', 'BAGI@ag.co.id'),
|
||||
'password' => config('mail.mailers.phpmailer.password', 'BAG@202!'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*/
|
||||
public function build(): self
|
||||
{
|
||||
return $this->view('lpj::penawaran.kirimEmail');
|
||||
}
|
||||
|
||||
/**
|
||||
* Kirim email menggunakan PHPMailer dengan dukungan attachment
|
||||
*
|
||||
* @param mixed $recipients
|
||||
* @param array $attachments
|
||||
* @return array
|
||||
*/
|
||||
public function sendWithPHPMailer($recipients, $attachments = [])
|
||||
{
|
||||
// Gunakan view yang dapat diakses secara global untuk testing
|
||||
// Jika data adalah array (testing), gunakan view sederhana
|
||||
// Jika data adalah object (production), gunakan view asli
|
||||
if (is_array($this->penawaran) || is_array($this->permohonan) || is_array($this->villages)) {
|
||||
// Mode testing - gunakan view sederhana
|
||||
$htmlContent = view('emails.test-penawaran-tender-phpmailer', [
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user
|
||||
])->render();
|
||||
} else {
|
||||
// Mode production - gunakan view asli
|
||||
$htmlContent = view('lpj::penawaran.kirimEmail', [
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user
|
||||
])->render();
|
||||
}
|
||||
|
||||
// Siapkan data untuk PHPMailer
|
||||
$data = [
|
||||
'from' => [
|
||||
'address' => config('mail.from.address', 'BAGI@ag.co.id'),
|
||||
'name' => config('mail.from.name', 'Notifikasi Sistem Laravel')
|
||||
],
|
||||
'to' => [],
|
||||
'cc' => [],
|
||||
'bcc' => [],
|
||||
'subject' => 'Penawaran Tender',
|
||||
'html' => $htmlContent,
|
||||
'text' => strip_tags($htmlContent),
|
||||
'attachments' => $attachments
|
||||
];
|
||||
|
||||
// Proses recipients
|
||||
if (is_array($recipients)) {
|
||||
foreach ($recipients as $recipient) {
|
||||
if (is_string($recipient)) {
|
||||
$data['to'][] = ['address' => $recipient, 'name' => ''];
|
||||
} elseif (is_array($recipient)) {
|
||||
$data['to'][] = [
|
||||
'address' => $recipient['address'] ?? $recipient['email'] ?? '',
|
||||
'name' => $recipient['name'] ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data['to'][] = ['address' => $recipients, 'name' => ''];
|
||||
}
|
||||
|
||||
// Tambahkan CC dan BCC dari user jika ada
|
||||
if ($this->user && isset($this->user->email)) {
|
||||
$data['cc'][] = [
|
||||
'address' => $this->user->email,
|
||||
'name' => $this->user->name ?? 'User'
|
||||
];
|
||||
}
|
||||
|
||||
// Kirim email menggunakan PHPMailerService
|
||||
return $this->phpMailerService->sendEmail($data);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ class LaporanAdminKreditExport implements WithColumnFormatting, WithHeadings, Fr
|
||||
$row->nilai_pasar_wajar,
|
||||
$row->nilai_likuidasi,
|
||||
$row->nama_penilai,
|
||||
$row->kolektibilitas,
|
||||
$row->keterangan,
|
||||
$row->created_at
|
||||
];
|
||||
}
|
||||
@@ -52,6 +54,8 @@ class LaporanAdminKreditExport implements WithColumnFormatting, WithHeadings, Fr
|
||||
'Nilai Pasar Wajar',
|
||||
'Nilai Likuidasi',
|
||||
'Nama Penilai',
|
||||
'Kolektibilitas',
|
||||
'Keterangan',
|
||||
'Created At'
|
||||
];
|
||||
}
|
||||
@@ -64,7 +68,9 @@ class LaporanAdminKreditExport implements WithColumnFormatting, WithHeadings, Fr
|
||||
'J' => NumberFormat::FORMAT_DATE_DDMMYYYY,
|
||||
'K' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1,
|
||||
'L' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1,
|
||||
'M' => NumberFormat::FORMAT_DATE_DATETIME,
|
||||
'N' => NumberFormat::FORMAT_TEXT, // Kolektibilitas
|
||||
'O' => NumberFormat::FORMAT_TEXT, // Keterangan
|
||||
'P' => NumberFormat::FORMAT_DATE_DATETIME, // Created At (moved from M to P)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
117
app/Exports/LaporanSlikExport.php
Normal file
117
app/Exports/LaporanSlikExport.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Exports;
|
||||
|
||||
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class LaporanSlikExport implements FromQuery, WithHeadings, WithMapping, WithStyles
|
||||
{
|
||||
protected $query;
|
||||
|
||||
public function __construct($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
public function query()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'Sandi Bank',
|
||||
'Kode Kantor',
|
||||
'Kode Cabang',
|
||||
'Tahun',
|
||||
'Bulan',
|
||||
'No Rekening',
|
||||
'CIF',
|
||||
'Nama Debitur',
|
||||
'NPWP',
|
||||
'No KTP',
|
||||
'No Telp',
|
||||
'Alamat',
|
||||
'Kode Pos',
|
||||
'Kode Kab/Kota',
|
||||
'Kode Negara Domisili',
|
||||
'Kode Jenis',
|
||||
'Kode Sifat',
|
||||
'Kode Valuta',
|
||||
'Baki Debet',
|
||||
'Kolektibilitas',
|
||||
'Tanggal Mulai',
|
||||
'Tanggal Jatuh Tempo',
|
||||
'Tanggal Selesai',
|
||||
'Tanggal Restrukturisasi',
|
||||
'Kode Sebab Macet',
|
||||
'Tanggal Macet',
|
||||
'Kode Kondisi',
|
||||
'Tanggal Kondisi',
|
||||
'Nilai Agunan',
|
||||
'Jenis Agunan',
|
||||
'Kode Agunan',
|
||||
'Peringkat Agunan',
|
||||
'Fasilitas',
|
||||
'Status Agunan',
|
||||
'Tanggal Lapor',
|
||||
'Status',
|
||||
'Tanggal Dibuat',
|
||||
];
|
||||
}
|
||||
|
||||
public function map($laporanSlik): array
|
||||
{
|
||||
return [
|
||||
$laporanSlik->sandi_bank,
|
||||
$laporanSlik->kode_kantor,
|
||||
$laporanSlik->kode_cabang,
|
||||
$laporanSlik->tahun,
|
||||
$laporanSlik->bulan,
|
||||
$laporanSlik->no_rekening,
|
||||
$laporanSlik->cif,
|
||||
$laporanSlik->nama_debitur,
|
||||
$laporanSlik->npwp,
|
||||
$laporanSlik->no_ktp,
|
||||
$laporanSlik->no_telp,
|
||||
$laporanSlik->alamat,
|
||||
$laporanSlik->kode_pos,
|
||||
$laporanSlik->kode_kab_kota,
|
||||
$laporanSlik->kode_negara_domisili,
|
||||
$laporanSlik->kode_jenis,
|
||||
$laporanSlik->kode_sifat,
|
||||
$laporanSlik->kode_valuta,
|
||||
$laporanSlik->baki_debet,
|
||||
$laporanSlik->kolektibilitas,
|
||||
$laporanSlik->tanggal_mulai,
|
||||
$laporanSlik->tanggal_jatuh_tempo,
|
||||
$laporanSlik->tanggal_selesai,
|
||||
$laporanSlik->tanggal_restrukturisasi,
|
||||
$laporanSlik->kode_sebab_macet,
|
||||
$laporanSlik->tanggal_macet,
|
||||
$laporanSlik->kode_kondisi,
|
||||
$laporanSlik->tanggal_kondisi,
|
||||
$laporanSlik->nilai_agunan,
|
||||
$laporanSlik->jenis_agunan,
|
||||
$laporanSlik->kode_agunan,
|
||||
$laporanSlik->peringkat_agunan,
|
||||
$laporanSlik->fasilitas,
|
||||
$laporanSlik->status_agunan,
|
||||
$laporanSlik->tanggal_lapor,
|
||||
$laporanSlik->status,
|
||||
$laporanSlik->created_at->format('d/m/Y H:i'),
|
||||
];
|
||||
}
|
||||
|
||||
public function styles(Worksheet $sheet)
|
||||
{
|
||||
return [
|
||||
1 => ['font' => ['bold' => true]],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
$row->id,
|
||||
$row->code,
|
||||
$row->name,
|
||||
$row->biaya,
|
||||
$row->created_at
|
||||
];
|
||||
}
|
||||
@@ -35,6 +36,7 @@
|
||||
'ID',
|
||||
'Code',
|
||||
'Name',
|
||||
'Biaya',
|
||||
'Created At'
|
||||
];
|
||||
}
|
||||
@@ -44,7 +46,8 @@
|
||||
{
|
||||
return [
|
||||
'A' => NumberFormat::FORMAT_NUMBER,
|
||||
'D' => NumberFormat::FORMAT_DATE_DATETIME
|
||||
'D' => NumberFormat::FORMAT_NUMBER_00,
|
||||
'E' => NumberFormat::FORMAT_DATE_DATETIME
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
128
app/Exports/ReferensiLinkExport.php
Normal file
128
app/Exports/ReferensiLinkExport.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Exports;
|
||||
|
||||
use Modules\Lpj\app\Models\ReferensiLink;
|
||||
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Maatwebsite\Excel\Concerns\WithStyles;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||
|
||||
class ReferensiLinkExport implements FromQuery, WithHeadings, WithMapping, WithStyles, ShouldAutoSize, WithColumnFormatting
|
||||
{
|
||||
protected $filters;
|
||||
|
||||
public function __construct($filters = [])
|
||||
{
|
||||
$this->filters = $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query data yang akan diexport
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
$query = ReferensiLink::with(['createdBy', 'updatedBy'])
|
||||
->select('referensi_link.*');
|
||||
|
||||
// Apply filters
|
||||
if (isset($this->filters['kategori']) && !empty($this->filters['kategori'])) {
|
||||
$query->where('kategori', $this->filters['kategori']);
|
||||
}
|
||||
|
||||
if (isset($this->filters['status']) && $this->filters['status'] !== '') {
|
||||
$query->where('is_active', $this->filters['status']);
|
||||
}
|
||||
|
||||
if (isset($this->filters['search']) && !empty($this->filters['search'])) {
|
||||
$query->search($this->filters['search']);
|
||||
}
|
||||
|
||||
return $query->ordered();
|
||||
}
|
||||
|
||||
/**
|
||||
* Header kolom
|
||||
*/
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'No',
|
||||
'Nama',
|
||||
'Link',
|
||||
'Kategori',
|
||||
'Deskripsi',
|
||||
'Status Aktif',
|
||||
'Urutan',
|
||||
'Dibuat Oleh',
|
||||
'Diupdate Oleh',
|
||||
'Tanggal Dibuat',
|
||||
'Tanggal Diupdate',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping data untuk setiap baris
|
||||
*/
|
||||
public function map($referensiLink): array
|
||||
{
|
||||
static $rowNumber = 0;
|
||||
$rowNumber++;
|
||||
|
||||
return [
|
||||
$rowNumber,
|
||||
$referensiLink->name,
|
||||
$referensiLink->link,
|
||||
$referensiLink->kategori ?? '-',
|
||||
$referensiLink->deskripsi ?? '-',
|
||||
$referensiLink->is_active ? 'Aktif' : 'Tidak Aktif',
|
||||
$referensiLink->urutan,
|
||||
$referensiLink->createdBy ? $referensiLink->createdBy->name : '-',
|
||||
$referensiLink->updatedBy ? $referensiLink->updatedBy->name : '-',
|
||||
$referensiLink->created_at->format('d-m-Y H:i:s'),
|
||||
$referensiLink->updated_at->format('d-m-Y H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Styling untuk worksheet
|
||||
*/
|
||||
public function styles(Worksheet $sheet)
|
||||
{
|
||||
return [
|
||||
// Header styling
|
||||
1 => [
|
||||
'font' => ['bold' => true, 'size' => 12],
|
||||
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E2EFDA']],
|
||||
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER],
|
||||
],
|
||||
// Alternating row colors
|
||||
'A2:K1000' => [
|
||||
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F8F9FA']],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format kolom
|
||||
*/
|
||||
public function columnFormats(): array
|
||||
{
|
||||
return [
|
||||
'F' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal dibuat
|
||||
'G' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal diupdate
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom title untuk sheet
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
return 'Referensi Link';
|
||||
}
|
||||
}
|
||||
185
app/Exports/SlikExport.php
Normal file
185
app/Exports/SlikExport.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Exports;
|
||||
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Modules\Lpj\Models\Slik;
|
||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||
|
||||
/**
|
||||
* Export class untuk data SLIK (Sistem Layanan Informasi Keuangan)
|
||||
*
|
||||
* Class ini menangani export data SLIK ke format Excel dengan:
|
||||
* - Mapping data sesuai struktur SLIK
|
||||
* - Format kolom yang sesuai (text, number, date)
|
||||
* - Header yang informatif
|
||||
*/
|
||||
class SlikExport implements WithColumnFormatting, WithHeadings, FromCollection, WithMapping
|
||||
{
|
||||
/**
|
||||
* Mengambil collection data SLIK untuk di-export
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function collection()
|
||||
{
|
||||
return Slik::orderBy('created_at', 'desc')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping data SLIK untuk setiap baris dalam Excel
|
||||
*
|
||||
* @param \Modules\Lpj\Models\Slik $row
|
||||
* @return array
|
||||
*/
|
||||
public function map($row): array
|
||||
{
|
||||
return [
|
||||
$row->id, // A - ID
|
||||
$row->sandi_bank, // B - Sandi Bank
|
||||
$row->tahun, // C - Tahun
|
||||
$row->bulan, // D - Bulan
|
||||
$row->flag_detail, // E - Flag Detail
|
||||
$row->kode_register_agunan, // F - Kode Register Agunan
|
||||
$row->no_rekening, // G - No Rekening
|
||||
$row->cif, // H - CIF
|
||||
$row->kolektibilitas, // I - Kolektibilitas
|
||||
$row->fasilitas, // J - Fasilitas
|
||||
$row->jenis_segmen_fasilitas, // K - Jenis Segmen Fasilitas
|
||||
$row->status_agunan, // L - Status Agunan
|
||||
$row->jenis_agunan, // M - Jenis Agunan
|
||||
$row->peringkat_agunan, // N - Peringkat Agunan
|
||||
$row->lembaga_pemeringkat, // O - Lembaga Pemeringkat
|
||||
$row->jenis_pengikatan, // P - Jenis Pengikatan
|
||||
$row->tanggal_pengikatan, // Q - Tanggal Pengikatan
|
||||
$row->nama_pemilik_agunan, // R - Nama Pemilik Agunan
|
||||
$row->bukti_kepemilikan, // S - Bukti Kepemilikan
|
||||
$row->alamat_agunan, // T - Alamat Agunan
|
||||
$row->lokasi_agunan, // U - Lokasi Agunan
|
||||
$row->nilai_agunan, // V - Nilai Agunan
|
||||
$row->nilai_agunan_menurut_ljk, // W - Nilai Agunan Menurut LJK
|
||||
$row->tanggal_penilaian_ljk, // X - Tanggal Penilaian LJK
|
||||
$row->nilai_agunan_penilai_independen, // Y - Nilai Agunan Penilai Independen
|
||||
$row->nama_penilai_independen, // Z - Nama Penilai Independen
|
||||
$row->tanggal_penilaian_penilai_independen, // AA - Tanggal Penilaian Penilai Independen
|
||||
$row->jumlah_hari_tunggakan, // AB - Jumlah Hari Tunggakan
|
||||
$row->status_paripasu, // AC - Status Paripasu
|
||||
$row->prosentase_paripasu, // AD - Prosentase Paripasu
|
||||
$row->status_kredit_join, // AE - Status Kredit Join
|
||||
$row->diasuransikan, // AF - Diasuransikan
|
||||
$row->keterangan, // AG - Keterangan
|
||||
$row->kantor_cabang, // AH - Kantor Cabang
|
||||
$row->operasi_data, // AI - Operasi Data
|
||||
$row->kode_cabang, // AJ - Kode Cabang
|
||||
$row->nama_debitur, // AK - Nama Debitur
|
||||
$row->nama_cabang, // AL - Nama Cabang
|
||||
$row->flag, // AM - Flag
|
||||
$row->created_at, // AN - Created At
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Header kolom untuk Excel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'ID',
|
||||
'Sandi Bank',
|
||||
'Tahun',
|
||||
'Bulan',
|
||||
'Flag Detail',
|
||||
'Kode Register Agunan',
|
||||
'No Rekening',
|
||||
'CIF',
|
||||
'Kolektibilitas',
|
||||
'Fasilitas',
|
||||
'Jenis Segmen Fasilitas',
|
||||
'Status Agunan',
|
||||
'Jenis Agunan',
|
||||
'Peringkat Agunan',
|
||||
'Lembaga Pemeringkat',
|
||||
'Jenis Pengikatan',
|
||||
'Tanggal Pengikatan',
|
||||
'Nama Pemilik Agunan',
|
||||
'Bukti Kepemilikan',
|
||||
'Alamat Agunan',
|
||||
'Lokasi Agunan',
|
||||
'Nilai Agunan',
|
||||
'Nilai Agunan Menurut LJK',
|
||||
'Tanggal Penilaian LJK',
|
||||
'Nilai Agunan Penilai Independen',
|
||||
'Nama Penilai Independen',
|
||||
'Tanggal Penilaian Penilai Independen',
|
||||
'Jumlah Hari Tunggakan',
|
||||
'Status Paripasu',
|
||||
'Prosentase Paripasu',
|
||||
'Status Kredit Join',
|
||||
'Diasuransikan',
|
||||
'Keterangan',
|
||||
'Kantor Cabang',
|
||||
'Operasi Data',
|
||||
'Kode Cabang',
|
||||
'Nama Debitur',
|
||||
'Nama Cabang',
|
||||
'Flag',
|
||||
'Created At',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format kolom untuk Excel
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function columnFormats(): array
|
||||
{
|
||||
return [
|
||||
'A' => NumberFormat::FORMAT_NUMBER, // ID
|
||||
'B' => NumberFormat::FORMAT_TEXT, // Sandi Bank
|
||||
'C' => NumberFormat::FORMAT_TEXT, // Tahun
|
||||
'D' => NumberFormat::FORMAT_TEXT, // Bulan
|
||||
'E' => NumberFormat::FORMAT_TEXT, // Flag Detail
|
||||
'F' => NumberFormat::FORMAT_TEXT, // Kode Register Agunan
|
||||
'G' => NumberFormat::FORMAT_TEXT, // No Rekening
|
||||
'H' => NumberFormat::FORMAT_TEXT, // CIF
|
||||
'I' => NumberFormat::FORMAT_TEXT, // Kolektibilitas
|
||||
'J' => NumberFormat::FORMAT_TEXT, // Fasilitas
|
||||
'K' => NumberFormat::FORMAT_TEXT, // Jenis Segmen Fasilitas
|
||||
'L' => NumberFormat::FORMAT_TEXT, // Status Agunan
|
||||
'M' => NumberFormat::FORMAT_TEXT, // Jenis Agunan
|
||||
'N' => NumberFormat::FORMAT_TEXT, // Peringkat Agunan
|
||||
'O' => NumberFormat::FORMAT_TEXT, // Lembaga Pemeringkat
|
||||
'P' => NumberFormat::FORMAT_TEXT, // Jenis Pengikatan
|
||||
'Q' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal Pengikatan
|
||||
'R' => NumberFormat::FORMAT_TEXT, // Nama Pemilik Agunan
|
||||
'S' => NumberFormat::FORMAT_TEXT, // Bukti Kepemilikan
|
||||
'T' => NumberFormat::FORMAT_TEXT, // Alamat Agunan
|
||||
'U' => NumberFormat::FORMAT_TEXT, // Lokasi Agunan
|
||||
'V' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // Nilai Agunan
|
||||
'W' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // Nilai Agunan Menurut LJK
|
||||
'X' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal Penilaian LJK
|
||||
'Y' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // Nilai Agunan Penilai Independen
|
||||
'Z' => NumberFormat::FORMAT_TEXT, // Nama Penilai Independen
|
||||
'AA' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal Penilaian Penilai Independen
|
||||
'AB' => NumberFormat::FORMAT_NUMBER, // Jumlah Hari Tunggakan
|
||||
'AC' => NumberFormat::FORMAT_TEXT, // Status Paripasu
|
||||
'AD' => NumberFormat::FORMAT_PERCENTAGE_00, // Prosentase Paripasu
|
||||
'AE' => NumberFormat::FORMAT_TEXT, // Status Kredit Join
|
||||
'AF' => NumberFormat::FORMAT_TEXT, // Diasuransikan
|
||||
'AG' => NumberFormat::FORMAT_TEXT, // Keterangan
|
||||
'AH' => NumberFormat::FORMAT_TEXT, // Kantor Cabang
|
||||
'AI' => NumberFormat::FORMAT_TEXT, // Operasi Data
|
||||
'AJ' => NumberFormat::FORMAT_TEXT, // Kode Cabang
|
||||
'AK' => NumberFormat::FORMAT_TEXT, // Nama Debitur
|
||||
'AL' => NumberFormat::FORMAT_TEXT, // Nama Cabang
|
||||
'AM' => NumberFormat::FORMAT_TEXT, // Flag
|
||||
'AN' => NumberFormat::FORMAT_DATE_DATETIME, // Created At
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Location\Models\City;
|
||||
use Modules\Location\Models\District;
|
||||
use Modules\Location\Models\Province;
|
||||
@@ -15,29 +18,118 @@
|
||||
use Modules\Lpj\Models\Penilaian;
|
||||
use Modules\Lpj\Models\TeamsUsers;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Lpj\Services\ImageResizeService;
|
||||
|
||||
function formatTanggalIndonesia($date, $time = false)
|
||||
/**
|
||||
* Format tanggal ke dalam format Bahasa Indonesia
|
||||
*
|
||||
* Mengubah tanggal menjadi format yang lebih mudah dibaca dalam Bahasa Indonesia.
|
||||
* Contoh: "15 Januari 2024" atau "15 Januari 2024 pukul 14.30 WIB"
|
||||
*
|
||||
* @param string|mixed $date Tanggal yang akan diformat (string tanggal atau null)
|
||||
* @param bool $time Apakah akan menampilkan waktu juga (default: false)
|
||||
* @return string Tanggal yang sudah diformat dalam Bahasa Indonesia
|
||||
*
|
||||
* @example
|
||||
* formatTanggalIndonesia('2024-01-15') // "15 Januari 2024"
|
||||
* formatTanggalIndonesia('2024-01-15 14:30:00', true) // "15 Januari 2024 pukul 14.30 WIB"
|
||||
* formatTanggalIndonesia(null) // ""
|
||||
* formatTanggalIndonesia('invalid-date') // "invalid-date" (return as-is jika error)
|
||||
*/
|
||||
function formatTanggalIndonesia($date, $time = false): string
|
||||
{
|
||||
Carbon::setLocale('id');
|
||||
try {
|
||||
$waktu = Carbon::parse($date);
|
||||
if (!$time) {
|
||||
return $waktu->translatedFormat('d F Y');
|
||||
}
|
||||
return $waktu->translatedFormat('d F Y') . ' pukul ' . $waktu->format('H.i') . ' WIB';
|
||||
} catch (Throwable $e) {
|
||||
return $date;
|
||||
Log::debug('Memulai format tanggal Indonesia', [
|
||||
'date' => $date,
|
||||
'time' => $time
|
||||
]);
|
||||
|
||||
// Validasi input null atau kosong
|
||||
if (empty($date)) {
|
||||
Log::debug('Tanggal kosong, return empty string');
|
||||
return '';
|
||||
}
|
||||
|
||||
Carbon::setLocale('id');
|
||||
|
||||
try {
|
||||
$waktu = Carbon::parse($date);
|
||||
|
||||
if (!$time) {
|
||||
$result = $waktu->translatedFormat('d F Y');
|
||||
Log::debug('Format tanggal berhasil', ['result' => $result]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $waktu->translatedFormat('d F Y') . ' pukul ' . $waktu->format('H.i') . ' WIB';
|
||||
Log::debug('Format tanggal dengan waktu berhasil', ['result' => $result]);
|
||||
return $result;
|
||||
|
||||
} catch (Throwable $e) {
|
||||
Log::warning('Gagal parse tanggal', [
|
||||
'date' => $date,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
// Return input as-is jika gagal parse
|
||||
return (string) $date;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function formatRupiah($number, $decimals = 0)
|
||||
/**
|
||||
* Format angka ke dalam format mata uang Rupiah Indonesia
|
||||
*
|
||||
* Mengubah angka menjadi format mata uang Rupiah dengan pemisah ribuan
|
||||
* dan menggunakan koma sebagai pemisah desimal sesuai standar Indonesia.
|
||||
*
|
||||
* @param int|float|string $number Angka yang akan diformat (bisa negatif)
|
||||
* @param int $decimals Jumlah digit desimal (default: 0)
|
||||
* @return string Angka yang sudah diformat dalam format Rupiah
|
||||
*
|
||||
* @example
|
||||
* formatRupiah(1500000) // "Rp 1.500.000"
|
||||
* formatRupiah(1500000.50, 2) // "Rp 1.500.000,50"
|
||||
* formatRupiah(-500000) // "Rp -500.000"
|
||||
* formatRupiah(0) // "Rp 0"
|
||||
* formatRupiah(null) // "Rp 0"
|
||||
*/
|
||||
function formatRupiah($number, $decimals = 0, $withSymbol = true): string
|
||||
{
|
||||
$number = (float) $number;
|
||||
return 'Rp ' . number_format($number, $decimals, ',', '.');
|
||||
}
|
||||
Log::debug('Memulai format Rupiah', [
|
||||
'number' => $number,
|
||||
'decimals' => $decimals,
|
||||
'withSymbol' => $withSymbol
|
||||
]);
|
||||
|
||||
// Handle null atau kosong
|
||||
if ($number === null || $number === '') {
|
||||
Log::debug('Number null atau kosong, return Rp 0');
|
||||
return $withSymbol ? 'Rp 0' : '0';
|
||||
}
|
||||
|
||||
// Remove dots if present
|
||||
$number = str_replace('.', '', (string) $number);
|
||||
|
||||
// Konversi ke float dan handle error
|
||||
try {
|
||||
$number = (float) $number;
|
||||
} catch (Throwable $e) {
|
||||
Log::warning('Gagal konversi number ke float', [
|
||||
'number' => $number,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return $withSymbol ? 'Rp 0' : '0';
|
||||
}
|
||||
|
||||
// Validasi decimals
|
||||
$decimals = max(0, (int) $decimals);
|
||||
|
||||
$formatted = number_format($number, $decimals, ',', '.');
|
||||
$result = $withSymbol ? 'Rp ' . $formatted : $formatted;
|
||||
Log::debug('Format Rupiah berhasil', ['result' => $result]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function formatAlamat($alamat)
|
||||
{
|
||||
@@ -96,8 +188,6 @@
|
||||
$query->orWhereNull('dokumen_persetujuan');
|
||||
},
|
||||
)->get();
|
||||
// $sql = DB::getQueryLog();
|
||||
|
||||
|
||||
if (sizeof($query) > 0) {
|
||||
$allow = false;
|
||||
@@ -238,9 +328,6 @@
|
||||
return $hasil;
|
||||
}
|
||||
|
||||
// andy add
|
||||
|
||||
|
||||
function hitungHariKerja($tanggalMulai, $tanggalSelesai)
|
||||
{
|
||||
$tanggalMulai = Carbon::parse($tanggalMulai)->startOfDay();
|
||||
@@ -326,7 +413,8 @@
|
||||
$day = str_pad(date('d'), 2, '0', STR_PAD_LEFT);
|
||||
|
||||
// Generate random numbers
|
||||
$randomNumber = str_pad(mt_rand(0, pow(10, $randomLength) - 1), $randomLength, '0', STR_PAD_LEFT);
|
||||
//$randomNumber = str_pad(mt_rand(0, pow(10, $randomLength) - 1), $randomLength, '0', STR_PAD_LEFT);
|
||||
$randomNumber = sprintf('%0' . $randomLength . 'd', mt_rand(0, pow(10, $randomLength) - 1));
|
||||
|
||||
// Concatenate components to create the custom code
|
||||
return $year . $month . $day . $randomNumber;
|
||||
@@ -530,15 +618,178 @@
|
||||
}
|
||||
}
|
||||
|
||||
function formatNotifikasi($notifikasi)
|
||||
{
|
||||
$data = json_decode(json_encode($notifikasi->data));
|
||||
$message = $data->message;
|
||||
$data = $data->data;
|
||||
$notifikasi = [
|
||||
'title' => 'Permohonan : ' . $data->nomor_registrasi,
|
||||
'message' => $message,
|
||||
];
|
||||
return $notifikasi;
|
||||
function parsePembandingMigration($keterangan) {
|
||||
$keterangan = preg_replace('/[-]{5,}/', '',$keterangan); // Hapus ------
|
||||
$keterangan = preg_replace('/[.]{5,}/', '',$keterangan); // Hapus .....
|
||||
|
||||
$keterangan = preg_replace('/\s+/', ' ',$keterangan);
|
||||
$keterangan = preg_replace('/\s*\n\s*/', "\n",$keterangan);
|
||||
|
||||
// Pecah teks per baris untuk diproses
|
||||
$lines = explode("\n",$keterangan);
|
||||
$cleaned = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (!empty($line)) {
|
||||
// Format angka dalam format Rp. 123.456.789
|
||||
$line = preg_replace_callback('/Rp\.\s*([\d.,]+)/', function($matches) {
|
||||
$angka = str_replace(['.', ','], '', $matches[1]);
|
||||
return 'Rp. ' . number_format((int)$angka, 0, ',', '.');
|
||||
}, $line);
|
||||
|
||||
// Jika ada tanda pagar (#), pisahkan menjadi baris baru
|
||||
$line = str_replace('#', "\n#", $line);
|
||||
|
||||
$cleaned[] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $cleaned);
|
||||
}
|
||||
|
||||
/**
|
||||
* get full path to internal storage file or external storage file
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function getFilePath($path)
|
||||
{
|
||||
// define base path external storage (use .env) example: 'F:\path\to\storage' in windows
|
||||
$externalBase = env('EXTERNAL_STORAGE_BASE_PATH', 'F:LPJ/lpj/LPJ Gambar/001/');
|
||||
|
||||
$segments = explode('/', $path);
|
||||
|
||||
if(strtoupper($segments[0]) === 'SURVEYOR'){
|
||||
$year = $segments[1];
|
||||
$month = ucfirst(strtolower($segments[2]));
|
||||
$date = $segments[3];
|
||||
$code = $segments[4];
|
||||
$file = $segments[5] ?? '';
|
||||
|
||||
$extenalFullpath = $externalBase . $year . '/' . $month . '/' . $date . '/' . $code . '/' . $file;
|
||||
|
||||
if(File::exists($extenalFullpath)){
|
||||
return $extenalFullpath;
|
||||
}
|
||||
}
|
||||
|
||||
// if not found in external storage, try to find in internal storage
|
||||
if (Storage::exists($path)) {
|
||||
return Storage::url('app/' . $path);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
|
||||
function parseTimestamp(?string $timestamp): ?string
|
||||
{
|
||||
if (!$timestamp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trim whitespace dan normalize
|
||||
$timestamp = trim($timestamp);
|
||||
|
||||
// Log untuk debugging
|
||||
Log::info('Mencoba parsing timestamp: "' . $timestamp . '"');
|
||||
|
||||
// Parsing dengan DateTime native PHP untuk lebih robust
|
||||
try {
|
||||
// Pattern untuk format d/m/Y H:i:s
|
||||
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/', $timestamp, $matches)) {
|
||||
$day = (int) $matches[1];
|
||||
$month = (int) $matches[2];
|
||||
$year = (int) $matches[3];
|
||||
$hour = (int) $matches[4];
|
||||
$minute = (int) $matches[5];
|
||||
$second = (int) $matches[6];
|
||||
|
||||
// Validasi nilai
|
||||
if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100 &&
|
||||
$hour >= 0 && $hour <= 23 && $minute >= 0 && $minute <= 59 && $second >= 0 && $second <= 59) {
|
||||
|
||||
// Buat DateTime object langsung
|
||||
$dateTime = new \DateTime();
|
||||
$dateTime->setDate($year, $month, $day);
|
||||
$dateTime->setTime($hour, $minute, $second);
|
||||
|
||||
$result = $dateTime->format('Y-m-d H:i:s');
|
||||
Log::info('Berhasil parsing dengan DateTime: ' . $timestamp . ' -> ' . $result);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// Pattern untuk format d/m/Y tanpa waktu
|
||||
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $timestamp, $matches)) {
|
||||
$day = (int) $matches[1];
|
||||
$month = (int) $matches[2];
|
||||
$year = (int) $matches[3];
|
||||
|
||||
// Validasi nilai
|
||||
if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100) {
|
||||
|
||||
// Buat DateTime object langsung
|
||||
$dateTime = new \DateTime();
|
||||
$dateTime->setDate($year, $month, $day);
|
||||
$dateTime->setTime(0, 0, 0);
|
||||
|
||||
$result = $dateTime->format('Y-m-d H:i:s');
|
||||
Log::info('Berhasil parsing tanpa waktu dengan DateTime: ' . $timestamp . ' -> ' . $result);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Gagal parsing dengan DateTime: ' . $timestamp . '. Error: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// Fallback ke format Carbon standar untuk format lainnya
|
||||
$formats = [
|
||||
'Y-m-d H:i:s',
|
||||
'Y-m-d',
|
||||
'd-m-Y H:i:s',
|
||||
'd-m-Y',
|
||||
'j-n-Y H:i:s',
|
||||
'j-n-Y',
|
||||
];
|
||||
|
||||
foreach ($formats as $format) {
|
||||
try {
|
||||
$carbon = \Carbon\Carbon::createFromFormat($format, $timestamp);
|
||||
|
||||
if ($carbon && $carbon->format($format) === $timestamp) {
|
||||
// Jika format tidak mengandung waktu, set ke awal hari
|
||||
if (!str_contains($format, 'H:i:s')) {
|
||||
$carbon = $carbon->startOfDay();
|
||||
}
|
||||
Log::info('Berhasil parsing dengan format ' . $format . ': ' . $timestamp . ' -> ' . $carbon->toDateTimeString());
|
||||
return $carbon->toDateTimeString();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Lanjut ke format berikutnya
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Log::error('Tidak dapat memparsing timestamp dengan format apapun: "' . $timestamp . '"');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!function_exists('resize_image')) {
|
||||
/**
|
||||
* Merubah ukuran gambar secara on-the-fly dan mengembalikan path-nya.
|
||||
*
|
||||
* @param string $path Path asli gambar.
|
||||
* @param int|null $width Lebar yang diinginkan.
|
||||
* @param int|null $height Tinggi yang diinginkan (opsional, akan menjaga rasio aspek jika null).
|
||||
* @param int $quality Kualitas gambar (1-100).
|
||||
* @return string Path gambar yang sudah di-resize.
|
||||
*/
|
||||
function resize_image(string $path, ?int $width, ?int $height = null, int $quality = 80): string
|
||||
{
|
||||
|
||||
return app(ImageResizeService::class)->resize($path, $width, $height, $quality);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class ActivityController extends Controller
|
||||
->when($teamId, fn ($q) => $q->where('id', $teamId));
|
||||
})
|
||||
->where('user_id', '!=', $user->id)
|
||||
->whereHas('user.roles', fn ($q) => $q->whereIn('name', ['surveyor', 'surveyor-penilai']))
|
||||
->whereHas('user.roles', fn ($q) => $q->whereIn('name', ['surveyor', 'surveyor-penilai','penilai']))
|
||||
->get();
|
||||
|
||||
$teamId = is_array($teamId) ? $teamId : [$teamId];
|
||||
@@ -306,6 +306,7 @@ class ActivityController extends Controller
|
||||
$query = Permohonan::query();
|
||||
|
||||
// Apply search filter if provided
|
||||
$query = $query->orderBy('nomor_registrasi', 'desc');
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
@@ -324,7 +325,9 @@ class ActivityController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
// Default sorting if no sort provided
|
||||
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
|
||||
183
app/Http/Controllers/Api/DebiturController.php
Normal file
183
app/Http/Controllers/Api/DebiturController.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Exception;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
|
||||
/**
|
||||
* Controller untuk API pencarian debitur
|
||||
* Digunakan untuk autocomplete search pada form pembayaran
|
||||
*/
|
||||
class DebiturController extends Controller
|
||||
{
|
||||
/**
|
||||
* Pencarian debitur untuk autocomplete
|
||||
*
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function search(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
// Log aktivitas pencarian
|
||||
Log::info('API Debitur Search - Request', [
|
||||
'query' => $request->get('q'),
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
$query = $request->get('q', '');
|
||||
|
||||
// Validasi minimal 2 karakter untuk pencarian
|
||||
if (strlen($query) < 2) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Minimal 2 karakter untuk pencarian',
|
||||
'data' => []
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Mulai database transaction
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Query pencarian debitur
|
||||
// Asumsi tabel debitur dengan kolom: id, code, nama, alamat
|
||||
$debiturs = Debiture::query()
|
||||
->select('id', 'cif', 'name', 'address')
|
||||
->whereAny(['cif','name'], 'LIKE', "%{$query}%")
|
||||
->orderBy('name', 'asc')
|
||||
->limit(20) // Batasi hasil maksimal 20
|
||||
->get();
|
||||
|
||||
// Format data untuk TomSelect
|
||||
$formattedData = $debiturs->map(function($debitur) {
|
||||
return [
|
||||
'id' => $debitur->id,
|
||||
'kode_debitur' => $debitur->cif,
|
||||
'name' => $debitur->name,
|
||||
'address' => $debitur->address
|
||||
];
|
||||
});
|
||||
|
||||
DB::commit();
|
||||
|
||||
// Log hasil pencarian
|
||||
Log::info('API Debitur Search - Success', [
|
||||
'query' => $query,
|
||||
'results_count' => $formattedData->count(),
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Data debitur berhasil ditemukan',
|
||||
'data' => $formattedData
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
DB::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Log error
|
||||
Log::error('API Debitur Search - Error', [
|
||||
'query' => $request->get('q'),
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan saat mencari data debitur',
|
||||
'error' => config('app.debug') ? $e->getMessage() : 'Internal Server Error'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detail debitur berdasarkan code
|
||||
*
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getByCode(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$code = $request->get('code');
|
||||
|
||||
if (empty($code)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Code debitur harus diisi',
|
||||
'data' => null
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Log aktivitas get detail
|
||||
Log::info('API Debitur GetByCode - Request', [
|
||||
'code' => $code,
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$debitur = DB::table('debitur')
|
||||
->select('id', 'code', 'nama', 'alamat', 'telepon', 'email')
|
||||
->where('code', $code)
|
||||
->where('status', 'aktif')
|
||||
->first();
|
||||
|
||||
if (!$debitur) {
|
||||
DB::rollback();
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Debitur tidak ditemukan',
|
||||
'data' => null
|
||||
], 404);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
Log::info('API Debitur GetByCode - Success', [
|
||||
'code' => $code,
|
||||
'debitur_id' => $debitur->id,
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Data debitur berhasil ditemukan',
|
||||
'data' => $debitur
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
DB::rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
Log::error('API Debitur GetByCode - Error', [
|
||||
'code' => $request->get('code'),
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan saat mengambil data debitur',
|
||||
'error' => config('app.debug') ? $e->getMessage() : 'Internal Server Error'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Log;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Location\Models\Province;
|
||||
use Modules\Lpj\Http\Requests\BankDataRequest;
|
||||
use Modules\Lpj\Models\BankData;
|
||||
@@ -88,7 +88,8 @@
|
||||
}
|
||||
} else {
|
||||
// Invalid coordinates
|
||||
Log::warning("Invalid coordinates: Lat: $_lat, Lng: $_lng");// Do something to handle this situation, such as logging an error or skipping the record
|
||||
Log::warning("Invalid coordinates: Lat: $_lat, Lng: $_lng");
|
||||
// Do something to handle this situation, such as logging an error or skipping the record
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +139,8 @@
|
||||
}
|
||||
} else {
|
||||
// Invalid coordinates
|
||||
Log::warning("Invalid coordinates: Lat: $lat, Lng: $lng");// Do something to handle this situation, such as logging an error or skipping the record
|
||||
Log::warning("Invalid coordinates: Lat: $lat, Lng: $lng");
|
||||
// Do something to handle this situation, such as logging an error or skipping the record
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,6 +199,24 @@
|
||||
// Retrieve data from the database
|
||||
$query = BankData::query();
|
||||
|
||||
// Check if show_all parameter is set
|
||||
$showAll = $request->has('show_all') && $request->get('show_all') === 'true';
|
||||
|
||||
// If show_all is true, we'll get all data without pagination
|
||||
if ($showAll) {
|
||||
// Get all records without pagination
|
||||
$data = $query->get();
|
||||
|
||||
return response()->json([
|
||||
'data' => $data,
|
||||
'recordsTotal' => $data->count(),
|
||||
'recordsFiltered' => $data->count(),
|
||||
'page' => 1,
|
||||
'pageSize' => $data->count(),
|
||||
'total' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
@@ -252,8 +272,10 @@
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
// Apply pagination only if explicitly requested or not first load
|
||||
$shouldPaginate = $request->has('page') && $request->has('size') && !$request->has('show_all');
|
||||
|
||||
if ($shouldPaginate) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
@@ -287,11 +309,11 @@
|
||||
];
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
// Calculate the page count (1 if showing all data)
|
||||
$pageCount = $shouldPaginate ? ceil($totalRecords / $request->get('size')) : 1;
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = $request->get('page', 1);
|
||||
$currentPage = $shouldPaginate ? $request->get('page', 1) : 1;
|
||||
|
||||
// Ensure current page doesn't exceed page count
|
||||
$currentPage = min($currentPage, $pageCount);
|
||||
|
||||
@@ -7,6 +7,8 @@ use Illuminate\Http\Request;
|
||||
use Modules\Lpj\Models\CategoryDaftarPustaka;
|
||||
use Modules\Lpj\Services\DaftarPustakaService;
|
||||
use Modules\Lpj\Http\Requests\DaftarPustakaRequest;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DaftarPustakaController extends Controller
|
||||
{
|
||||
@@ -22,7 +24,15 @@ class DaftarPustakaController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$categories = CategoryDaftarPustaka::all();
|
||||
// Get categories with count of daftar pustaka
|
||||
try {
|
||||
$categories = CategoryDaftarPustaka::withCount('daftarPustaka')->get();
|
||||
} catch (\Exception $e) {
|
||||
// Handle jika tabel belum ada atau error lainnya
|
||||
Log::warning('Error loading categories with count: ' . $e->getMessage());
|
||||
$categories = CategoryDaftarPustaka::get(); // Fallback tanpa count
|
||||
}
|
||||
|
||||
$daftar_pustaka = $this->daftarPustaka->getAllDaftarPustaka($request);
|
||||
|
||||
return view('lpj::daftar-pustaka.index', [
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
// Retrieve data from the database
|
||||
$query = Debiture::query();
|
||||
|
||||
if (!Auth::user()->hasAnyRole(['administrator'])) {
|
||||
if (!Auth::user()->hasAnyRole(['administrator','admin'])) {
|
||||
$query = $query->where('branch_id', Auth::user()->branch_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +406,9 @@
|
||||
|
||||
|
||||
// Remove values from $legalitasJaminan that are in $currentLegalitasJaminan
|
||||
$legalitasJaminan = array_diff($legalitasJaminan, $currentLegalitasJaminan->pluck('code')->toArray());
|
||||
$legalitasJaminan = is_array($legalitasJaminan)
|
||||
? array_diff($legalitasJaminan, $currentLegalitasJaminan->pluck('code')->toArray())
|
||||
: [];
|
||||
|
||||
$legalitas = JenisLegalitasJaminan::whereIn('code', $legalitasJaminan)->get();
|
||||
}
|
||||
@@ -441,7 +443,7 @@
|
||||
try {
|
||||
// Periksa apakah pengguna adalah admin
|
||||
if (!auth()->user()->hasRole('administrator')) {
|
||||
return response()->json(['success' => false, 'message' => 'Hanya administrator yang dapat menghapus dokumen jaminan'], 403);
|
||||
//return response()->json(['success' => false, 'message' => 'Hanya administrator yang dapat menghapus dokumen jaminan'], 403);
|
||||
}
|
||||
|
||||
$jaminan = DokumenJaminan::find($jaminan_id);
|
||||
@@ -451,8 +453,8 @@
|
||||
}
|
||||
|
||||
// Periksa apakah dokumen jaminan terkait dengan permohonan aktif
|
||||
if ($jaminan->permohonan()->exists()) {
|
||||
return response()->json(['success' => false, 'message' => 'Tidak dapat menghapus dokumen jaminan yang terkait dengan permohonan aktif'], 400);
|
||||
if ($jaminan->permohonan()->exists() && !in_array($jaminan->permohonan->status,['order','revisi'])) {
|
||||
// return response()->json(['success' => false, 'message' => 'Tidak dapat menghapus dokumen jaminan yang terkait dengan permohonan aktif'], 400);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
239
app/Http/Controllers/InspeksiController.php
Normal file
239
app/Http/Controllers/InspeksiController.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
use Modules\Lpj\Services\InspeksiCleanupService;
|
||||
|
||||
/**
|
||||
* Controller contoh untuk menunjukkan penggunaan InspeksiCleanupService
|
||||
*
|
||||
* Controller ini berisi contoh method untuk membuat dan update data inspeksi
|
||||
* dengan otomatis menjalankan cleanup data lama yang tidak memiliki dokument_id
|
||||
*/
|
||||
class InspeksiController extends Controller
|
||||
{
|
||||
protected InspeksiCleanupService $cleanupService;
|
||||
|
||||
public function __construct(InspeksiCleanupService $cleanupService)
|
||||
{
|
||||
$this->cleanupService = $cleanupService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contoh method untuk membuat data inspeksi baru
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'permohonan_id' => 'required|integer|exists:permohonan,id',
|
||||
'dokument_id' => 'nullable|integer|exists:dokumen_jaminan,id',
|
||||
'data_form' => 'required|json',
|
||||
'foto_form' => 'nullable|json',
|
||||
'denah_form' => 'nullable|json',
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => 'required|string|max:50',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Buat data inspeksi baru
|
||||
$inspeksi = Inspeksi::create(array_merge($validated, [
|
||||
'created_by' => Auth::id(),
|
||||
'updated_by' => Auth::id(),
|
||||
]));
|
||||
|
||||
Log::info('InspeksiController: Data inspeksi berhasil dibuat', [
|
||||
'inspeksi_id' => $inspeksi->id,
|
||||
'permohonan_id' => $inspeksi->permohonan_id,
|
||||
'dokument_id' => $inspeksi->dokument_id,
|
||||
'created_by' => $inspeksi->created_by
|
||||
]);
|
||||
|
||||
// Commit transaksi utama
|
||||
DB::commit();
|
||||
|
||||
// Jalankan cleanup secara async jika ada dokument_id
|
||||
// Ini akan menghapus data lama yang tidak memiliki dokument_id
|
||||
if ($inspeksi->dokument_id) {
|
||||
Log::info('InspeksiController: Memulai cleanup data lama', [
|
||||
'permohonan_id' => $inspeksi->permohonan_id,
|
||||
'created_by' => $inspeksi->created_by,
|
||||
'dokument_id' => $inspeksi->dokument_id
|
||||
]);
|
||||
|
||||
// Dispatch job cleanup secara async
|
||||
$this->cleanupService->cleanupAsync(
|
||||
$inspeksi->permohonan_id,
|
||||
$inspeksi->created_by,
|
||||
$inspeksi->dokument_id
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Data inspeksi berhasil dibuat',
|
||||
'data' => $inspeksi->load(['permohonan', 'dokument'])
|
||||
], 201);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
Log::error('InspeksiController: Gagal membuat data inspeksi', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal membuat data inspeksi: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contoh method untuk update data inspeksi
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'dokument_id' => 'nullable|integer|exists:dokumen_jaminan,id',
|
||||
'data_form' => 'required|json',
|
||||
'foto_form' => 'nullable|json',
|
||||
'denah_form' => 'nullable|json',
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => 'required|string|max:50',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$inspeksi = Inspeksi::findOrFail($id);
|
||||
|
||||
// Simpan data lama untuk logging
|
||||
$oldDokumentId = $inspeksi->dokument_id;
|
||||
|
||||
// Update data
|
||||
$inspeksi->update(array_merge($validated, [
|
||||
'updated_by' => Auth::id(),
|
||||
]));
|
||||
|
||||
Log::info('InspeksiController: Data inspeksi berhasil diupdate', [
|
||||
'inspeksi_id' => $inspeksi->id,
|
||||
'permohonan_id' => $inspeksi->permohonan_id,
|
||||
'old_dokument_id' => $oldDokumentId,
|
||||
'new_dokument_id' => $inspeksi->dokument_id,
|
||||
'updated_by' => $inspeksi->updated_by
|
||||
]);
|
||||
|
||||
// Commit transaksi utama
|
||||
DB::commit();
|
||||
|
||||
// Jalankan cleanup jika dokument_id berubah dari null ke ada nilai
|
||||
if (!$oldDokumentId && $inspeksi->dokument_id) {
|
||||
Log::info('InspeksiController: Dokument ID berubah dari null, memulai cleanup', [
|
||||
'inspeksi_id' => $inspeksi->id,
|
||||
'permohonan_id' => $inspeksi->permohonan_id,
|
||||
'created_by' => $inspeksi->created_by,
|
||||
'new_dokument_id' => $inspeksi->dokument_id
|
||||
]);
|
||||
|
||||
// Dispatch job cleanup secara async
|
||||
$this->cleanupService->cleanupAsync(
|
||||
$inspeksi->permohonan_id,
|
||||
$inspeksi->created_by,
|
||||
$inspeksi->dokument_id
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Data inspeksi berhasil diupdate',
|
||||
'data' => $inspeksi->load(['permohonan', 'dokument'])
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
Log::error('InspeksiController: Gagal update data inspeksi', [
|
||||
'inspeksi_id' => $id,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal update data inspeksi: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contoh method untuk menjalankan cleanup secara manual
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function cleanup(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'permohonan_id' => 'required|integer|exists:permohonan,id',
|
||||
'created_by' => 'required|integer|exists:users,id',
|
||||
'sync' => 'boolean', // Opsional, default false (async)
|
||||
]);
|
||||
|
||||
try {
|
||||
$permohonanId = $validated['permohonan_id'];
|
||||
$createdBy = $validated['created_by'];
|
||||
$sync = $validated['sync'] ?? false;
|
||||
|
||||
Log::info('InspeksiController: Menjalankan cleanup manual', [
|
||||
'permohonan_id' => $permohonanId,
|
||||
'created_by' => $createdBy,
|
||||
'sync' => $sync
|
||||
]);
|
||||
|
||||
if ($sync) {
|
||||
// Jalankan secara sync
|
||||
$this->cleanupService->cleanupSync($permohonanId, $createdBy);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Cleanup selesai dijalankan secara synchronous'
|
||||
]);
|
||||
} else {
|
||||
// Dispatch ke queue
|
||||
$this->cleanupService->cleanupAsync($permohonanId, $createdBy);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Cleanup job berhasil di-dispatch ke queue'
|
||||
]);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('InspeksiController: Gagal menjalankan cleanup manual', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal menjalankan cleanup: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
@@ -149,6 +150,8 @@
|
||||
$request->validate([
|
||||
'kode_register_t24' => 'nullable',
|
||||
'cif' => 'required',
|
||||
'keterangan' => 'nullable|string',
|
||||
'kolektibilitas' => 'nullable|string|in:1,2,3,4,5',
|
||||
]);
|
||||
|
||||
try {
|
||||
@@ -157,6 +160,8 @@
|
||||
// Update only the editable fields
|
||||
$laporanAdminKredit->update([
|
||||
'kode_register_t24' => $request->kode_register_t24,
|
||||
'keterangan' => $request->keterangan,
|
||||
'kolektibilitas' => $request->kolektibilitas,
|
||||
'updated_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
@@ -169,11 +174,11 @@
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('laporan-admin-kredit.index')
|
||||
->route('admin-kredit.laporan.index')
|
||||
->with('success', 'Laporan Admin Kredit updated successfully');
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
->route('laporan-admin-kredit.edit', $id)
|
||||
->route('admin-kredit.laporan.edit', $id)
|
||||
->with('error', 'Failed to update Laporan Admin Kredit');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Modules\Lpj\Http\Controllers\PenilaiController;
|
||||
|
||||
class LaporanController extends Controller
|
||||
@@ -15,7 +16,6 @@ class LaporanController extends Controller
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
|
||||
|
||||
public function __construct(PenilaiController $penilaiController){
|
||||
$this->penilaiController = $penilaiController;
|
||||
}
|
||||
@@ -75,11 +75,12 @@ class LaporanController extends Controller
|
||||
}
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query()->whereIn('status',['proses-laporan','done', 'paparan', 'proses-paparan'])->whereNotNull('approval_so_at')->whereNotNull('approval_eo_at')->where(function ($q) {
|
||||
$q->whereIn('nilai_plafond_id', [1,4])
|
||||
->whereNotNull('approval_dd_at')
|
||||
->orWhereIn('nilai_plafond_id', [2,3]);
|
||||
});
|
||||
$query = Permohonan::query()
|
||||
->whereIn('status',['proses-laporan','done', 'paparan', 'proses-paparan']);
|
||||
|
||||
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
|
||||
$query = $query->where('branch_id', Auth::user()->branch_id);
|
||||
}
|
||||
|
||||
$query = $query->orderBy('nomor_registrasi', 'desc');
|
||||
// Apply search filter if provided
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Exports\LaporanHasilPenilaianJaminanInternalExternalExport;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
@@ -60,6 +61,7 @@
|
||||
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search->search) . '%');
|
||||
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
|
||||
@@ -69,7 +71,7 @@
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
$query->orderBy($column, $order);
|
||||
}
|
||||
@@ -104,12 +106,12 @@
|
||||
$lpj = json_decode($permohonan->penilai->lpj, true);
|
||||
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
|
||||
|
||||
$luas_tanah = $lpj['luas_tanah'] ?? 0;
|
||||
$luas_tanah = $lpj['luas_tanah'] ?? 0;
|
||||
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
|
||||
// Calculate nilai_tanah dynamically by looking for all keys that start with 'nilai_tanah_'
|
||||
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
|
||||
|
||||
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
|
||||
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
|
||||
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\StatusPermohonan;
|
||||
use Modules\Lpj\Exports\LaporanPenilaiJaminanExport;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Models\Branch;
|
||||
use Modules\Lpj\Services\PreviewLaporanService;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
use Modules\Lpj\Models\Penilai;
|
||||
|
||||
class LaporanPenilaiJaminanController extends Controller
|
||||
{
|
||||
@@ -16,59 +20,31 @@ class LaporanPenilaiJaminanController extends Controller
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
|
||||
protected $previewLaporanService;
|
||||
|
||||
public function __construct(PreviewLaporanService $previewLaporanService)
|
||||
{
|
||||
$this->previewLaporanService = $previewLaporanService;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$status_permohonan = StatusPermohonan::all();
|
||||
return view('lpj::laporan-penilai-jaminan.index', compact('status_permohonan'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('lpj::create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the specified resource.
|
||||
*/
|
||||
public function show($id)
|
||||
public function show($permohonan_id, $dokumen_id, $jaminan_id)
|
||||
{
|
||||
return view('lpj::laporan-penilai-jaminan.show');
|
||||
$back = route('laporan-penilai-jaminan.index');
|
||||
return $this->previewLaporanService->previewLaporan($permohonan_id, $dokumen_id, $jaminan_id, $back);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
return view('lpj::edit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
@@ -93,91 +69,104 @@ class LaporanPenilaiJaminanController extends Controller
|
||||
// dd($startDate);
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query();
|
||||
|
||||
$query = $query->where('status', 'done')->orderBy('tanggal_permohonan', 'desc');
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$paramsSearch = json_decode($search);
|
||||
$search = json_decode($request->get('search'));
|
||||
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('debiture', 'name', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
|
||||
if (!empty($search->start_date) || !empty($search->end_date)) {
|
||||
$startDate = $search->start_date ?? '1900-01-01';
|
||||
$endDate = $search->end_date ?? now()->toDateString();
|
||||
|
||||
if (!empty($paramsSearch->tanggal_awal) && !empty($paramsSearch->tanggal_akhir)) {
|
||||
$q->whereBetween('tanggal_permohonan', [$paramsSearch->tanggal_awal, $paramsSearch->tanggal_akhir]);
|
||||
$query->where(function ($q) use ($startDate, $endDate) {
|
||||
|
||||
$q->whereHas('penilaian', function ($q2) use ($startDate, $endDate) {
|
||||
$q2->whereBetween('tanggal_kunjungan', [$startDate, $endDate]);
|
||||
});
|
||||
|
||||
// OR check if has penawaran with date in range
|
||||
$q->orWhereHas('penawaran', function ($q3) use ($startDate, $endDate) {
|
||||
$q3->whereBetween('tanggal_penilaian_sebelumnya', [$startDate, $endDate]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (isset($search->branch_id) && !empty($search->branch_id)) {
|
||||
$query->where('branch_id', $search->branch_id);
|
||||
}
|
||||
|
||||
if (isset($search->laporan) && is_array($search->laporan) && !empty($search->laporan)) {
|
||||
foreach ($search->laporan as $type) {
|
||||
$query->whereHas('penilai', function ($q) use ($type) {
|
||||
$q->where('type_penilai', 'LIKE', '%' . $type . '%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$statusKeywords = explode(',', $search);
|
||||
foreach ($statusKeywords as $keyword) {
|
||||
$q->orWhereRelation('penilai', 'type_penilai', 'LIKE', '%' . trim($keyword) . '%');
|
||||
}
|
||||
});
|
||||
// dd($search->search);
|
||||
|
||||
if (isset($search->search)) {
|
||||
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('nomor_registrasi', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search->search) . '%');
|
||||
|
||||
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
$query->where('status', 'done');
|
||||
|
||||
|
||||
|
||||
// Default sorting if no sort provided
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
$query->orderBy($column, $order);
|
||||
} else {
|
||||
$query->orderBy('nomor_registrasi', 'asc');
|
||||
}
|
||||
|
||||
// Get total count of records before pagination
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Pagination
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = (int) $request->get('page', 1);
|
||||
$size = (int) $request->get('size', 10);
|
||||
$offset = ($page - 1) * $size;
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get filtered count
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
|
||||
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Pagination
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = (int) $request->get('page', 1);
|
||||
$size = (int) $request->get('size', 10);
|
||||
$offset = ($page - 1) * $size;
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get filtered count
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get data with necessary relationships
|
||||
$data = $query->with(['user', 'debiture', 'branch', 'tujuanPenilaian', 'penilaian', 'dokumenjaminan.jenisJaminan','nilaiPlafond', 'penilai'])->get();
|
||||
$data = $query->with(['user', 'debiture', 'branch', 'tujuanPenilaian', 'penilaian', 'dokumenjaminan.jenisJaminan','nilaiPlafond', 'penilai', 'dokumenjaminan.inspeksi'])->get();
|
||||
|
||||
// Calculate total pages
|
||||
$pageCount = ceil($totalRecords / $request->get('size', 10));
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $size);
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = max(1, $request->get('page', 1));
|
||||
|
||||
|
||||
|
||||
// Calculate total pages
|
||||
$pageCount = ceil($totalRecords / $request->get('size', 10));
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $request->get('page', 1),
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $data,
|
||||
]);
|
||||
@@ -185,15 +174,53 @@ class LaporanPenilaiJaminanController extends Controller
|
||||
|
||||
public function export(Request $request)
|
||||
{
|
||||
$tanggalAwal = $request->input('tanggal_awal');
|
||||
$tanggalAkhir = $request->input('tanggal_akhir');
|
||||
$status = $request->input('status');
|
||||
$selectedIds = $request->input('selected_ids');
|
||||
$startDate = $request->input('start_date');
|
||||
$endDate = $request->input('end_date');
|
||||
|
||||
// Validate the date format
|
||||
if (isset($startDate) && isset($endDate)) {
|
||||
$startDate = date('Y-m-d', strtotime($startDate));
|
||||
$endDate = date('Y-m-d', strtotime($endDate));
|
||||
|
||||
if ($startDate > $endDate) {
|
||||
return redirect()->back()->with('error', 'Tanggal awal tidak boleh lebih kecil dari tanggal akhir');
|
||||
}
|
||||
}
|
||||
// name the file
|
||||
$filename = $this->createNameLaporan($request);
|
||||
|
||||
$filename = 'laporan_penilai_jaminan_' . date('YmdHis') . '.xlsx';
|
||||
return Excel::download(
|
||||
new LaporanPenilaiJaminanExport($tanggalAwal, $tanggalAkhir, $status, $selectedIds),
|
||||
new LaporanPenilaiJaminanExport($request),
|
||||
$filename
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function createNameLaporan($request)
|
||||
{
|
||||
$startDate = $request->start_date ?? null;
|
||||
$endDate = $request->end_date ?? null;
|
||||
$branchId = $request->branch_id ?? null;
|
||||
$laporan = $request->laporan ?? null;
|
||||
|
||||
// Initialize filename parts
|
||||
$parts = ['Laporan Penilai Jaminan'];
|
||||
if ($startDate && $endDate) {
|
||||
$parts[] = "{$startDate}_{$endDate}";
|
||||
}
|
||||
if ($laporan) {
|
||||
$parts[] = $laporan;
|
||||
}
|
||||
if ($branchId) {
|
||||
$parts[] = $this->getBranchId($branchId);
|
||||
}
|
||||
// Return concatenated filename with extension
|
||||
return implode('_', $parts) . '.xlsx';
|
||||
}
|
||||
|
||||
public function getBranchId($branchId)
|
||||
{
|
||||
$branchesName = Branch::find($branchId)->name ?? null;
|
||||
return $branchesName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,161 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Lpj\Exports\LaporanPenilaianJaminanExport;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Lpj\Exports\LaporanPenilaianJaminanExport;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\Penilaian;
|
||||
use Modules\Lpj\Models\PenawaranTender;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LaporanPenilaianJaminanController extends Controller
|
||||
class LaporanPenilaianJaminanController extends Controller
|
||||
{
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('lpj::laporan_penilaian_jaminan.index');
|
||||
}
|
||||
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
if (is_null($this->user) || !$this->user->can('laporan-admin-kredit.view')) {
|
||||
//abort(403, 'Sorry! You are not allowed to view laporan admin kredit.');
|
||||
}
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query();
|
||||
$query = $query->where('status', 'done');
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = json_decode($request->get('search'));
|
||||
|
||||
if (isset($search->start_date) || isset($search->end_date)) {
|
||||
$query->whereBetween('tanggal_permohonan', [
|
||||
$search->start_date ?? '1900-01-01',
|
||||
$search->end_date ?? now()->toDateString()
|
||||
]);
|
||||
}
|
||||
|
||||
// Filter by branch if provided
|
||||
if (isset($search->branch_id) && !empty($search->branch_id)) {
|
||||
$query->where('branch_id', $search->branch_id);
|
||||
}
|
||||
|
||||
if (isset($search->penilai_id) && !empty($search->penilai_id)) {
|
||||
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function($q) use ($search) {
|
||||
$q->where('user_id', $search->penilai_id);
|
||||
});
|
||||
}
|
||||
|
||||
if (isset($search->search)) {
|
||||
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('nomor_registrasi', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
$query->orderBy($column, $order);
|
||||
}
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get the data for the current page
|
||||
$data = $query->with(['debiture.branch'])->get();
|
||||
|
||||
$data = $data->map(function ($permohonan) {
|
||||
$luas_tanah = 0;
|
||||
$luas_bangunan = 0;
|
||||
$nilai_tanah = 0;
|
||||
$nilai_bangunan = 0;
|
||||
$npw = 0;
|
||||
$nilai_liquidasi = 0;
|
||||
if (isset($permohonan->penilai->lpj)) {
|
||||
$lpj = json_decode($permohonan->penilai->lpj, true);
|
||||
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
|
||||
|
||||
$luas_tanah = $lpj['luas_tanah'] ?? 0;
|
||||
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
|
||||
// Calculate nilai_tanah dynamically by looking for all keys that start with 'nilai_tanah_'
|
||||
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
|
||||
|
||||
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
|
||||
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $permohonan->id,
|
||||
'nomor_registrasi' => $permohonan->nomor_registrasi,
|
||||
'tanggal_permohonan' => $permohonan->tanggal_permohonan,
|
||||
'branch' => $permohonan->debiture?->branch?->name,
|
||||
'name' => $permohonan->debiture?->name,
|
||||
'pemohon' => $permohonan->creator?->name,
|
||||
'tujuan_penilaian' => $permohonan->tujuanPenilaian?->name,
|
||||
'jenis_agunan' => $permohonan->documents?->pluck('jenisJaminan.name')->unique()->implode(', '),
|
||||
'alamat_agunan' => $permohonan->documents?->map(function ($document) {
|
||||
return formatAlamat($document);
|
||||
})->unique()->implode(', '),
|
||||
'luas_tanah' => $luas_tanah . ' m²',
|
||||
'nilai_tanah' => formatRupiah($nilai_tanah,2),
|
||||
'luas_bangunan' => $luas_bangunan . ' m²',
|
||||
'nilai_bangunan' => formatRupiah($nilai_bangunan,2),
|
||||
'tanggal_laporan' => $permohonan->approval_dd_at ?? $permohonan->approval_eo_at ?? '',
|
||||
'tanggal_review' => $permohonan->penilaian?->tanggal_kunjungan ?? '',
|
||||
'nilai_pasar_wajar' => formatRupiah($npw,2),
|
||||
'nilai_likuidasi' => formatRupiah($nilai_liquidasi,2),
|
||||
'nama_penilai' => $permohonan->penilaian?->_user_penilai?->userPenilaiTeam?->name,
|
||||
];
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = $request->get('page', 1);
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function export(Request $request)
|
||||
{
|
||||
return Excel::download(new LaporanPenilaianJaminanExport($request), 'laporan_penilaian_jaminan.xlsx');
|
||||
}
|
||||
return view('lpj::laporan_penilaian_jaminan.index');
|
||||
}
|
||||
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
if (is_null($this->user) || !$this->user->can('laporan-admin-kredit.view')) {
|
||||
//abort(403, 'Sorry! You are not allowed to view laporan admin kredit.');
|
||||
}
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query();
|
||||
$query = $query->where('status', 'done');
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = json_decode($request->get('search'));
|
||||
|
||||
|
||||
if (!empty($search->start_date) || !empty($search->end_date)) {
|
||||
$startDate = $search->start_date ?? '1900-01-01';
|
||||
$endDate = $search->end_date ?? now()->toDateString();
|
||||
|
||||
$query->where(function ($q) use ($startDate, $endDate) {
|
||||
$q->whereHas('penilaian', function ($q2) use ($startDate, $endDate) {
|
||||
$q2->whereBetween('tanggal_kunjungan', [$startDate, $endDate]);
|
||||
})
|
||||
->orWhereHas('penawaran', function ($q3) use ($startDate, $endDate) {
|
||||
$q3->whereBetween('tanggal_penilaian_sebelumnya', [$startDate, $endDate]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Filter by branch if provided
|
||||
if (isset($search->branch_id) && !empty($search->branch_id)) {
|
||||
$query->where('branch_id', $search->branch_id);
|
||||
}
|
||||
|
||||
if (isset($search->penilai_id) && !empty($search->penilai_id)) {
|
||||
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function ($q) use ($search) {
|
||||
$q->where('user_id', $search->penilai_id);
|
||||
});
|
||||
}
|
||||
|
||||
if (isset($search->search)) {
|
||||
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('nomor_registrasi', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search->search) . '%');
|
||||
|
||||
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
|
||||
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
$query->orderBy($column, $order);
|
||||
}
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get the data for the current page
|
||||
$data = $query->with(['debiture.branch'])->get();
|
||||
|
||||
$data = $data->map(function ($permohonan) {
|
||||
$luas_tanah = 0;
|
||||
$luas_bangunan = 0;
|
||||
$nilai_tanah = 0;
|
||||
$nilai_bangunan = 0;
|
||||
$npw = 0;
|
||||
$nilai_liquidasi = 0;
|
||||
if (isset($permohonan->penilai->lpj)) {
|
||||
$lpj = json_decode($permohonan->penilai->lpj, true);
|
||||
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
|
||||
|
||||
$luas_tanah = $lpj['luas_tanah'] ?? 0;
|
||||
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
|
||||
// Calculate nilai_tanah dynamically by looking for all keys that start with 'nilai_tanah_'
|
||||
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
|
||||
|
||||
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
|
||||
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $permohonan->id,
|
||||
'nomor_registrasi' => $permohonan->nomor_registrasi,
|
||||
'tanggal_permohonan' => $permohonan->tanggal_permohonan,
|
||||
'branch' => $permohonan->debiture?->branch?->name,
|
||||
'name' => $permohonan->debiture?->name,
|
||||
'pemohon' => $permohonan->creator?->name,
|
||||
'tujuan_penilaian' => $permohonan->tujuanPenilaian?->name,
|
||||
'jenis_agunan' => $permohonan->documents?->pluck('jenisJaminan.name')->unique()->implode(', '),
|
||||
'alamat_agunan' => $permohonan->documents?->map(function ($document) {
|
||||
return formatAlamat($document);
|
||||
})->unique()->implode(', '),
|
||||
'luas_tanah' => $luas_tanah . ' m²',
|
||||
'nilai_tanah' => formatRupiah($nilai_tanah, 2),
|
||||
'luas_bangunan' => $luas_bangunan . ' m²',
|
||||
'nilai_bangunan' => formatRupiah($nilai_bangunan, 2),
|
||||
'tanggal_laporan' => $permohonan->approval_dd_at ?? $permohonan->approval_eo_at ?? '',
|
||||
'tanggal_review' => $permohonan->penilaian?->tanggal_kunjungan ?? '',
|
||||
'nilai_pasar_wajar' => formatRupiah($npw, 2),
|
||||
'nilai_likuidasi' => formatRupiah($nilai_liquidasi, 2),
|
||||
'nama_penilai' => $permohonan->penilaian?->_user_penilai?->userPenilaiTeam?->name,
|
||||
];
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = $request->get('page', 1);
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function export(Request $request)
|
||||
{
|
||||
$startDate = $request->start_date;
|
||||
$endDate = $request->end_date;
|
||||
// name of the file
|
||||
$fileName = 'laporan_penilaian_jaminan_' . $startDate . '_' . $endDate . '.xlsx';
|
||||
return Excel::download(new LaporanPenilaianJaminanExport($request), $fileName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query();
|
||||
$query->where('status','done');
|
||||
|
||||
if (!Auth::user()->hasAnyRole(['administrator'])) {
|
||||
$query = $query->where('branch_id', Auth::user()->branch_id);
|
||||
|
||||
304
app/Http/Controllers/LaporanSlikController.php
Normal file
304
app/Http/Controllers/LaporanSlikController.php
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Models\LaporanSlik;
|
||||
use Modules\Lpj\Models\Slik;
|
||||
use Modules\Lpj\Exports\LaporanSlikExport;
|
||||
|
||||
class LaporanSlikController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('lpj::laporan-slik.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$request->validate([
|
||||
'slik_id' => 'required|exists:sliks,id'
|
||||
]);
|
||||
|
||||
$slik = Slik::findOrFail($request->slik_id);
|
||||
|
||||
// Cek apakah data sudah ada di laporan_slik
|
||||
$existing = LaporanSlik::where('slik_id', $slik->id)->first();
|
||||
if ($existing) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Data sudah ada di laporan SLIK'
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Copy data dari tabel slik ke laporan_slik
|
||||
$laporanSlik = LaporanSlik::create([
|
||||
'slik_id' => $slik->id,
|
||||
'sandi_bank' => $slik->sandi_bank,
|
||||
'kode_kantor' => $slik->kode_kantor,
|
||||
'kode_cabang' => $slik->kode_cabang,
|
||||
'tahun' => $slik->tahun,
|
||||
'bulan' => $slik->bulan,
|
||||
'no_rekening' => $slik->no_rekening,
|
||||
'cif' => $slik->cif,
|
||||
'kode_jenis' => $slik->kode_jenis,
|
||||
'kode_jenis_ket' => $slik->kode_jenis_ket,
|
||||
'kode_sifat' => $slik->kode_sifat,
|
||||
'kode_sifat_ket' => $slik->kode_sifat_ket,
|
||||
'kode_valuta' => $slik->kode_valuta,
|
||||
'kode_valuta_ket' => $slik->kode_valuta_ket,
|
||||
'baki_debet' => $slik->baki_debet,
|
||||
'kolektibilitas' => $slik->kolektibilitas,
|
||||
'kolektibilitas_ket' => $slik->kolektibilitas_ket,
|
||||
'tanggal_mulai' => $slik->tanggal_mulai,
|
||||
'tanggal_jatuh_tempo' => $slik->tanggal_jatuh_tempo,
|
||||
'tanggal_selesai' => $slik->tanggal_selesai,
|
||||
'tanggal_restrukturisasi' => $slik->tanggal_restrukturisasi,
|
||||
'kode_sebab_macet' => $slik->kode_sebab_macet,
|
||||
'kode_sebab_macet_ket' => $slik->kode_sebab_macet_ket,
|
||||
'tanggal_macet' => $slik->tanggal_macet,
|
||||
'kode_kondisi' => $slik->kode_kondisi,
|
||||
'kode_kondisi_ket' => $slik->kode_kondisi_ket,
|
||||
'tanggal_kondisi' => $slik->tanggal_kondisi,
|
||||
'nilai_agunan' => $slik->nilai_agunan,
|
||||
'nilai_agunan_ket' => $slik->nilai_agunan_ket,
|
||||
'jenis_agunan' => $slik->jenis_agunan,
|
||||
'kode_agunan' => $slik->kode_agunan,
|
||||
'kode_agunan_ket' => $slik->kode_agunan_ket,
|
||||
'peringkat_agunan' => $slik->peringkat_agunan,
|
||||
'peringkat_agunan_ket' => $slik->peringkat_agunan_ket,
|
||||
'nama_debitur' => $slik->nama_debitur,
|
||||
'npwp' => $slik->npwp,
|
||||
'no_ktp' => $slik->no_ktp,
|
||||
'no_telp' => $slik->no_telp,
|
||||
'kode_kab_kota' => $slik->kode_kab_kota,
|
||||
'kode_kab_kota_ket' => $slik->kode_kab_kota_ket,
|
||||
'kode_negara_domisili' => $slik->kode_negara_domisili,
|
||||
'kode_negara_domisili_ket' => $slik->kode_negara_domisili_ket,
|
||||
'kode_pos' => $slik->kode_pos,
|
||||
'alamat' => $slik->alamat,
|
||||
'fasilitas' => $slik->fasilitas,
|
||||
'status_agunan' => $slik->status_agunan,
|
||||
'tanggal_lapor' => $slik->tanggal_lapor,
|
||||
'status' => 'active',
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
// Hapus data dari tabel slik setelah berhasil dipindahkan
|
||||
$slik->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Data berhasil dipindahkan ke laporan SLIK',
|
||||
'data' => $laporanSlik
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error moving SLIK to laporan: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan saat memindahkan data'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data untuk datatables dengan server-side processing
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
try {
|
||||
// Retrieve data from the database
|
||||
$query = LaporanSlik::query();
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('sandi_bank', 'LIKE', "%$search%")
|
||||
->orWhere('no_rekening', 'LIKE', "%$search%")
|
||||
->orWhere('cif', 'LIKE', "%$search%")
|
||||
->orWhere('nama_debitur', 'LIKE', "%$search%")
|
||||
->orWhere('fasilitas', 'LIKE', "%$search%")
|
||||
->orWhere('status_agunan', 'LIKE', "%$search%");
|
||||
});
|
||||
}
|
||||
|
||||
// Apply year filter
|
||||
if ($request->has('year') && !empty($request->get('year'))) {
|
||||
$query->where('tahun', $request->get('year'));
|
||||
}
|
||||
|
||||
// Apply month filter
|
||||
if ($request->has('month') && !empty($request->get('month'))) {
|
||||
$query->where('bulan', $request->get('month'));
|
||||
}
|
||||
|
||||
// Apply sandi bank filter
|
||||
if ($request->has('sandi_bank') && !empty($request->get('sandi_bank'))) {
|
||||
$query->where('sandi_bank', $request->get('sandi_bank'));
|
||||
}
|
||||
|
||||
// Apply kolektibilitas filter
|
||||
if ($request->has('kolektibilitas') && !empty($request->get('kolektibilitas'))) {
|
||||
$query->where('kolektibilitas', $request->get('kolektibilitas'));
|
||||
}
|
||||
|
||||
// Apply status filter
|
||||
if ($request->has('status') && !empty($request->get('status'))) {
|
||||
$query->where('status', $request->get('status'));
|
||||
}
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField', 'created_at');
|
||||
$query->orderBy($column, $order);
|
||||
} else {
|
||||
$query->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get the data for the current page
|
||||
$data = $query->get();
|
||||
|
||||
// Transform data untuk datatables
|
||||
$transformedData = $data->map(function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'sandi_bank' => $item->sandi_bank,
|
||||
'tahun' => $item->tahun,
|
||||
'bulan' => $item->bulan,
|
||||
'no_rekening' => $item->no_rekening,
|
||||
'cif' => $item->cif,
|
||||
'nama_debitur' => $item->nama_debitur,
|
||||
'kolektibilitas' => $item->kolektibilitas,
|
||||
'kolektibilitas_badge' => $item->kolektibilitas_badge ?? '',
|
||||
'fasilitas' => $item->fasilitas,
|
||||
'nilai_agunan' => $item->nilai_agunan_formatted ?? '',
|
||||
'status_agunan' => $item->status_agunan,
|
||||
'status_badge' => $item->status_badge ?? '',
|
||||
'created_by' => $item->creator?->name ?? '-',
|
||||
'created_at' => dateFormat($item->created_at, true) ?? $item->created_at->format('d/m/Y H:i')
|
||||
];
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / ($request->get('size', 10)));
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = $request->get('page', 1);
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $transformedData,
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error in laporan slik datatables: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => 0,
|
||||
'recordsFiltered' => 0,
|
||||
'pageCount' => 0,
|
||||
'page' => 1,
|
||||
'totalCount' => 0,
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
try {
|
||||
$laporanSlik = LaporanSlik::findOrFail($id);
|
||||
return view('lpj::laporan-slik.show', compact('laporanSlik'));
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error showing laporan slik: ' . $e->getMessage());
|
||||
return back()->with('error', 'Data tidak ditemukan');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export laporan SLIK to Excel
|
||||
*/
|
||||
public function export(Request $request)
|
||||
{
|
||||
try {
|
||||
$query = LaporanSlik::query();
|
||||
|
||||
// Apply filters
|
||||
if ($request->has('search') && $request->search) {
|
||||
$search = $request->search;
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('nama_debitur', 'like', "%{$search}%")
|
||||
->orWhere('no_rekening', 'like', "%{$search}%")
|
||||
->orWhere('cif', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->has('year') && $request->year) {
|
||||
$query->where('tahun', $request->year);
|
||||
}
|
||||
|
||||
if ($request->has('month') && $request->month) {
|
||||
$query->where('bulan', $request->month);
|
||||
}
|
||||
|
||||
if ($request->has('status') && $request->status) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
$filename = 'laporan-slik-' . now()->format('Y-m-d-His') . '.xlsx';
|
||||
|
||||
return Excel::download(new LaporanSlikExport($query), $filename);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error exporting laporan slik: ' . $e->getMessage());
|
||||
return back()->with('error', 'Gagal export data: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Exports\NilaiPlafondExport;
|
||||
use Modules\Lpj\Http\Requests\NilaiPlafondRequest;
|
||||
@@ -14,137 +16,239 @@
|
||||
{
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Menampilkan halaman daftar Nilai Plafond.
|
||||
* Log setiap akses dan sebelum return view.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('lpj::nilai_plafond.index');
|
||||
Log::info('NilaiPlafondController@index: akses halaman index');
|
||||
return \view('lpj::nilai_plafond.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menyimpan data Nilai Plafond baru termasuk field biaya.
|
||||
* Gunakan validasi dari NilaiPlafondRequest, log proses, dan bungkus dengan transaksi DB.
|
||||
*/
|
||||
public function store(NilaiPlafondRequest $request)
|
||||
{
|
||||
Log::info('NilaiPlafondController@store: mulai proses simpan');
|
||||
$validate = $request->validated();
|
||||
|
||||
if ($validate) {
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Save to database
|
||||
NilaiPlafond::create($validate);
|
||||
return redirect()
|
||||
$record = NilaiPlafond::create($validate);
|
||||
DB::commit();
|
||||
|
||||
Log::info('NilaiPlafondController@store: simpan berhasil', ['id' => $record->id]);
|
||||
return \redirect()
|
||||
->route('basicdata.nilai-plafond.index')
|
||||
->with('success', 'Jenis Aset created successfully');
|
||||
->with('success', 'Nilai Plafond berhasil dibuat');
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
DB::rollBack();
|
||||
Log::error('NilaiPlafondController@store: simpan gagal', ['error' => $e->getMessage()]);
|
||||
|
||||
return \redirect()
|
||||
->route('basicdata.nilai-plafond.create')
|
||||
->with('error', 'Failed to create nilai plafond');
|
||||
->with('error', 'Gagal membuat Nilai Plafond');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan form pembuatan Nilai Plafond.
|
||||
* Log akses sebelum return view.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('lpj::nilai_plafond.create');
|
||||
Log::info('NilaiPlafondController@create: akses halaman create');
|
||||
return \view('lpj::nilai_plafond.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan form edit Nilai Plafond berdasarkan ID.
|
||||
* Gunakan transaksi untuk pembacaan data dan logging.
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$nilaiPlafond = NilaiPlafond::find($id);
|
||||
return view('lpj::nilai_plafond.create', compact('nilaiPlafond'));
|
||||
Log::info('NilaiPlafondController@edit: mulai proses edit', ['id' => $id]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$nilaiPlafond = NilaiPlafond::find($id);
|
||||
DB::commit();
|
||||
|
||||
Log::info('NilaiPlafondController@edit: data ditemukan', ['id' => $id]);
|
||||
return \view('lpj::nilai_plafond.create', compact('nilaiPlafond'));
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error('NilaiPlafondController@edit: gagal mengambil data', ['id' => $id, 'error' => $e->getMessage()]);
|
||||
return \redirect()
|
||||
->route('basicdata.nilai-plafond.index')
|
||||
->with('error', 'Gagal mengambil data Nilai Plafond');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Memperbarui data Nilai Plafond termasuk field biaya.
|
||||
* Validasi input, logging, dan gunakan transaksi DB.
|
||||
*/
|
||||
public function update(NilaiPlafondRequest $request, $id)
|
||||
{
|
||||
Log::info('NilaiPlafondController@update: mulai proses update', ['id' => $id]);
|
||||
$validate = $request->validated();
|
||||
|
||||
if ($validate) {
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Update in database
|
||||
$nilaiPlafond = NilaiPlafond::find($id);
|
||||
if (!$nilaiPlafond) {
|
||||
Log::warning('NilaiPlafondController@update: data tidak ditemukan', ['id' => $id]);
|
||||
DB::rollBack();
|
||||
return \redirect()
|
||||
->route('basicdata.nilai-plafond.index')
|
||||
->with('error', 'Data Nilai Plafond tidak ditemukan');
|
||||
}
|
||||
|
||||
$nilaiPlafond->update($validate);
|
||||
return redirect()
|
||||
DB::commit();
|
||||
|
||||
Log::info('NilaiPlafondController@update: update berhasil', ['id' => $id]);
|
||||
return \redirect()
|
||||
->route('basicdata.nilai-plafond.index')
|
||||
->with('success', 'Jenis Aset updated successfully');
|
||||
->with('success', 'Nilai Plafond berhasil diperbarui');
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
DB::rollBack();
|
||||
Log::error('NilaiPlafondController@update: update gagal', ['id' => $id, 'error' => $e->getMessage()]);
|
||||
|
||||
return \redirect()
|
||||
->route('basicdata.nilai-plafond.edit', $id)
|
||||
->with('error', 'Failed to update nilai plafond');
|
||||
->with('error', 'Gagal memperbarui Nilai Plafond');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menghapus data Nilai Plafond berdasarkan ID.
|
||||
* Logging setiap langkah dan gunakan transaksi DB.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
Log::info('NilaiPlafondController@destroy: mulai proses hapus', ['id' => $id]);
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Delete from database
|
||||
$nilaiPlafond = NilaiPlafond::find($id);
|
||||
$nilaiPlafond->delete();
|
||||
if (!$nilaiPlafond) {
|
||||
DB::rollBack();
|
||||
Log::warning('NilaiPlafondController@destroy: data tidak ditemukan', ['id' => $id]);
|
||||
return \response()->json(['success' => false, 'message' => 'Data Nilai Plafond tidak ditemukan']);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Jenis Aset deleted successfully']);
|
||||
$nilaiPlafond->delete();
|
||||
DB::commit();
|
||||
|
||||
Log::info('NilaiPlafondController@destroy: hapus berhasil', ['id' => $id]);
|
||||
return \response()->json(['success' => true, 'message' => 'Nilai Plafond berhasil dihapus']);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to delete nilai plafond']);
|
||||
DB::rollBack();
|
||||
Log::error('NilaiPlafondController@destroy: hapus gagal', ['id' => $id, 'error' => $e->getMessage()]);
|
||||
return \response()->json(['success' => false, 'message' => 'Gagal menghapus Nilai Plafond']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menyediakan data untuk datatables dengan pencarian, sortir, dan paginasi.
|
||||
* Logging proses dan gunakan transaksi DB untuk konsistensi pembacaan.
|
||||
*/
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
Log::info('NilaiPlafondController@dataForDatatables: mulai proses');
|
||||
|
||||
if (is_null($this->user) || !$this->user->can('nilai_plafond.view')) {
|
||||
//abort(403, 'Sorry! You are not allowed to view users.');
|
||||
}
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = NilaiPlafond::query();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Retrieve data from the database
|
||||
$query = NilaiPlafond::query();
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('code', 'LIKE', "%$search%");
|
||||
$q->orWhere('name', 'LIKE', "%$search%");
|
||||
});
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('code', 'LIKE', "%$search%");
|
||||
$q->orWhere('name', 'LIKE', "%$search%");
|
||||
});
|
||||
}
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
$query->orderBy($column, $order);
|
||||
}
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get the data for the current page
|
||||
$data = $query->get();
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = 0 + 1;
|
||||
|
||||
DB::commit();
|
||||
Log::info('NilaiPlafondController@dataForDatatables: proses selesai, mengembalikan data');
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return \response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $data,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error('NilaiPlafondController@dataForDatatables: gagal memproses data', ['error' => $e->getMessage()]);
|
||||
return \response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => 0,
|
||||
'recordsFiltered' => 0,
|
||||
'pageCount' => 0,
|
||||
'page' => 1,
|
||||
'totalCount' => 0,
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField');
|
||||
$query->orderBy($column, $order);
|
||||
}
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get the data for the current page
|
||||
$data = $query->get();
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = 0 + 1;
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengekspor data Nilai Plafond ke Excel.
|
||||
* Log akses sebelum proses download.
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
Log::info('NilaiPlafondController@export: mulai proses export');
|
||||
return Excel::download(new NilaiPlafondExport, 'nilai_plafond.xlsx');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Modules\Lpj\Http\Requests\NocRequest;
|
||||
use Modules\Lpj\Models\Noc;
|
||||
use Modules\Lpj\Models\Bucok;
|
||||
use Modules\Lpj\Models\Noc;
|
||||
use Modules\Lpj\Models\PersetujuanPenawaran;
|
||||
use Modules\Lpj\Models\JenisPenilaian;
|
||||
|
||||
class NocController extends Controller
|
||||
{
|
||||
@@ -24,13 +26,15 @@
|
||||
public function pembayaran()
|
||||
{
|
||||
$persetujuanPenawarans = PersetujuanPenawaran::all();
|
||||
return view('lpj::noc.pembayaran', compact('persetujuanPenawarans'));
|
||||
$jenisPenilaians = JenisPenilaian::get();
|
||||
return view('lpj::noc.pembayaran', compact('persetujuanPenawarans', 'jenisPenilaians'));
|
||||
}
|
||||
|
||||
public function penyelesaian()
|
||||
{
|
||||
$persetujuanPenawarans = PersetujuanPenawaran::all();
|
||||
return view('lpj::noc.penyelesaian', compact('persetujuanPenawarans'));
|
||||
$jenisPenilaians = JenisPenilaian::get();
|
||||
return view('lpj::noc.penyelesaian', compact('persetujuanPenawarans', 'jenisPenilaians'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,15 +49,13 @@
|
||||
public function store(NocRequest $request)
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
|
||||
$validated['updated_by'] = Auth::id();
|
||||
if (request()->get('status_bayar') == "sudah_bayar") {
|
||||
$validated['status'] = '1';
|
||||
$status = "spk";
|
||||
} else {
|
||||
$status = "persetujuan-penawaran";
|
||||
}
|
||||
|
||||
|
||||
$dataNoc = [
|
||||
'nominal_bayar' => $validated['nominal_bayar'],
|
||||
'total_pembukuan' => $validated['total_pembukuan'],
|
||||
@@ -66,13 +68,23 @@
|
||||
'nominal_lebih_bayar' => $validated['nominal_lebih_bayar'] ?? '0',
|
||||
'bukti_pengembalian' => $validated['bukti_pengembalian'] ?? '',
|
||||
];
|
||||
$noc = Noc::updateOrCreate(
|
||||
|
||||
if($validated['permohonan_id']){
|
||||
$noc = Noc::updateOrCreate(
|
||||
[
|
||||
'permohonan_id' => $validated['permohonan_id'],
|
||||
'persetujuan_penawaran_id' => $validated['persetujuan_penawaran_id'],
|
||||
],
|
||||
$dataNoc,
|
||||
);
|
||||
} else {
|
||||
$noc = Noc::updateOrCreate(
|
||||
[
|
||||
'persetujuan_penawaran_id' => $validated['persetujuan_penawaran_id'],
|
||||
],
|
||||
$dataNoc,
|
||||
);
|
||||
}
|
||||
|
||||
$folderPath = 'noc/' . request()->get('persetujuan_penawaran_id') . '/bukti_ksl/';
|
||||
|
||||
@@ -84,26 +96,13 @@
|
||||
}
|
||||
$noc->save();
|
||||
|
||||
/* Update the status of the related permohonan to 'spk'
|
||||
$permohonan = Permohonan::find(request()->get('permohonan_id'));
|
||||
if ($permohonan) {
|
||||
$permohonan->status_bayar = request()->get('status_pembayar');
|
||||
if ($permohonan->jenis_penilaian_id == 2) {
|
||||
$permohonan->status = $status;
|
||||
}
|
||||
$permohonan->save();
|
||||
|
||||
// andy add, update status penawaran.status='spk'
|
||||
// $penawaran = PenawaranTender::where('nomor_registrasi',$permohonan->nomor_registrasi)->first();
|
||||
if ($permohonan->jenis_penilaian_id == 2) {
|
||||
PenawaranTender::where('nomor_registrasi', $permohonan->nomor_registrasi)->update([
|
||||
'status' => $status,
|
||||
'updated_by' => Auth::id(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
// andy add, update status penawaran.status='spk'
|
||||
}*/
|
||||
$bucok = Bucok::where('nomor_tiket', $noc->nomor_tiket)->orWhere('permohonan_id', $noc->permohonan_id)->first();
|
||||
if($bucok){
|
||||
$bucok->nominal_penyelesaian = $noc->total_pembukuan ?? '';
|
||||
$bucok->tanggal_penyelesaian = $noc->tanggal_pembayaran ?? date('Y-m-d');
|
||||
$bucok->penyelesaian = 'Selesai';
|
||||
$bucok->save();
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('noc.index')->with('success', 'NOC berhasil disimpan.');
|
||||
@@ -116,6 +115,7 @@
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
|
||||
if($request->get('is_memo')){
|
||||
|
||||
$memo = Noc::find($request->get('is_memo'));
|
||||
@@ -220,15 +220,19 @@
|
||||
$query = PersetujuanPenawaran::query();
|
||||
|
||||
// Filter for pembayaran (where memo_penyelesaian is null)
|
||||
$query->whereDoesntHave('noc', function($q) {
|
||||
/*$query->whereDoesntHave('noc', function($q) {
|
||||
$q->whereNotNull('memo_penyelesaian');
|
||||
});
|
||||
});*/
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan.debiture','name', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan.jenisPenilaian', 'name', 'LIKE', '%' . $search . '%')
|
||||
->orWhere('nomor_tiket', 'LIKE', '%' . $search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -260,10 +264,11 @@
|
||||
$data = $data->map(function ($persetujuanPenawaran) {
|
||||
return [
|
||||
'id' => $persetujuanPenawaran->id,
|
||||
'nomor_registrasi' => $persetujuanPenawaran->permohonan->nomor_registrasi ?? $persetujuanPenawaran->penawaran->nomor_registrasi,
|
||||
'nama_debitur' => $persetujuanPenawaran->permohonan->debiture->name ?? $persetujuanPenawaran->penawaran->permohonan->debiture->name,
|
||||
'kode_cabang' => $persetujuanPenawaran->permohonan->branch->code ?? $persetujuanPenawaran->penawaran->permohonan->branch->code,
|
||||
'cabang' => $persetujuanPenawaran->permohonan->branch->name ?? $persetujuanPenawaran->penawaran->permohonan->branch->name,
|
||||
'nomor_registrasi' => $persetujuanPenawaran->permohonan?->nomor_registrasi ?? $persetujuanPenawaran->penawaran?->nomor_registrasi,
|
||||
'nomor_tiket' => $persetujuanPenawaran->nomor_tiket ?? '',
|
||||
'nama_debitur' => $persetujuanPenawaran?->permohonan?->debiture->name ?? $persetujuanPenawaran->penawaran?->permohonan?->debiture->name ?? $persetujuanPenawaran->noc?->debiture->name,
|
||||
'kode_cabang' => $persetujuanPenawaran?->permohonan?->branch->code ?? $persetujuanPenawaran->penawaran?->permohonan?->branch->code ?? $persetujuanPenawaran->noc?->branch->code,
|
||||
'cabang' => $persetujuanPenawaran?->permohonan?->branch->name ?? $persetujuanPenawaran->penawaran?->permohonan?->branch->name ?? $persetujuanPenawaran->noc?->branch->name,
|
||||
'tanggal_pembayaran' => dateFormat(
|
||||
$persetujuanPenawaran->noc->tanggal_pembayaran ?? $persetujuanPenawaran->noc?->created_at,
|
||||
true,
|
||||
@@ -273,12 +278,20 @@
|
||||
'nominal_diterima' => currencyFormat(
|
||||
$persetujuanPenawaran->noc->nominal_bayar ?? 0,
|
||||
),
|
||||
'jenis_penilaian' => $persetujuanPenawaran->permohonan?->jenisPenilaian?->name ?? "",
|
||||
'bukti_ksl' => $persetujuanPenawaran->noc->bukti_ksl ?? $persetujuanPenawaran->bukti_ksl ?? null,
|
||||
'bukti_bayar' => $persetujuanPenawaran->bukti_bayar ?? null,
|
||||
'updated_at' => dateFormat($persetujuanPenawaran->updated_at, true),
|
||||
];
|
||||
})->sortBy('updated_at', 1)->values();
|
||||
|
||||
// Calculate total nominal diterima from all filtered data (not just current page)
|
||||
$totalNominalDiterima = $data->sum(function ($item) {
|
||||
// Extract numeric value from formatted currency string
|
||||
$nominal = str_replace(['Rp', '.', ',00'], '', $item['nominal_diterima']);
|
||||
return (float) $nominal;
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
|
||||
@@ -293,6 +306,7 @@
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'totalNominalDiterima' => $totalNominalDiterima,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
@@ -307,7 +321,7 @@
|
||||
$query = PersetujuanPenawaran::query();
|
||||
|
||||
// Filter for penyelesaian (where memo_penyelesaian is not null)
|
||||
$query->whereHas('noc', function($q) {
|
||||
$query->whereDoesntHave('noc', function($q) {
|
||||
$q->whereNotNull('memo_penyelesaian');
|
||||
});
|
||||
|
||||
@@ -315,7 +329,15 @@
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan.debiture','name', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan.jenisPenilaian', 'name', 'LIKE', '%' . $search . '%')
|
||||
->orWhere('nomor_tiket', 'LIKE', '%' . $search . '%'); $q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan.debiture','name', 'LIKE', '%' . $search . '%')
|
||||
->orWhereRelation('permohonan.jenisPenilaian', 'name', 'LIKE', '%' . $search . '%')
|
||||
->orWhere('nomor_tiket', 'LIKE', '%' . $search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -347,9 +369,11 @@
|
||||
$data = $data->map(function ($persetujuanPenawaran) {
|
||||
return [
|
||||
'id' => $persetujuanPenawaran->id,
|
||||
'nomor_registrasi' => $persetujuanPenawaran->permohonan->nomor_registrasi ?? $persetujuanPenawaran->penawaran->nomor_registrasi,
|
||||
'nama_debitur' => $persetujuanPenawaran->permohonan->debiture->name ?? $persetujuanPenawaran->penawaran->permohonan->debiture->name,
|
||||
'cabang' => $persetujuanPenawaran->permohonan->branch->name ?? $persetujuanPenawaran->penawaran->permohonan->branch->name,
|
||||
'nomor_registrasi' => $persetujuanPenawaran->permohonan?->nomor_registrasi ?? $persetujuanPenawaran->penawaran?->nomor_registrasi ?? '',
|
||||
'nomor_tiket' => $persetujuanPenawaran->nomor_tiket ?? '',
|
||||
'nama_debitur' => $persetujuanPenawaran->permohonan?->debiture?->name ?? $persetujuanPenawaran->penawaran?->permohonan?->debiture?->name ?? $persetujuanPenawaran->noc?->debiture?->name,
|
||||
'kode_cabang' => $persetujuanPenawaran?->permohonan?->branch?->code ?? $persetujuanPenawaran->penawaran?->permohonan?->branch?->code ?? $persetujuanPenawaran->noc?->branch?->code ?? '',
|
||||
'cabang' => $persetujuanPenawaran->permohonan?->branch?->name ?? $persetujuanPenawaran->penawaran?->permohonan?->branch?->name ?? $persetujuanPenawaran->noc?->branch?->name ?? '',
|
||||
'tanggal_pembayaran' => dateFormat(
|
||||
$persetujuanPenawaran->noc->tanggal_pembayaran ?? $persetujuanPenawaran->noc?->created_at,
|
||||
true,
|
||||
@@ -359,6 +383,7 @@
|
||||
'nominal_diterima' => currencyFormat(
|
||||
$persetujuanPenawaran->noc->nominal_bayar ?? 0,
|
||||
),
|
||||
'jenis_penilaian' => $persetujuanPenawaran->permohonan?->jenisPenilaian?->name ?? "",
|
||||
'bukti_ksl' => $persetujuanPenawaran->noc->bukti_ksl ?? $persetujuanPenawaran->bukti_ksl ?? null,
|
||||
'bukti_bayar' => $persetujuanPenawaran->bukti_bayar ?? null,
|
||||
'memo_penyelesaian' => $persetujuanPenawaran->noc->memo_penyelesaian ?? $persetujuanPenawaran->memo_penyelesaian ?? null,
|
||||
@@ -371,7 +396,15 @@
|
||||
true) : '-',
|
||||
'updated_at' => dateFormat($persetujuanPenawaran->updated_at, true),
|
||||
];
|
||||
})->sortBy('updated_at', 1);
|
||||
})->sortBy('updated_at', 1)->values();
|
||||
|
||||
|
||||
// Calculate total nominal diterima from all filtered data (not just current page)
|
||||
$totalNominalDiterima = $data->sum(function ($item) {
|
||||
// Extract numeric value from formatted currency string
|
||||
$nominal = str_replace(['Rp', '.', ',00'], '', $item['nominal_diterima']);
|
||||
return (float) $nominal;
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / $request->get('size'));
|
||||
@@ -387,6 +420,7 @@
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'totalNominalDiterima' => $totalNominalDiterima,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -3,26 +3,12 @@
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Location\Models\City;
|
||||
use Modules\Location\Models\District;
|
||||
use Modules\Location\Models\Province;
|
||||
use Modules\Location\Models\Village;
|
||||
use Modules\Lpj\Exports\PermohonanExport;
|
||||
use Modules\Lpj\Http\Requests\PermohonanRequest;
|
||||
use Modules\Lpj\Models\Branch;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Lpj\Models\DokumenJaminan;
|
||||
use Modules\Lpj\Models\JenisFasilitasKredit;
|
||||
use Modules\Lpj\Models\NilaiPlafond;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\PermohonanPembatalan;
|
||||
use Modules\Lpj\Models\StatusPermohonan;
|
||||
use Modules\Lpj\Models\TujuanPenilaian;
|
||||
use Modules\Lpj\Services\PermohonanHistoryService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PembatalanController extends Controller
|
||||
{
|
||||
@@ -92,6 +78,10 @@
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = PermohonanPembatalan::query();
|
||||
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
|
||||
$query = $query->whereRelation('permohonan', 'branch_id', Auth::user()->branch_id);
|
||||
}
|
||||
|
||||
$query = $query->orderBy('created_at', 'desc');
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Modules\Lpj\Http\Controllers;
|
||||
use Exception;
|
||||
use Modules\Lpj\Models\Noc;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Lpj\Models\Bucok;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -12,6 +13,7 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Modules\Lpj\Models\PenawaranTender;
|
||||
use Modules\Lpj\Models\PersetujuanPenawaran;
|
||||
use Modules\Lpj\Http\Requests\PersetujuanPenawaranRequest;
|
||||
|
||||
class PembayaranController extends Controller
|
||||
{
|
||||
public $user;
|
||||
@@ -42,6 +44,9 @@ class PembayaranController extends Controller
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = PersetujuanPenawaran::query();
|
||||
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
|
||||
$query = $query->whereRelation('permohonan', 'branch_id', Auth::user()->branch_id);
|
||||
}
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
@@ -109,11 +114,23 @@ class PembayaranController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(){
|
||||
return view('lpj::pembayaran.create');
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$permohonan = Permohonan::find($id);
|
||||
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
|
||||
$req = request()->all();
|
||||
|
||||
if(isset($req['tiket'])){
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::find($id);
|
||||
$permohonan = Permohonan::find($persetujuanPenawaran?->permohonan_id);
|
||||
} else {
|
||||
$permohonan = Permohonan::find($id);
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
|
||||
}
|
||||
|
||||
return view('lpj::pembayaran.form', compact('permohonan', 'persetujuanPenawaran'));
|
||||
}
|
||||
|
||||
@@ -130,51 +147,93 @@ class PembayaranController extends Controller
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
|
||||
return view('lpj::pembayaran.form-lebih', compact('noc','permohonan','persetujuanPenawaran'));
|
||||
}
|
||||
|
||||
public function store(PersetujuanPenawaranRequest $request)
|
||||
{
|
||||
if($req['type'] == 'kurang_bayar'){
|
||||
$noc = Noc::find($req['noc_id']);
|
||||
$noc->nominal_pelunasan = $req['nominal_pelunasan'];
|
||||
if (request()->hasFile('bukti_ksl_kurang_bayar')) {
|
||||
$folderPath = 'persetujuan_penawaran/bukti_ksl_kurang_bayar/' . $req['noc_id'];
|
||||
$noc->bukti_ksl_kurang_bayar = $request->file('bukti_ksl_kurang_bayar')->store($folderPath, 'public');
|
||||
$req = request()->all();
|
||||
|
||||
if(isset($req['type'])){
|
||||
if($req['type'] == 'create'){
|
||||
$data = [
|
||||
'nomor_tiket' => $req['nomor_tiket'] ?? '',
|
||||
'nominal_bayar' => $req['nominal_bayar'] ?? '',
|
||||
'catatan' => $req['catatan'] ?? ''
|
||||
];
|
||||
|
||||
if(request()->hasFile('bukti_bayar')){
|
||||
$folderPath = 'persetujuan_penawaran/bukti_bayar/' . $req['nomor_tiket'];
|
||||
$data['bukti_bayar'] = $request->file('bukti_bayar')->store($folderPath, 'public');
|
||||
}
|
||||
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::create($data);
|
||||
$noc = [
|
||||
'persetujuan_penawaran_id' => $persetujuanPenawaran->id,
|
||||
'nomor_tiket' => $req['nomor_tiket'] ?? '',
|
||||
'debiture_id' => $req['debitur_id'] ?? '',
|
||||
'branch_id' => Auth::user()->branch_id,
|
||||
];
|
||||
$noc = Noc::create($noc);
|
||||
|
||||
$bucok = [
|
||||
'tanggal_penuh' => $persetujuanPenawaran->created_at ?? $noc->created_at,
|
||||
'tanggal' => $persetujuanPenawaran->created_at?->format('d') ?? $noc->created_at?->format('d'),
|
||||
'bulan' => $persetujuanPenawaran->created_at?->format('m') ?? $noc->created_at?->format('m'),
|
||||
'tahun' => $persetujuanPenawaran->created_at?->format('Y') ?? $noc->created_at?->format('Y'),
|
||||
'nomor_tiket' => $req['nomor_tiket'] ?? '',
|
||||
'nominal' => $req['nominal_bayar'] ?? '',
|
||||
'nominal_berjalan' => $req['nominal_bayar'] ?? '',
|
||||
'penyelesaian' => 'Belum Selesai',
|
||||
'nama_sub_direktorat' => $noc->branch?->name ?? '',
|
||||
'nama_direktorat_cabang' => $noc->branch?->name ?? '',
|
||||
];
|
||||
|
||||
Bucok::updateOrCreate([
|
||||
'nomor_tiket' => $req['nomor_tiket'] ?? '',
|
||||
], $bucok);
|
||||
|
||||
return redirect()
|
||||
->route('pembayaran.index')->with('success', 'Pembayaran berhasil disimpan.');
|
||||
}
|
||||
$noc->save();
|
||||
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::find($noc->persetujuan_penawaran_id);
|
||||
$persetujuanPenawaran->bukti_ksl_kurang_bayar = $noc->bukti_ksl_kurang_bayar;
|
||||
$persetujuanPenawaran->nominal_kurang_bayar = $req['nominal_pelunasan'];
|
||||
$persetujuanPenawaran->save();
|
||||
return redirect()
|
||||
->route('pembayaran.kurang.index')->with('success', 'Pelunasan Kurang Bayar berhasil disimpan.');
|
||||
}
|
||||
if($req['type'] == 'kurang_bayar'){
|
||||
$noc = Noc::find($req['noc_id']);
|
||||
$noc->nominal_pelunasan = $req['nominal_pelunasan'];
|
||||
if (request()->hasFile('bukti_ksl_kurang_bayar')) {
|
||||
$folderPath = 'persetujuan_penawaran/bukti_ksl_kurang_bayar/' . $req['noc_id'];
|
||||
$noc->bukti_ksl_kurang_bayar = $request->file('bukti_ksl_kurang_bayar')->store($folderPath, 'public');
|
||||
}
|
||||
$noc->save();
|
||||
|
||||
if($req['type'] == 'lebih_bayar'){
|
||||
$noc = Noc::find($req['noc_id']);
|
||||
if (request()->hasFile('bukti_ksl_lebih_bayar')) {
|
||||
$folderPath = 'persetujuan_penawaran/bukti_ksl_lebih_bayar/' . $req['noc_id'];
|
||||
$noc->bukti_ksl_lebih_bayar = $request->file('bukti_ksl_lebih_bayar')->store($folderPath, 'public');
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::find($noc->persetujuan_penawaran_id);
|
||||
$persetujuanPenawaran->bukti_ksl_kurang_bayar = $noc->bukti_ksl_kurang_bayar;
|
||||
$persetujuanPenawaran->nominal_kurang_bayar = $req['nominal_pelunasan'];
|
||||
$persetujuanPenawaran->save();
|
||||
return redirect()
|
||||
->route('pembayaran.kurang.index')->with('success', 'Pelunasan Kurang Bayar berhasil disimpan.');
|
||||
}
|
||||
$noc->save();
|
||||
|
||||
return redirect()
|
||||
->route('pembayaran.lebih.index')->with('success', 'Pengembalian Lebih Bayar berhasil disimpan.');
|
||||
if($req['type'] == 'lebih_bayar'){
|
||||
$noc = Noc::find($req['noc_id']);
|
||||
if (request()->hasFile('bukti_ksl_lebih_bayar')) {
|
||||
$folderPath = 'persetujuan_penawaran/bukti_ksl_lebih_bayar/' . $req['noc_id'];
|
||||
$noc->bukti_ksl_lebih_bayar = $request->file('bukti_ksl_lebih_bayar')->store($folderPath, 'public');
|
||||
}
|
||||
$noc->save();
|
||||
|
||||
return redirect()
|
||||
->route('pembayaran.lebih.index')->with('success', 'Pengembalian Lebih Bayar berhasil disimpan.');
|
||||
}
|
||||
}
|
||||
|
||||
$validated = $request->validated();
|
||||
$validated['nominal_bayar'] = $req['nominal_bayar'] ?? 0;
|
||||
$validated['created_by'] = Auth::id();
|
||||
$validated['created_at'] = now();
|
||||
$validated['status'] = '0';
|
||||
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $validated['permohonan_id'] ?? null)->first();
|
||||
$permohonan = Permohonan::find(request()->get('permohonan_id'));
|
||||
if ($persetujuanPenawaran) {
|
||||
// if (isset($validated['penawaran_id'])) {
|
||||
|
||||
// $persetujuanPenawaran = PersetujuanPenawaran::create(
|
||||
// ['penawaran_id' => $validated['penawaran_id']],
|
||||
// $validated,
|
||||
// );
|
||||
|
||||
$persetujuanPenawaran->fill($validated);
|
||||
|
||||
if ($request->hasFile('bukti_bayar')) {
|
||||
@@ -186,7 +245,7 @@ class PembayaranController extends Controller
|
||||
|
||||
$permohonan->approve_bayar_by = null;
|
||||
$permohonan->approve_bayar_at = null;
|
||||
$permohonan->status = 'done';
|
||||
$permohonan->status = 'proses-laporan';
|
||||
$permohonan->save();
|
||||
} else {
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::create(
|
||||
@@ -210,6 +269,33 @@ class PembayaranController extends Controller
|
||||
$persetujuanPenawaran->save();
|
||||
}
|
||||
|
||||
$bucok = [
|
||||
'tanggal_penuh' => $persetujuanPenawaran->created_at ?? $validated['created_at'],
|
||||
'tanggal' => $persetujuanPenawaran->created_at?->format('d') ?? $validated['created_at']?->format('d'),
|
||||
'bulan' => $persetujuanPenawaran->created_at?->format('m') ?? $validated['created_at']?->format('m'),
|
||||
'tahun' => $persetujuanPenawaran->created_at?->format('Y') ?? $validated['created_at']?->format('Y'),
|
||||
'nomor_tiket' => $req['nomor_tiket'] ?? '',
|
||||
'nominal' => $req['nominal_bayar'] ?? '',
|
||||
'nominal_berjalan' => $req['nominal_bayar'] ?? '',
|
||||
'penyelesaian' => 'Belum Selesai',
|
||||
'nama_sub_direktorat' => $noc->branch?->name ?? '',
|
||||
'nama_direktorat_cabang' => $noc->branch?->name ?? '',
|
||||
'permohonan_id' => $permohonan->id,
|
||||
'nomor_registrasi' => $permohonan->nomor_registrasi,
|
||||
];
|
||||
|
||||
|
||||
if(isset($req['nomor_tiket']) && $req['nomor_tiket'] !=''){
|
||||
Bucok::updateOrCreate([
|
||||
'nomor_registrasi' => $permohonan->nomor_registrasi,
|
||||
'nomor_tiket' => $req['nomor_tiket'],
|
||||
], $bucok);
|
||||
} else {
|
||||
Bucok::updateOrCreate([
|
||||
'nomor_registrasi' => $permohonan->nomor_registrasi
|
||||
], $bucok);
|
||||
}
|
||||
|
||||
// Update the status of the related permohonan to 'spk'
|
||||
|
||||
if ($permohonan) {
|
||||
@@ -260,11 +346,11 @@ class PembayaranController extends Controller
|
||||
} else {
|
||||
$data['status_bayar'] = 'sudah_bayar';
|
||||
$data['status'] = 'proses-laporan';
|
||||
}
|
||||
|
||||
if ($permohonan->jenis_penilaian_id == 2) {
|
||||
$data['status_bayar'] = 'sudah_bayar';
|
||||
$data['status'] = 'spk';
|
||||
if ($permohonan->jenis_penilaian_id == 2) {
|
||||
$data['status_bayar'] = 'sudah_bayar';
|
||||
$data['status'] = 'spk';
|
||||
}
|
||||
}
|
||||
|
||||
if ($permohonan->jenis_penilaian_id == 1) {
|
||||
@@ -307,31 +393,27 @@ class PembayaranController extends Controller
|
||||
// abort(403, 'Sorry! You are not allowed to view users.');
|
||||
}
|
||||
|
||||
$query = Permohonan::query()->where(function ($query) {
|
||||
$query->where(['status_bayar' => 'belum_bayar', 'jenis_penilaian_id' => 1])
|
||||
->orWhere('status', 'revisi-pembayaran');
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereNotIn('id', function ($subquery) {
|
||||
$subquery->select('permohonan_id')
|
||||
->from('persetujuan_penawaran')
|
||||
->whereNotNull('permohonan_id');
|
||||
$query = PersetujuanPenawaran::query();
|
||||
|
||||
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
|
||||
$query = $query->whereRelation('permohonan', 'branch_id', Auth::user()->branch_id);
|
||||
}
|
||||
|
||||
/*$query->where(function($q) {
|
||||
$q->whereRelation('permohonan', function($query) {
|
||||
$query->where('status_bayar', 'belum_bayar')
|
||||
->where('jenis_penilaian_id', 1);
|
||||
});
|
||||
});*/
|
||||
$query->orWhereRelation('permohonan','status_bayar','revisi-pembayaran');
|
||||
$query->orWhere(function($q) {
|
||||
$q->where('permohonan_id',null);
|
||||
$q->where('nomor_tiket','!=',null);
|
||||
});
|
||||
|
||||
|
||||
// Pencarian berdasarkan parameter search
|
||||
// Pencarian berdasarkan parameter search
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhereRelation('debiture', 'name', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
|
||||
$q->orWhere('status', 'LIKE', '%' . $search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
// Sorting berdasarkan sortField dan sortOrder
|
||||
@@ -361,7 +443,21 @@ class PembayaranController extends Controller
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Ambil data dengan relasi
|
||||
$data = $query->with(['user', 'debiture', 'branch', 'jenisPenilaian'])->get();
|
||||
$data = $query->get();
|
||||
|
||||
$data = $data->map(function ($item) {
|
||||
return [
|
||||
'id' => $item->permohonan?->id ?? $item->id,
|
||||
'nomor_registrasi' => $item->permohonan?->nomor_registrasi,
|
||||
'nomor_tiket' => $item->nomor_tiket ?? '',
|
||||
'debiture' => $item->permohonan?->debiture ?? $item->noc?->debiture,
|
||||
'user' => $item->permohonan?->user ?? $item->creator,
|
||||
'status_bayar' => $item->permohonan?->status_bayar ?? ($item->nomor_tiket ? 'Sudah Bayar' : ''),
|
||||
'tanggal_permohonan' => $item->permohonan?->tanggal_permohonan ?? '',
|
||||
'branch' => $item->permohonan?->branch ?? $item->noc?->branch,
|
||||
'is_permohonan' => $item->permohonan ?? ''
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
// Hitung jumlah halaman
|
||||
|
||||
@@ -26,18 +26,21 @@ use Modules\Lpj\Http\Requests\FormSurveyorRequest;
|
||||
use Modules\Lpj\Models\Authorization;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Lpj\Services\SaveFormInspesksiService;
|
||||
use Modules\Lpj\Services\PreviewLaporanService;
|
||||
|
||||
class PenilaiController extends Controller
|
||||
{
|
||||
public $user;
|
||||
protected $surveyorController;
|
||||
protected $inspeksiService;
|
||||
protected $previewLaporanService;
|
||||
|
||||
|
||||
public function __construct(SurveyorController $surveyorController, SaveFormInspesksiService $inspeksiService)
|
||||
public function __construct(SurveyorController $surveyorController, SaveFormInspesksiService $inspeksiService, PreviewLaporanService $previewLaporanService)
|
||||
{
|
||||
$this->surveyorController = $surveyorController;
|
||||
$this->inspeksiService = $inspeksiService;
|
||||
$this->previewLaporanService = $previewLaporanService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +157,7 @@ class PenilaiController extends Controller
|
||||
$permohonan = $this->surveyorController->getPermohonanJaminanId($id, $documentId, $jaminanId);
|
||||
|
||||
if ($permohonan->status == 'proses-laporan') {
|
||||
return redirect()->back()->with('error', 'Masih dalam proses laporan');
|
||||
//return redirect()->back()->with('error', 'Masih dalam proses laporan');
|
||||
}
|
||||
|
||||
$basicData = $this->surveyorController->getCommonData();
|
||||
@@ -548,13 +551,7 @@ class PenilaiController extends Controller
|
||||
return view('lpj::penilai.components.call-report', compact('permohonan', 'basicData', 'nomorLaporan', 'forminspeksi', 'cities', 'districts', 'villages', 'cekAlamat', 'callReport'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
@@ -622,7 +619,8 @@ class PenilaiController extends Controller
|
||||
'jenisfasilitasKredit',
|
||||
'penilaian.userPenilai',
|
||||
'penilai',
|
||||
'nilaiPlafond'
|
||||
'nilaiPlafond',
|
||||
'penawaran'
|
||||
])->get();
|
||||
|
||||
// Calculate the page count
|
||||
@@ -700,15 +698,17 @@ class PenilaiController extends Controller
|
||||
'lokasi_lengkap' => $data->lokasi_lengkap ?? '',
|
||||
];
|
||||
|
||||
// Extract data pembanding
|
||||
if (isset($dataPembanding['data_pembanding'])) {
|
||||
foreach ($dataPembanding['data_pembanding'] as $index => $pembanding) {
|
||||
if ($index == 0) {
|
||||
$exportData['pembanding1'] = $pembanding;
|
||||
} elseif ($index == 1) {
|
||||
$exportData['pembanding2'] = $pembanding;
|
||||
} elseif ($index == 2) {
|
||||
$exportData['pembanding3'] = $pembanding;
|
||||
if(isset($dataPembanding)){
|
||||
// Extract data pembanding
|
||||
if (isset($dataPembanding['data_pembanding'])) {
|
||||
foreach ($dataPembanding['data_pembanding'] as $index => $pembanding) {
|
||||
if ($index == 0) {
|
||||
$exportData['pembanding1'] = $pembanding;
|
||||
} elseif ($index == 1) {
|
||||
$exportData['pembanding2'] = $pembanding;
|
||||
} elseif ($index == 2) {
|
||||
$exportData['pembanding3'] = $pembanding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -841,10 +841,10 @@ class PenilaiController extends Controller
|
||||
$permohonan = Permohonan::findOrFail($id);
|
||||
|
||||
if ($permohonan->status === 'proses-laporan') {
|
||||
return response()->json([
|
||||
/*return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Masih proses laporan',
|
||||
], 400);
|
||||
], 400);*/
|
||||
}
|
||||
|
||||
if ($permohonan->status === 'proses-paparan') {
|
||||
@@ -1006,11 +1006,10 @@ class PenilaiController extends Controller
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
|
||||
Inspeksi::create([
|
||||
Inspeksi::updateOrCreate([
|
||||
'permohonan_id' => $validatedData['permohonan_id'],
|
||||
'dokument_id' => $validatedData['dokument_id'],
|
||||
'dokument_id' => $validatedData['dokument_id']
|
||||
],[
|
||||
'data_form' => json_encode($newData),
|
||||
'name' => $validatedData['type']
|
||||
]);
|
||||
@@ -1252,9 +1251,10 @@ class PenilaiController extends Controller
|
||||
|
||||
|
||||
|
||||
Inspeksi::create([
|
||||
Inspeksi::updateOrCreate([
|
||||
'permohonan_id' => $validated['permohonan_id'],
|
||||
'dokument_id' => $validated['dokument_id'],
|
||||
'dokument_id' => $validated['dokument_id']
|
||||
],[
|
||||
'data_form' => json_encode($newData),
|
||||
'name' => $validated['type']
|
||||
]);
|
||||
@@ -1275,8 +1275,6 @@ class PenilaiController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function print_out(Request $request)
|
||||
{
|
||||
$documentId = $request->query('documentId');
|
||||
@@ -1392,6 +1390,15 @@ class PenilaiController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function print_out_laporan($permohonan_id, $document_id, $jaminan_id)
|
||||
{
|
||||
// jika tidak ada id kembalikan ke halaman sebelumnya
|
||||
if (!$permohonan_id || !$document_id || !$jaminan_id) {
|
||||
return redirect()->back()->with('error', 'Laporan tidak valid');
|
||||
}
|
||||
return $this->previewLaporanService->printOutLaporan($permohonan_id, $document_id, $jaminan_id);
|
||||
}
|
||||
|
||||
private function getViewLaporan($tipe)
|
||||
{
|
||||
$viewMap = [
|
||||
@@ -1771,4 +1778,24 @@ class PenilaiController extends Controller
|
||||
'message' => 'Berhasil Revisi Ke surveyor',
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function showLaporanInspeksi(
|
||||
$permohonan_id,
|
||||
$dokumen_id,
|
||||
$jaminan_id,
|
||||
Request $request)
|
||||
{
|
||||
if ($request->type == 'penilai') {
|
||||
$back = route('penilai.show', $permohonan_id);
|
||||
}else{
|
||||
$back = route('surveyor.show', $permohonan_id);
|
||||
}
|
||||
|
||||
return $this->previewLaporanService->previewLaporan($permohonan_id, $dokumen_id, $jaminan_id, $back);
|
||||
}
|
||||
|
||||
public function showInspectionReportReview($permohonan_id, $dokumen_id, $jaminan_id)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,12 +517,18 @@ class PenilaianController extends Controller
|
||||
$role = Auth::user()->roles[0]->name;
|
||||
$status = 'done';
|
||||
$approvalField = null;
|
||||
|
||||
$lpj_ = optional(json_decode($permohonan->penilai->lpj));
|
||||
$npw = $lpj_->total_nilai_pasar_wajar ?? 0;
|
||||
$npw = str_replace('.', '', $npw);
|
||||
|
||||
|
||||
if ($role === 'senior-officer') {
|
||||
$approvalField = 'approval_so';
|
||||
$status = in_array($permohonan->nilai_plafond_id, [3]) ? 'done' : 'proses-laporan';
|
||||
$status = $npw <=1000000000 ? 'done' : 'proses-laporan';
|
||||
} elseif ($role === 'EO Appraisal') {
|
||||
$approvalField = 'approval_eo';
|
||||
$status = in_array($permohonan->nilai_plafond_id, [2, 1]) ? 'done' : 'proses-laporan';
|
||||
$status = $npw <=5000000000 ? 'done' : 'proses-laporan';
|
||||
} elseif ($role === 'DD Appraisal') {
|
||||
$approvalField = 'approval_dd';
|
||||
$status = 'done';
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
use Modules\Lpj\Models\Penilaian;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\PermohonanPembatalan;
|
||||
use Modules\Lpj\Models\PersetujuanPenawaran;
|
||||
use Modules\Lpj\Models\StatusPermohonan;
|
||||
use Modules\Lpj\Models\TujuanPenilaian;
|
||||
use Modules\Lpj\Services\PermohonanHistoryService;
|
||||
use Modules\Lpj\Models\Noc;
|
||||
|
||||
|
||||
|
||||
class PermohonanController extends Controller
|
||||
{
|
||||
@@ -46,6 +50,9 @@
|
||||
{
|
||||
$validate = $request->validated();
|
||||
if ($validate) {
|
||||
if(auth()->user()->hasRole('admin')){
|
||||
$validate['status'] = "preregister";
|
||||
}
|
||||
try {
|
||||
// Process file upload
|
||||
$filePath = null;
|
||||
@@ -168,7 +175,7 @@
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query();
|
||||
|
||||
if (!Auth::user()->hasAnyRole(['administrator'])) {
|
||||
if (!Auth::user()->hasAnyRole(['administrator','admin'])) {
|
||||
$query = $query->where('branch_id', Auth::user()->branch_id);
|
||||
}
|
||||
|
||||
@@ -358,6 +365,55 @@
|
||||
$permohonan->status = $request->status;
|
||||
$permohonan->keterangan = $request->keterangan;
|
||||
$permohonan->save();
|
||||
|
||||
if ($permohonan->status_bayar == 'belum_bayar') {
|
||||
PersetujuanPenawaran::firstOrCreate(
|
||||
['permohonan_id' => $id],
|
||||
['created_by' => Auth::id()]
|
||||
);
|
||||
}
|
||||
|
||||
if ($permohonan->status == 'sudah_dibayar') {
|
||||
|
||||
$documents = $permohonan->dokumenjaminan->first(function ($doc) {
|
||||
return $doc->detail && $doc->detail->contains('name', 'Bukti Bayar');
|
||||
});
|
||||
|
||||
$buktiBayar = $documents->detail->filter(function ($detail) {
|
||||
return $detail->name == 'Bukti Bayar';
|
||||
})->first() ?? null;
|
||||
|
||||
if ($buktiBayar->isEmpty()) {
|
||||
return redirect()->route('authorization.show', $id)->with('error', 'Bukti Bayar harus diunggah');
|
||||
}
|
||||
|
||||
$dokumenJaminan = json_decode($buktiBayar->dokumen_jaminan);
|
||||
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::firstOrCreate(
|
||||
['permohonan_id' => $id],
|
||||
[
|
||||
'created_by' => Auth::id(),
|
||||
'bukti_bayar' => $buktiBayar->first()->dokumen_jaminan[0],
|
||||
]
|
||||
);
|
||||
|
||||
try {
|
||||
Noc::updateOrCreate([
|
||||
'permohonan_id' => $persetujuanPenawaran->permohonan_id,
|
||||
'persetujuan_penawaran_id' => $persetujuanPenawaran->id
|
||||
],[
|
||||
'bukti_bayar' => $persetujuanPenawaran->bukti_bayar,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Failed to create or update NOC: ' . $e->getMessage());
|
||||
return redirect()
|
||||
->route('persetujuan-penawaran.index')
|
||||
->with('error', 'Persetujuan Penawaran berhasil disimpan tetapi gagal membuat NOC: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (Exception $e) {
|
||||
return redirect()->route('authorization.show', $id)->with('error', 'Failed to update permohonan');
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
$validated = $request->validated();
|
||||
$validated['created_by'] = Auth::id();
|
||||
$validated['status'] = '0';
|
||||
$validated['nominal_bayar'] = $validated['biaya_final'];
|
||||
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::updateOrCreate(
|
||||
['penawaran_id' => $validated['penawaran_id']],
|
||||
@@ -179,8 +180,8 @@
|
||||
public function edit($id)
|
||||
{
|
||||
$permohonan = Permohonan::with(['debiture', 'penawaranTender.detail'])->find($id);
|
||||
|
||||
return view('lpj::persetujuan_penawaran.form', compact('permohonan'));
|
||||
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $id)->first();
|
||||
return view('lpj::persetujuan_penawaran.form', compact('permohonan', 'persetujuanPenawaran'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,8 +202,12 @@
|
||||
}
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = Permohonan::query()->where(['status' => 'persetujuan-penawaran']);
|
||||
|
||||
//$query = Permohonan::query()->where(['status' => 'persetujuan-penawaran']);
|
||||
$query = Permohonan::query()
|
||||
->where(['status' => 'persetujuan-penawaran'])
|
||||
->whereHas('penawaranTender', function ($q) {
|
||||
$q->where('status', 'persetujuan-penawaran');
|
||||
});
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
|
||||
441
app/Http/Controllers/ReferensiLinkController.php
Normal file
441
app/Http/Controllers/ReferensiLinkController.php
Normal file
@@ -0,0 +1,441 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Models\ReferensiLink;
|
||||
use Modules\Lpj\Http\Requests\ReferensiLinkRequest;
|
||||
use Modules\Lpj\Exports\ReferensiLinkExport;
|
||||
use Modules\Lpj\Imports\ReferensiLinkImport;
|
||||
|
||||
class ReferensiLinkController extends Controller
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->middleware(function ($request, $next) {
|
||||
$this->user = auth()->user();
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//$this->authorize('referensi-link.view', $this->user);
|
||||
|
||||
$data = [
|
||||
'title' => 'Referensi Link',
|
||||
'subtitle' => 'Daftar Referensi Link',
|
||||
'breadcrumb' => [
|
||||
['url' => route('dashboard'), 'text' => 'Dashboard'],
|
||||
['text' => 'Referensi Link']
|
||||
],
|
||||
'kategoriOptions' => $this->getKategoriOptions(),
|
||||
];
|
||||
|
||||
return view('lpj::referensi_link.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//$this->authorize('referensi-link.create', $this->user);
|
||||
|
||||
$data = [
|
||||
'title' => 'Tambah Referensi Link',
|
||||
'subtitle' => 'Form Tambah Referensi Link Baru',
|
||||
'breadcrumb' => [
|
||||
['url' => route('dashboard'), 'text' => 'Dashboard'],
|
||||
['url' => route('basicdata.referensi-link.index'), 'text' => 'Referensi Link'],
|
||||
['text' => 'Tambah']
|
||||
],
|
||||
'kategoriOptions' => $this->getKategoriOptions(),
|
||||
];
|
||||
|
||||
return view('lpj::referensi_link.create', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(ReferensiLinkRequest $request)
|
||||
{
|
||||
//$this->authorize('referensi-link.create', $this->user);
|
||||
|
||||
try {
|
||||
$validated = $request->validated();
|
||||
|
||||
// Set urutan otomatis jika belum diisi
|
||||
if (empty($validated['urutan'])) {
|
||||
$validated['urutan'] = ReferensiLink::max('urutan') + 1;
|
||||
}
|
||||
|
||||
$referensiLink = ReferensiLink::create($validated);
|
||||
|
||||
return redirect()
|
||||
->route('basicdata.referensi-link.index')
|
||||
->with('success', 'Referensi Link berhasil ditambahkan');
|
||||
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('error', 'Gagal menambahkan Referensi Link: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//$this->authorize('referensi-link.update', $this->user);
|
||||
|
||||
$referensiLink = ReferensiLink::findOrFail($id);
|
||||
|
||||
$data = [
|
||||
'title' => 'Edit Referensi Link',
|
||||
'subtitle' => 'Form Edit Referensi Link',
|
||||
'breadcrumb' => [
|
||||
['url' => route('dashboard'), 'text' => 'Dashboard'],
|
||||
['url' => route('basicdata.referensi-link.index'), 'text' => 'Referensi Link'],
|
||||
['text' => 'Edit']
|
||||
],
|
||||
'referensiLink' => $referensiLink,
|
||||
'kategoriOptions' => $this->getKategoriOptions(),
|
||||
];
|
||||
|
||||
return view('lpj::referensi_link.create', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(ReferensiLinkRequest $request, $id)
|
||||
{
|
||||
//$this->authorize('referensi-link.update', $this->user);
|
||||
|
||||
try {
|
||||
$referensiLink = ReferensiLink::findOrFail($id);
|
||||
$validated = $request->validated();
|
||||
|
||||
$referensiLink->update($validated);
|
||||
|
||||
return redirect()
|
||||
->route('basicdata.referensi-link.index')
|
||||
->with('success', 'Referensi Link berhasil diperbarui');
|
||||
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('error', 'Gagal memperbarui Referensi Link: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//$this->authorize('referensi-link.delete', $this->user);
|
||||
|
||||
try {
|
||||
$referensiLink = ReferensiLink::findOrFail($id);
|
||||
$referensiLink->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Referensi Link berhasil dihapus'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal menghapus Referensi Link: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Datatable API for KTDataTable
|
||||
*/
|
||||
public function dataTable(Request $request)
|
||||
{
|
||||
//$this->authorize('referensi-link.view', $this->user);
|
||||
|
||||
$query = ReferensiLink::with(['createdBy', 'updatedBy'])
|
||||
->select('referensi_link.*');
|
||||
|
||||
// Search
|
||||
$search = $request->input('search');
|
||||
if (!empty($search)) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('name', 'LIKE', "%{$search}%")
|
||||
->orWhere('link', 'LIKE', "%{$search}%")
|
||||
->orWhere('kategori', 'LIKE', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// Optional filters (support multiple request shapes)
|
||||
$filters = $request->input('filters', []);
|
||||
$kategori = $request->input('kategori', $filters['kategori'] ?? null);
|
||||
if (!empty($kategori)) {
|
||||
if (is_array($kategori)) {
|
||||
$query->whereIn('kategori', $kategori);
|
||||
} else {
|
||||
$values = preg_split('/[,|]/', (string) $kategori, -1, PREG_SPLIT_NO_EMPTY);
|
||||
if (count($values) > 1) {
|
||||
$query->whereIn('kategori', $values);
|
||||
} else {
|
||||
$query->where('kategori', $kategori);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$statusRaw = $request->input('status', $filters['status'] ?? $request->input('is_active'));
|
||||
$statusParsed = $this->parseActiveFilter($statusRaw);
|
||||
if ($statusParsed !== null) {
|
||||
$query->where('is_active', $statusParsed);
|
||||
}
|
||||
|
||||
// Sorting
|
||||
$allowedSortFields = ['id', 'name', 'link', 'kategori', 'urutan', 'is_active', 'created_at', 'updated_at'];
|
||||
$sortField = in_array($request->input('sortField', 'urutan'), $allowedSortFields, true)
|
||||
? $request->input('sortField', 'urutan')
|
||||
: 'urutan';
|
||||
$sortOrder = strtolower($request->input('sortOrder', 'asc'));
|
||||
if (in_array($sortOrder, ['asc', 'desc'], true)) {
|
||||
$query->orderBy($sortField, $sortOrder);
|
||||
}
|
||||
|
||||
// Pagination
|
||||
$page = max((int) $request->input('page', 1), 1);
|
||||
$size = max((int) $request->input('size', 10), 1);
|
||||
$totalRecords = (clone $query)->count();
|
||||
$offset = ($page - 1) * $size;
|
||||
$items = $query->skip($offset)->take($size)->get();
|
||||
|
||||
// Map data rows
|
||||
$data = $items->map(function ($row) {
|
||||
return [
|
||||
'id' => $row->id,
|
||||
'name' => $row->name,
|
||||
'link' => '<a href="' . $row->link . '" target="_blank" class="text-primary">' . Str::limit($row->link, 50) . ' <i class="fas fa-external-link-alt"></i></a>',
|
||||
'kategori' => $row->kategori,
|
||||
'status_badge' => $row->status_badge,
|
||||
'urutan' => $row->urutan,
|
||||
'actions' => (
|
||||
(auth()->user()->can('referensi-link.update') ? '<a class="dropdown-item" href="' . route('basicdata.referensi-link.edit', $row->id) . '"><i class="fas fa-edit"></i> Edit</a>' : '') .
|
||||
(auth()->user()->can('referensi-link.delete') ? '<a class="dropdown-item text-danger" href="javascript:void(0)" onclick="deleteData(' . $row->id . ')"><i class="fas fa-trash"></i> Hapus</a>' : '')
|
||||
),
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'draw' => (int) $request->input('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $totalRecords,
|
||||
'pageCount' => (int) ceil($totalRecords / $size),
|
||||
'page' => $page,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data to Excel
|
||||
*/
|
||||
public function export(Request $request)
|
||||
{
|
||||
//$this->authorize('referensi-link.export', $this->user);
|
||||
|
||||
try {
|
||||
$filename = 'referensi_link_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
return Excel::download(new ReferensiLinkExport($request->all()), $filename);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'Gagal export data: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show import form
|
||||
*/
|
||||
public function import()
|
||||
{
|
||||
//$this->authorize('referensi-link.import', $this->user);
|
||||
|
||||
$data = [
|
||||
'title' => 'Import Referensi Link',
|
||||
'subtitle' => 'Import data Referensi Link dari Excel',
|
||||
'breadcrumb' => [
|
||||
['url' => route('dashboard'), 'text' => 'Dashboard'],
|
||||
['url' => route('basicdata.referensi-link.index'), 'text' => 'Referensi Link'],
|
||||
['text' => 'Import']
|
||||
],
|
||||
];
|
||||
|
||||
return view('lpj::referensi_link.import', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process import
|
||||
*/
|
||||
public function importProcess(Request $request)
|
||||
{
|
||||
//$this->authorize('referensi-link.import', $this->user);
|
||||
|
||||
$request->validate([
|
||||
'file' => 'required|mimes:xlsx,xls|max:10240', // max 10MB
|
||||
]);
|
||||
|
||||
try {
|
||||
$import = new ReferensiLinkImport();
|
||||
Excel::import($import, $request->file('file'));
|
||||
|
||||
$stats = $import->getImportStats();
|
||||
|
||||
$message = "Import berhasil! {$stats['success']} data berhasil diimport";
|
||||
if ($stats['failed'] > 0) {
|
||||
$message .= ", {$stats['failed']} data gagal diimport";
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('basicdata.referensi-link.index')
|
||||
->with('success', $message);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Log::error('ReferensiLink import error: ' . $e->getMessage());
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'Gagal import data: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle status (active/inactive)
|
||||
*/
|
||||
public function toggleStatus($id)
|
||||
{
|
||||
//$this->authorize('referensi-link.update', $this->user);
|
||||
|
||||
try {
|
||||
$referensiLink = ReferensiLink::findOrFail($id);
|
||||
$referensiLink->is_active = !$referensiLink->is_active;
|
||||
$referensiLink->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Status berhasil diubah',
|
||||
'status' => $referensiLink->is_active
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal mengubah status: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get kategori options for dropdown
|
||||
*/
|
||||
private function getKategoriOptions()
|
||||
{
|
||||
return [
|
||||
'regulasi' => 'Regulasi',
|
||||
'panduan' => 'Panduan',
|
||||
'prosedur' => 'Prosedur',
|
||||
'formulir' => 'Formulir',
|
||||
'laporan' => 'Laporan',
|
||||
'lainnya' => 'Lainnya'
|
||||
];
|
||||
}
|
||||
|
||||
private function parseActiveFilter($value): ?bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$val = strtolower(trim((string) $value));
|
||||
|
||||
if (in_array($val, ['1', 'true', 'aktif', 'active', 'yes', 'y'], true)) {
|
||||
return true;
|
||||
}
|
||||
if (in_array($val, ['0', 'false', 'tidak', 'inactive', 'nonaktif', 'no', 'n'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download import template
|
||||
*/
|
||||
public function downloadTemplate()
|
||||
{
|
||||
//$this->authorize('referensi-link.import', $this->user);
|
||||
|
||||
try {
|
||||
$headers = [
|
||||
'Nama',
|
||||
'Link',
|
||||
'Kategori',
|
||||
'Deskripsi',
|
||||
'Status Aktif',
|
||||
'Urutan'
|
||||
];
|
||||
|
||||
$filename = 'template_referensi_link_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
return Excel::download(new class($headers) implements \Maatwebsite\Excel\Concerns\FromArray, \Maatwebsite\Excel\Concerns\WithHeadings {
|
||||
private $headers;
|
||||
|
||||
public function __construct($headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
public function array(): array
|
||||
{
|
||||
return [
|
||||
['Contoh Referensi', 'https://example.com', 'panduan', 'Deskripsi contoh referensi link', 'aktif', 1],
|
||||
['Contoh Regulasi', 'https://regulasi.example.com', 'regulasi', 'Deskripsi regulasi', 'aktif', 2],
|
||||
];
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
}, $filename);
|
||||
|
||||
} catch (Exception $e) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'Gagal download template: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
$query =PenawaranTender::query()
|
||||
->select('penawaran.*', 'tujuan_penilaian_kjpp.name as tujuan_penilaian_kjpp_name')
|
||||
->leftJoin('tujuan_penilaian_kjpp', 'tujuan_penilaian_kjpp.id','=','penawaran.tujuan_penilaian_kjpp_id')
|
||||
->where('penawaran.status','=','spk')
|
||||
->where('penawaran.status','=','registrasi-final')
|
||||
->withCount('penawarandetails');
|
||||
|
||||
// Apply search filter if provided
|
||||
|
||||
548
app/Http/Controllers/SlikController.php
Normal file
548
app/Http/Controllers/SlikController.php
Normal file
@@ -0,0 +1,548 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Exports\SlikExport;
|
||||
use Modules\Lpj\Imports\SlikImport;
|
||||
use Modules\Lpj\Models\Slik;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Controller untuk mengelola data Slik
|
||||
*
|
||||
* Menangani operasi CRUD dan import data Slik dari file Excel
|
||||
* dengan fitur server-side processing untuk datatables
|
||||
*
|
||||
* @package Modules\Lpj\Http\Controllers
|
||||
*/
|
||||
class SlikController extends Controller
|
||||
{
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->user = Auth::user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan halaman index slik
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('lpj::slik.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan detail slik
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$slik = Slik::findOrFail($id);
|
||||
return view('lpj::slik.show', compact('slik'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data untuk datatables dengan server-side processing
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function dataForDatatables(Request $request)
|
||||
{
|
||||
// Authorization check dapat ditambahkan sesuai kebutuhan
|
||||
// if (is_null($this->user)) {
|
||||
// abort(403, 'Unauthorized access.');
|
||||
// }
|
||||
|
||||
// Retrieve data from the database
|
||||
$query = Slik::query();
|
||||
|
||||
// Apply search filter if provided
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('sandi_bank', 'LIKE', "%$search%")
|
||||
->orWhere('no_rekening', 'LIKE', "%$search%")
|
||||
->orWhere('cif', 'LIKE', "%$search%")
|
||||
->orWhere('nama_debitur', 'LIKE', "%$search%")
|
||||
->orWhere('nama_cabang', 'LIKE', "%$search%")
|
||||
->orWhere('jenis_agunan', 'LIKE', "%$search%")
|
||||
->orWhere('nama_pemilik_agunan', 'LIKE', "%$search%")
|
||||
->orWhere('alamat_agunan', 'LIKE', "%$search%")
|
||||
->orWhere('lokasi_agunan', 'LIKE', "%$search%");
|
||||
});
|
||||
}
|
||||
|
||||
// Apply year filter
|
||||
if ($request->has('year') && !empty($request->get('year'))) {
|
||||
$query->byYear($request->get('year'));
|
||||
}
|
||||
|
||||
// Apply month filter
|
||||
if ($request->has('month') && !empty($request->get('month'))) {
|
||||
$query->byMonth($request->get('month'));
|
||||
}
|
||||
|
||||
// Apply sandi bank filter
|
||||
if ($request->has('sandi_bank') && !empty($request->get('sandi_bank'))) {
|
||||
$query->where('sandi_bank', $request->get('sandi_bank'));
|
||||
}
|
||||
|
||||
// Apply kolektibilitas filter
|
||||
if ($request->has('kolektibilitas') && !empty($request->get('kolektibilitas'))) {
|
||||
$query->where('kolektibilitas', $request->get('kolektibilitas'));
|
||||
}
|
||||
|
||||
// Apply jenis agunan filter
|
||||
if ($request->has('jenis_agunan') && !empty($request->get('jenis_agunan'))) {
|
||||
$query->where('jenis_agunan', $request->get('jenis_agunan'));
|
||||
}
|
||||
|
||||
// Apply sorting if provided
|
||||
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
|
||||
$order = $request->get('sortOrder');
|
||||
$column = $request->get('sortField', 'created_at');
|
||||
$query->orderBy($column, $order);
|
||||
} else {
|
||||
$query->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
// Get the total count of records
|
||||
$totalRecords = $query->count();
|
||||
|
||||
// Apply pagination if provided
|
||||
if ($request->has('page') && $request->has('size')) {
|
||||
$page = $request->get('page');
|
||||
$size = $request->get('size');
|
||||
$offset = ($page - 1) * $size; // Calculate the offset
|
||||
|
||||
$query->skip($offset)->take($size);
|
||||
}
|
||||
|
||||
// Get the filtered count of records
|
||||
$filteredRecords = $query->count();
|
||||
|
||||
// Get the data for the current page with relationships
|
||||
$data = $query->get();
|
||||
|
||||
// Transform data untuk datatables
|
||||
$transformedData = $data->map(function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'sandi_bank' => $item->sandi_bank,
|
||||
'tahun' => $item->tahun,
|
||||
'bulan' => $item->bulan,
|
||||
'no_rekening' => $item->no_rekening,
|
||||
'cif' => $item->cif,
|
||||
'nama_debitur' => $item->nama_debitur,
|
||||
'kolektibilitas' => $item->kolektibilitas,
|
||||
'kolektibilitas_badge' => $item->kolektibilitas_badge,
|
||||
'fasilitas' => $item->fasilitas,
|
||||
'jenis_agunan' => $item->jenis_agunan,
|
||||
'nama_pemilik_agunan' => $item->nama_pemilik_agunan,
|
||||
'nilai_agunan' => $item->nilai_agunan_formatted,
|
||||
'nilai_agunan_ljk' => $item->nilai_agunan_ljk_formatted,
|
||||
'alamat_agunan' => $item->alamat_agunan,
|
||||
'lokasi_agunan' => $item->lokasi_agunan,
|
||||
'nama_cabang' => $item->nama_cabang,
|
||||
'kode_cabang' => $item->kode_cabang,
|
||||
'created_by' => $item->creator?->name ?? '-',
|
||||
'created_at' => dateFormat($item->created_at, true)
|
||||
];
|
||||
});
|
||||
|
||||
// Calculate the page count
|
||||
$pageCount = ceil($totalRecords / ($request->get('size', 10)));
|
||||
|
||||
// Calculate the current page number
|
||||
$currentPage = $request->get('page', 1);
|
||||
|
||||
// Return the response data as a JSON object
|
||||
return response()->json([
|
||||
'draw' => $request->get('draw'),
|
||||
'recordsTotal' => $totalRecords,
|
||||
'recordsFiltered' => $filteredRecords,
|
||||
'pageCount' => $pageCount,
|
||||
'page' => $currentPage,
|
||||
'totalCount' => $totalRecords,
|
||||
'data' => $transformedData,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import data slik dari Excel dengan optimasi memory dan progress tracking
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function import(Request $request)
|
||||
{
|
||||
Log::info('SlikController: Starting import process with optimizations', [
|
||||
'user_id' => Auth::id(),
|
||||
'request_size' => $request->header('Content-Length'),
|
||||
'has_file' => $request->hasFile('file'),
|
||||
'memory_limit' => ini_get('memory_limit'),
|
||||
'max_execution_time' => ini_get('max_execution_time')
|
||||
]);
|
||||
|
||||
// Validasi file upload dengan logging detail dan error handling komprehensif
|
||||
try {
|
||||
// Cek apakah ada file yang diupload
|
||||
if (!$request->hasFile('file')) {
|
||||
Log::error('SlikController: Tidak ada file yang diupload', [
|
||||
'user_id' => Auth::id(),
|
||||
'files_count' => count($request->allFiles()),
|
||||
'request_data' => $request->all()
|
||||
]);
|
||||
throw ValidationException::withMessages(['file' => 'Tidak ada file yang diupload.']);
|
||||
}
|
||||
|
||||
$file = $request->file('file');
|
||||
|
||||
// Cek apakah file valid
|
||||
if (!$file->isValid()) {
|
||||
$error = $file->getError();
|
||||
$errorMessage = match($error) {
|
||||
UPLOAD_ERR_INI_SIZE => 'File terlalu besar (melebihi upload_max_filesize).',
|
||||
UPLOAD_ERR_FORM_SIZE => 'File terlalu besar (melebihi MAX_FILE_SIZE).',
|
||||
UPLOAD_ERR_PARTIAL => 'File hanya terupload sebagian.',
|
||||
UPLOAD_ERR_NO_FILE => 'Tidak ada file yang diupload.',
|
||||
UPLOAD_ERR_NO_TMP_DIR => 'Direktori temp tidak tersedia.',
|
||||
UPLOAD_ERR_CANT_WRITE => 'Gagal menulis file ke disk.',
|
||||
UPLOAD_ERR_EXTENSION => 'Upload dibatalkan oleh ekstensi PHP.',
|
||||
default => 'Error upload tidak diketahui: ' . $error
|
||||
};
|
||||
|
||||
Log::error('SlikController: File upload tidak valid', [
|
||||
'error' => $error,
|
||||
'error_message' => $errorMessage,
|
||||
'user_id' => Auth::id(),
|
||||
'file_info' => [
|
||||
'name' => $file->getClientOriginalName(),
|
||||
'size' => $file->getSize(),
|
||||
'mime' => $file->getMimeType()
|
||||
]
|
||||
]);
|
||||
throw ValidationException::withMessages(['file' => $errorMessage]);
|
||||
}
|
||||
|
||||
$maxFileSize = config('import.slik.max_file_size', 50) * 1024; // dalam KB
|
||||
$request->validate([
|
||||
'file' => 'required|file|mimes:xlsx,xls|max:' . $maxFileSize
|
||||
]);
|
||||
Log::info('SlikController: Validasi file berhasil');
|
||||
} catch (\Illuminate\Validation\ValidationException $e) {
|
||||
Log::error('SlikController: Validasi file gagal', [
|
||||
'errors' => $e->errors(),
|
||||
'user_id' => Auth::id(),
|
||||
'request_size' => $request->header('Content-Length')
|
||||
]);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
$uploadedFile = $request->file('file');
|
||||
$originalName = $uploadedFile->getClientOriginalName();
|
||||
$fileSize = $uploadedFile->getSize();
|
||||
|
||||
Log::info('SlikController: Memulai import data Slik', [
|
||||
'user_id' => Auth::id(),
|
||||
'filename' => $originalName,
|
||||
'filesize' => $fileSize,
|
||||
'filesize_mb' => round($fileSize / 1024 / 1024, 2),
|
||||
'mime_type' => $uploadedFile->getMimeType(),
|
||||
'extension' => $uploadedFile->getClientOriginalExtension()
|
||||
]);
|
||||
|
||||
// Generate unique import ID
|
||||
$importId = uniqid('slik_import_');
|
||||
$userId = Auth::id() ?? 1;
|
||||
|
||||
// Cek apakah menggunakan queue processing untuk file besar
|
||||
$useQueue = config('import.slik.queue.enabled', false) && $fileSize > (5 * 1024 * 1024); // > 5MB
|
||||
|
||||
// Pastikan direktori temp ada
|
||||
$tempDir = storage_path('app/temp');
|
||||
if (!file_exists($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
Log::info('SlikController: Direktori temp dibuat', ['path' => $tempDir]);
|
||||
}
|
||||
|
||||
// Simpan file sementara dengan nama unik
|
||||
$tempFileName = 'slik_import_' . time() . '_' . uniqid() . '.' . $uploadedFile->getClientOriginalExtension();
|
||||
$tempFilePath = $tempDir . '/' . $tempFileName;
|
||||
|
||||
Log::info('SlikController: Memindahkan file ke temp', [
|
||||
'temp_path' => $tempFilePath,
|
||||
'use_queue' => $useQueue
|
||||
]);
|
||||
|
||||
// Pindahkan file ke direktori temp
|
||||
$uploadedFile->move($tempDir, $tempFilePath);
|
||||
|
||||
// Verifikasi file berhasil dipindahkan
|
||||
if (!file_exists($tempFilePath)) {
|
||||
throw new Exception('File gagal dipindahkan ke direktori temp');
|
||||
}
|
||||
|
||||
Log::info('SlikController: File berhasil dipindahkan', [
|
||||
'file_size' => filesize($tempFilePath)
|
||||
]);
|
||||
|
||||
if ($useQueue) {
|
||||
Log::info('SlikController: Menggunakan queue processing untuk file besar', [
|
||||
'import_id' => $importId,
|
||||
'file_size_mb' => round($fileSize / 1024 / 1024, 2)
|
||||
]);
|
||||
|
||||
// Dispatch job ke queue
|
||||
\Modules\Lpj\Jobs\ProcessSlikImport::dispatch($tempFilePath, $userId, $importId);
|
||||
|
||||
return redirect()->back()->with('success', 'Import sedang diproses di background. ID: ' . $importId);
|
||||
}
|
||||
|
||||
// Import langsung untuk file kecil
|
||||
Log::info('SlikController: Processing file directly', [
|
||||
'import_id' => $importId,
|
||||
'file_size_mb' => round($fileSize / 1024 / 1024, 2)
|
||||
]);
|
||||
|
||||
// Set optimasi memory untuk import langsung
|
||||
$memoryLimit = config('import.slik.memory_limit', 256);
|
||||
ini_set('memory_limit', $memoryLimit . 'M');
|
||||
ini_set('max_execution_time', config('import.slik.timeout', 30000));
|
||||
|
||||
// Enable garbage collection jika diizinkan
|
||||
if (config('import.slik.enable_gc', true)) {
|
||||
gc_enable();
|
||||
}
|
||||
|
||||
// Proses import menggunakan SlikImport class
|
||||
Log::info('SlikController: Memulai proses Excel import');
|
||||
$import = new SlikImport();
|
||||
Excel::import($import, $tempFilePath);
|
||||
Log::info('SlikController: Excel import selesai');
|
||||
|
||||
// Force garbage collection setelah selesai
|
||||
if (config('import.slik.enable_gc', true)) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
// Hapus file temporary setelah import
|
||||
if (file_exists($tempFilePath)) {
|
||||
unlink($tempFilePath);
|
||||
Log::info('SlikController: File temp berhasil dihapus');
|
||||
}
|
||||
|
||||
Log::info('SlikController: Data Slik berhasil diimport', [
|
||||
'user_id' => Auth::id(),
|
||||
'import_id' => $importId
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', 'Data Slik berhasil diimport dari file Excel.');
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Hapus file temporary jika ada error
|
||||
if (isset($tempFilePath) && file_exists($tempFilePath)) {
|
||||
unlink($tempFilePath);
|
||||
Log::info('SlikController: File temp dihapus karena error');
|
||||
}
|
||||
|
||||
Log::error('SlikController: Gagal import data Slik', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'user_id' => Auth::id(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'memory_usage' => memory_get_usage(true)
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('error', 'Gagal import data Slik: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan halaman form import
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function importForm()
|
||||
{
|
||||
return view('lpj::slik.import');
|
||||
}
|
||||
|
||||
/**
|
||||
* Download template Excel untuk import
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function downloadTemplate()
|
||||
{
|
||||
$templatePath = resource_path('metronic/slik.xlsx');
|
||||
|
||||
if (!file_exists($templatePath)) {
|
||||
return redirect()->back()->with('error', 'Template file tidak ditemukan.');
|
||||
}
|
||||
|
||||
return response()->download($templatePath, 'template_slik.xlsx');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get import progress
|
||||
*
|
||||
* @param string $importId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function progress(string $importId)
|
||||
{
|
||||
try {
|
||||
$progressService = new \Modules\Lpj\Services\ImportProgressService();
|
||||
$progress = $progressService->getProgress($importId);
|
||||
|
||||
if (!$progress) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Progress import tidak ditemukan'
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'progress' => $progress
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('SlikController: Error getting progress', [
|
||||
'import_id' => $importId,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal mendapatkan progress: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data SLIK ke Excel
|
||||
*
|
||||
* Method ini menangani export data SLIK ke format Excel dengan:
|
||||
* - Logging aktivitas export
|
||||
* - Error handling yang proper
|
||||
* - Format Excel yang sesuai dengan struktur SLIK
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
try {
|
||||
Log::info('SLIK Export: Memulai proses export data SLIK', [
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now(),
|
||||
'memory_usage' => memory_get_usage(true) / 1024 / 1024 . ' MB'
|
||||
]);
|
||||
|
||||
// Hitung total data yang akan di-export
|
||||
$totalData = Slik::count();
|
||||
|
||||
Log::info('SLIK Export: Informasi data export', [
|
||||
'total_records' => $totalData,
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now()
|
||||
]);
|
||||
|
||||
// Generate nama file dengan timestamp
|
||||
$filename = 'slik_export_' . date('Y-m-d_H-i-s') . '.xlsx';
|
||||
|
||||
// Proses export menggunakan SlikExport class
|
||||
$export = Excel::download(new SlikExport(), $filename);
|
||||
|
||||
Log::info('SLIK Export: Berhasil generate file export', [
|
||||
'filename' => $filename,
|
||||
'total_records' => $totalData,
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now(),
|
||||
'memory_peak' => memory_get_peak_usage(true) / 1024 / 1024 . ' MB'
|
||||
]);
|
||||
|
||||
return $export;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('SLIK Export: Gagal melakukan export data SLIK', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now(),
|
||||
'memory_usage' => memory_get_usage(true) / 1024 / 1024 . ' MB'
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal melakukan export data SLIK: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate all SLIK data
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function truncate()
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
Log::info('SLIK Truncate: Memulai proses truncate data SLIK', [
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now()
|
||||
]);
|
||||
|
||||
// Truncate tabel SLIK
|
||||
Slik::truncate();
|
||||
|
||||
DB::commit();
|
||||
|
||||
Log::info('SLIK Truncate: Berhasil menghapus semua data SLIK', [
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Semua data SLIK berhasil dihapus'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
|
||||
Log::error('SLIK Truncate: Gagal menghapus data SLIK', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'user_id' => Auth::id(),
|
||||
'timestamp' => now()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal menghapus data SLIK: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +345,19 @@ use Illuminate\Support\Facades\Auth;
|
||||
$content = $pdf->download()->getOriginalContent();
|
||||
Storage::put('public/'.$newFileNameWithPath,$content);
|
||||
|
||||
$permohonanModel = Permohonan::where('nomor_registrasi', $penawaran->nomor_registrasi)->first();
|
||||
if ($permohonanModel) {
|
||||
$permohonanModel->status = 'registrasi-final';
|
||||
$permohonanModel->save();
|
||||
}
|
||||
|
||||
$persetujuanPenawaran = PenawaranTender::where('id', $penawaran->id)->first();
|
||||
if ($persetujuanPenawaran) {
|
||||
$persetujuanPenawaran->status = 'registrasi-final';
|
||||
$persetujuanPenawaran->save();
|
||||
}
|
||||
|
||||
|
||||
$data1['status'] = 'success';
|
||||
$data1['spkpenawaran_path'] = $spkpenawaran_path;
|
||||
$data1['message']['message_success'] = array('Generate SPK PDF successfully');
|
||||
|
||||
@@ -3,25 +3,21 @@
|
||||
namespace Modules\Lpj\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Modules\Lpj\Exports\BasicDataSurveyorExport;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Http\Response;
|
||||
use Exception;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Lpj\Models\LaporanExternal;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\Branch;
|
||||
use Modules\Lpj\Models\Surveyor;
|
||||
use Modules\Lpj\Models\BentukTanah;
|
||||
use Modules\Lpj\Models\KonturTanah;
|
||||
use Modules\Location\Models\Province;
|
||||
@@ -41,17 +37,8 @@ use Modules\Lpj\Models\SpekBangunan;
|
||||
use Modules\Lpj\Models\SpekKategoritBangunan;
|
||||
use Modules\Lpj\Models\SaranaPelengkap;
|
||||
use Modules\Lpj\Models\ArahMataAngin;
|
||||
use Modules\Lpj\Models\Analisa;
|
||||
use Modules\Lpj\Models\Penilaian;
|
||||
use Modules\Lpj\Models\PerkerasanJalan;
|
||||
use Modules\Lpj\Models\AnalisaFakta;
|
||||
use Modules\Lpj\Models\AnalisaLingkungan;
|
||||
use Modules\Lpj\Models\AnalisaTanahBagunan;
|
||||
use Modules\Lpj\Models\SpekBangunanAnalisa;
|
||||
use Modules\Lpj\Models\Denah;
|
||||
use Modules\Lpj\Models\FotoJaminan;
|
||||
use Modules\Lpj\Models\Lingkungan;
|
||||
use Modules\Lpj\Models\LantaiUnit;
|
||||
use Modules\Lpj\Models\Teams;
|
||||
use Modules\Lpj\Models\Lantai;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
@@ -62,29 +49,24 @@ use Modules\Lpj\Models\PosisiUnit;
|
||||
use Modules\Lpj\Models\TerletakArea;
|
||||
use Modules\Lpj\Models\FasilitasObjek;
|
||||
use Modules\Lpj\Models\MerupakanDaerah;
|
||||
use Modules\Lpj\Models\ObjekJaminan;
|
||||
use Modules\Lpj\Models\ModelAlatBerat;
|
||||
use Modules\Lpj\Models\JenisPesawat;
|
||||
use Modules\Lpj\Models\DokumenJaminan;
|
||||
use Modules\Lpj\Models\DetailDokumenJaminan;
|
||||
use Modules\Lpj\Models\JenisKapal;
|
||||
use Modules\Lpj\Models\JenisKendaraan;
|
||||
use Modules\Lpj\Models\RuteJaminan;
|
||||
use Modules\Lpj\Models\HubunganPemilikJaminan;
|
||||
use Modules\Lpj\Models\HubunganPenghuniJaminan;
|
||||
use Modules\Lpj\Models\AnalisaUnit;
|
||||
use Modules\Lpj\Models\GolonganMasySekitar;
|
||||
use Modules\Lpj\Models\TingkatKeramaian;
|
||||
use Modules\Lpj\Models\TujuanPenilaian;
|
||||
use Modules\Lpj\Models\LaluLintasLokasi;
|
||||
use Modules\Lpj\Models\SpekBagunanAnalisaDetail;
|
||||
use Modules\Lpj\Http\Requests\SurveyorRequest;
|
||||
use Modules\Lpj\Http\Requests\FormSurveyorRequest;
|
||||
use Modules\Lpj\Jobs\SendJadwalKunjunganEmailJob;
|
||||
use App\Helpers\Lpj;
|
||||
use Modules\Lpj\Models\Authorization;
|
||||
use Modules\Lpj\Services\SurveyorValidateService;
|
||||
use Modules\Lpj\Services\SaveFormInspesksiService;
|
||||
use Modules\Lpj\Models\PermohonanHistory;
|
||||
|
||||
class SurveyorController extends Controller
|
||||
{
|
||||
@@ -126,6 +108,9 @@ class SurveyorController extends Controller
|
||||
$provinces = Province::all();
|
||||
$bentukTanah = BentukTanah::all();
|
||||
|
||||
// Jalankan cleanup inspeksi otomatis untuk permohonan ini
|
||||
$this->cleanupInspeksiData($id);
|
||||
|
||||
// Get all inspeksi data for this permohonan
|
||||
if (strtolower($permohonan->tujuanPenilaian->name) == 'rap') {
|
||||
$inspeksiData = Inspeksi::where('permohonan_id', $id)
|
||||
@@ -151,13 +136,17 @@ class SurveyorController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
$catatan_revisi_survey = PermohonanHistory::where('permohonan_id', $id)
|
||||
->where('status', 'revisi-survey')->latest()->first();
|
||||
|
||||
return view('lpj::surveyor.detail', compact(
|
||||
'permohonan',
|
||||
'surveyor',
|
||||
'branches',
|
||||
'provinces',
|
||||
'bentukTanah',
|
||||
'inspeksiData'
|
||||
'inspeksiData',
|
||||
'catatan_revisi_survey',
|
||||
));
|
||||
}
|
||||
|
||||
@@ -253,7 +242,7 @@ class SurveyorController extends Controller
|
||||
], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Denah Store Error: ' . $e->getMessage());
|
||||
Log::error('Denah Store Error: ' . $e->getMessage());
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Gagal menyimpan data: ' . $e->getMessage()
|
||||
@@ -794,12 +783,12 @@ class SurveyorController extends Controller
|
||||
]);
|
||||
|
||||
|
||||
if ($permohonan->jenisPenilaian->name == "External") {
|
||||
if (in_array(strtolower($permohonan->jenisPenilaian->name), ['external', 'eksternal'])) {
|
||||
LaporanExternal::updateOrCreate(
|
||||
['permohonan_id' => $permohonan->id],
|
||||
[
|
||||
'nomor_laporan' => $permohonan->nomor_registrasi,
|
||||
'tanggal_laporan' => now(),
|
||||
'tgl_final_laporan' => now(),
|
||||
'created_by' => Auth::id(),
|
||||
]
|
||||
);
|
||||
@@ -1079,7 +1068,7 @@ class SurveyorController extends Controller
|
||||
return null;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('File upload error: ' . $e->getMessage());
|
||||
Log::error('File upload error: ' . $e->getMessage());
|
||||
throw new \Exception("Gagal mengupload file: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -1396,6 +1385,11 @@ class SurveyorController extends Controller
|
||||
}
|
||||
public function storeDataPembanding(Request $request)
|
||||
{
|
||||
$request->merge([
|
||||
'kordinat_lat' => $request->get('kordinat_lat') ? str_replace(',', '', $request->get('kordinat_lat')) : null,
|
||||
'kordinat_lng' => $request->get('kordinat_lng') ? str_replace(',', '', $request->get('kordinat_lng')) : null,
|
||||
]);
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
@@ -1847,10 +1841,10 @@ class SurveyorController extends Controller
|
||||
return redirect()
|
||||
->route('basicdata.' . $type . '.index')
|
||||
->with('success', 'created successfully');
|
||||
} catch (Exeception $e) {
|
||||
} catch (\Exception $e) {
|
||||
return redirect()
|
||||
->route('basicdata.' . $type . '.index')
|
||||
->with('error', $th->getMessage());
|
||||
->with('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2116,7 +2110,7 @@ class SurveyorController extends Controller
|
||||
|
||||
public function dataForDatatablesData(Request $request, $type)
|
||||
{
|
||||
if (is_null(auth()->user()) || !$this->user->can('jenis_aset.view')) {
|
||||
if (is_null(Auth::user()) || !$this->user->can('jenis_aset.view')) {
|
||||
//abort(403, 'Sorry! You are not allowed to view users.');
|
||||
}
|
||||
|
||||
@@ -2156,7 +2150,7 @@ class SurveyorController extends Controller
|
||||
if (array_key_exists($type, $models)) {
|
||||
$query = $models[$type]::query();
|
||||
} else {
|
||||
throw new InvalidArgumentException("Invalid type: $type");
|
||||
throw new \InvalidArgumentException("Invalid type: $type");
|
||||
}
|
||||
|
||||
if ($request->has('search') && !empty($request->get('search'))) {
|
||||
@@ -2229,9 +2223,9 @@ class SurveyorController extends Controller
|
||||
$model = $modelClass::findOrFail($id);
|
||||
$model->delete();
|
||||
return response()->json(['success' => true, 'message' => 'deleted successfully']);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
|
||||
return response()->json(['success' => false, 'message' => 'not found.'], 404);
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => 'Failed to delete.'], 500);
|
||||
}
|
||||
}
|
||||
@@ -2387,14 +2381,14 @@ class SurveyorController extends Controller
|
||||
$path = $file->storeAs("public/surveyor/{$request->type}", $fileName);
|
||||
|
||||
if ($path === false) {
|
||||
throw new Exception("Failed to store file for {$fileKey}");
|
||||
throw new \Exception("Failed to store file for {$fileKey}");
|
||||
}
|
||||
if (isset($data[$fileKey]) && $data[$fileKey]) {
|
||||
$this->deleteFile($data[$fileKey]);
|
||||
}
|
||||
return str_replace('public/', '', $path);
|
||||
} else {
|
||||
throw new Exception("Invalid file upload for {$fileKey}");
|
||||
throw new \Exception("Invalid file upload for {$fileKey}");
|
||||
}
|
||||
} elseif (isset($data[$fileKey]) && $data[$fileKey]) {
|
||||
return $data[$fileKey];
|
||||
@@ -2423,14 +2417,14 @@ class SurveyorController extends Controller
|
||||
public function uploadFile($file, $type)
|
||||
{
|
||||
if (!$file->isValid()) {
|
||||
throw new Exception("Invalid file upload for {$type}");
|
||||
throw new \Exception("Invalid file upload for {$type}");
|
||||
}
|
||||
|
||||
$fileName = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs("public/surveyor/{$type}", $fileName);
|
||||
|
||||
if ($path === false) {
|
||||
throw new Exception("Failed to store file for {$type}");
|
||||
throw new \Exception("Failed to store file for {$type}");
|
||||
}
|
||||
|
||||
return str_replace('public/', '', $path);
|
||||
@@ -2880,9 +2874,10 @@ class SurveyorController extends Controller
|
||||
$inspeksi->data_form = json_encode($existingData);
|
||||
$inspeksi->save();
|
||||
} else {
|
||||
Inspeksi::create([
|
||||
Inspeksi::updateOrCreate([
|
||||
'permohonan_id' => $request->input('permohonan_id'),
|
||||
'dokument_id' => $request->input('document_id'),
|
||||
'dokument_id' => $request->input('document_id')
|
||||
],[
|
||||
'data_form' => json_encode($existingData),
|
||||
]);
|
||||
}
|
||||
@@ -2903,5 +2898,99 @@ class SurveyorController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fungsi untuk cleanup data inspeksi otomatis
|
||||
* Menghapus data inspeksi tanpa dokument_id jika ada data lain dengan dokument_id yang sama
|
||||
*/
|
||||
private function cleanupInspeksiData($permohonanId)
|
||||
{
|
||||
try {
|
||||
Log::info('SurveyorController: Memulai cleanup inspeksi otomatis', [
|
||||
'permohonan_id' => $permohonanId,
|
||||
'user_id' => Auth::id()
|
||||
]);
|
||||
|
||||
// Ambil data inspeksi yang memiliki dokument_id (data lengkap)
|
||||
$dataWithDokument = Inspeksi::where('permohonan_id', $permohonanId)
|
||||
->whereNotNull('dokument_id')
|
||||
->whereNull('deleted_at')
|
||||
->get();
|
||||
|
||||
// Ambil data inspeksi yang tidak memiliki dokument_id (data yang akan di-cleanup)
|
||||
$dataWithoutDokument = Inspeksi::where('permohonan_id', $permohonanId)
|
||||
->whereNull('dokument_id')
|
||||
->whereNull('deleted_at')
|
||||
->get();
|
||||
|
||||
// Jika ada data tanpa dokument_id, cek apakah ada data dengan dokument_id yang sama
|
||||
if ($dataWithoutDokument->isNotEmpty() && $dataWithDokument->isNotEmpty()) {
|
||||
|
||||
// Group data dengan dokument_id by created_by
|
||||
$groupedDataWithDokument = $dataWithDokument->groupBy('created_by');
|
||||
|
||||
// Group data tanpa dokument_id by created_by
|
||||
$groupedDataWithoutDokument = $dataWithoutDokument->groupBy('created_by');
|
||||
|
||||
// Proses cleanup untuk setiap user
|
||||
foreach ($groupedDataWithDokument as $userId => $userDataWithDokument) {
|
||||
|
||||
// Cek apakah user ini juga memiliki data tanpa dokument_id
|
||||
if (isset($groupedDataWithoutDokument[$userId])) {
|
||||
|
||||
// Ambil salah satu data dengan dokument_id sebagai referensi untuk logging
|
||||
$referenceData = $userDataWithDokument->first();
|
||||
|
||||
Log::info('SurveyorController: Menemukan data lengkap untuk user, akan menghapus data tidak lengkap', [
|
||||
'user_id' => $userId,
|
||||
'permohonan_id' => $permohonanId,
|
||||
'reference_dokument_id' => $referenceData->dokument_id,
|
||||
'data_count_to_delete' => $groupedDataWithoutDokument[$userId]->count()
|
||||
]);
|
||||
|
||||
// Ambil semua data tanpa dokument_id untuk user ini
|
||||
$userDataWithoutDokument = $groupedDataWithoutDokument[$userId];
|
||||
|
||||
// Soft delete data tanpa dokument_id
|
||||
foreach ($userDataWithoutDokument as $dataToDelete) {
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Soft delete data
|
||||
$dataToDelete->delete();
|
||||
|
||||
Log::info('SurveyorController: Data inspeksi berhasil di-soft delete', [
|
||||
'id' => $dataToDelete->id,
|
||||
'permohonan_id' => $dataToDelete->permohonan_id,
|
||||
'dokument_id' => $dataToDelete->dokument_id,
|
||||
'created_by' => $dataToDelete->created_by,
|
||||
'deleted_at' => now()
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
Log::error('SurveyorController: Gagal menghapus data inspeksi', [
|
||||
'id' => $dataToDelete->id,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('SurveyorController: Cleanup inspeksi otomatis selesai', [
|
||||
'permohonan_id' => $permohonanId,
|
||||
'data_with_dokument' => $dataWithDokument->count(),
|
||||
'data_without_dokument' => $dataWithoutDokument->count()
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('SurveyorController: Error saat cleanup inspeksi otomatis', [
|
||||
'permohonan_id' => $permohonanId,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|max:255',
|
||||
'biaya' => 'nullable|numeric|min:0',
|
||||
];
|
||||
|
||||
if ($this->method() == 'PUT') {
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
private function getBasicInfoRules()
|
||||
{
|
||||
return [
|
||||
'permohonan_id' => 'required|exists:permohonan,id',
|
||||
'permohonan_id' => 'nullable|exists:permohonan,id',
|
||||
'persetujuan_penawaran_id' => 'required|exists:persetujuan_penawaran,id',
|
||||
'status' => 'nullable|boolean',
|
||||
'created_by' => 'nullable|exists:users,id',
|
||||
@@ -130,7 +130,6 @@
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'permohonan_id.required' => 'ID Permohonan harus diisi',
|
||||
'permohonan_id.exists' => 'ID Permohonan tidak valid',
|
||||
'persetujuan_penawaran_id.required' => 'ID Persetujuan Penawaran harus diisi',
|
||||
'persetujuan_penawaran_id.exists' => 'ID Persetujuan Penawaran tidak valid',
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
),
|
||||
'tanggal_permohonan' => date('Y-m-d'),
|
||||
'user_id' => auth()->user()->id,
|
||||
'branch_id' => auth()->user()->branch_id,
|
||||
'status' => 'order'
|
||||
]);
|
||||
}
|
||||
|
||||
114
app/Http/Requests/ReferensiLinkRequest.php
Normal file
114
app/Http/Requests/ReferensiLinkRequest.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ReferensiLinkRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|string|max:255',
|
||||
'link' => 'required|string|max:500',
|
||||
'kategori' => 'nullable|string|max:100',
|
||||
'deskripsi' => 'nullable|string|max:2000',
|
||||
'is_active' => 'boolean',
|
||||
'urutan' => 'nullable|integer|min:0',
|
||||
];
|
||||
|
||||
// Validasi tambahan untuk link
|
||||
$rules['link'] .= '|url';
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom messages for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'name.required' => 'Nama referensi link wajib diisi',
|
||||
'name.string' => 'Nama referensi link harus berupa teks',
|
||||
'name.max' => 'Nama referensi link maksimal 255 karakter',
|
||||
'link.required' => 'Link wajib diisi',
|
||||
'link.string' => 'Link harus berupa teks',
|
||||
'link.max' => 'Link maksimal 500 karakter',
|
||||
'link.url' => 'Link harus berupa URL yang valid',
|
||||
'kategori.string' => 'Kategori harus berupa teks',
|
||||
'kategori.max' => 'Kategori maksimal 100 karakter',
|
||||
'deskripsi.string' => 'Deskripsi harus berupa teks',
|
||||
'deskripsi.max' => 'Deskripsi maksimal 2000 karakter',
|
||||
'is_active.boolean' => 'Status aktif harus berupa ya/tidak',
|
||||
'urutan.integer' => 'Urutan harus berupa angka',
|
||||
'urutan.min' => 'Urutan minimal 0',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'Nama Referensi Link',
|
||||
'link' => 'Link',
|
||||
'kategori' => 'Kategori',
|
||||
'deskripsi' => 'Deskripsi',
|
||||
'is_active' => 'Status Aktif',
|
||||
'urutan' => 'Urutan',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
// Format link jika belum memiliki protocol
|
||||
if ($this->has('link')) {
|
||||
$link = $this->input('link');
|
||||
if ($link && !preg_match('/^(https?:\/\/)/i', $link)) {
|
||||
$this->merge([
|
||||
'link' => 'https://' . $link
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set default is_active jika tidak diset
|
||||
if (!$this->has('is_active')) {
|
||||
$this->merge([
|
||||
'is_active' => true
|
||||
]);
|
||||
}
|
||||
|
||||
// Set default urutan jika tidak diset atau 0
|
||||
if (!$this->has('urutan') || empty($this->input('urutan'))) {
|
||||
$this->merge([
|
||||
'urutan' => 0
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
166
app/Imports/ReferensiLinkImport.php
Normal file
166
app/Imports/ReferensiLinkImport.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Imports;
|
||||
|
||||
use Modules\Lpj\app\Models\ReferensiLink;
|
||||
use Maatwebsite\Excel\Concerns\ToModel;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithValidation;
|
||||
use Maatwebsite\Excel\Concerns\Importable;
|
||||
use Maatwebsite\Excel\Concerns\SkipsFailures;
|
||||
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
|
||||
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||
use Maatwebsite\Excel\Concerns\WithChunkReading;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ReferensiLinkImport implements ToModel, WithHeadingRow, WithValidation, SkipsOnFailure, WithBatchInserts, WithChunkReading
|
||||
{
|
||||
use Importable, SkipsFailures;
|
||||
|
||||
protected $importedCount = 0;
|
||||
protected $failedCount = 0;
|
||||
protected $errors = [];
|
||||
|
||||
/**
|
||||
* Convert row to model
|
||||
*/
|
||||
public function model(array $row)
|
||||
{
|
||||
try {
|
||||
$this->importedCount++;
|
||||
|
||||
return new ReferensiLink([
|
||||
'name' => $row['nama'] ?? $row['name'],
|
||||
'link' => $this->formatLink($row['link'] ?? $row['url'] ?? ''),
|
||||
'kategori' => $row['kategori'] ?? $row['category'] ?? 'lainnya',
|
||||
'deskripsi' => $row['deskripsi'] ?? $row['description'] ?? null,
|
||||
'is_active' => $this->parseStatus($row['status_aktif'] ?? $row['is_active'] ?? 'aktif'),
|
||||
'urutan' => $this->parseUrutan($row['urutan'] ?? $row['order'] ?? 0),
|
||||
'created_by' => Auth::id(),
|
||||
'updated_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->failedCount++;
|
||||
$this->errors[] = "Baris {$row['row_number']}: " . $e->getMessage();
|
||||
Log::error('Import ReferensiLink Error: ' . $e->getMessage(), ['row' => $row]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation rules
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:255',
|
||||
'link' => 'required|string|max:500',
|
||||
'url' => 'required|string|max:500',
|
||||
'kategori' => 'nullable|string|max:100',
|
||||
'category' => 'nullable|string|max:100',
|
||||
'deskripsi' => 'nullable|string|max:2000',
|
||||
'description' => 'nullable|string|max:2000',
|
||||
'status_aktif' => 'nullable|string|in:aktif,tidak aktif,1,0,true,false',
|
||||
'is_active' => 'nullable|string|in:aktif,tidak aktif,1,0,true,false',
|
||||
'urutan' => 'nullable|integer|min:0',
|
||||
'order' => 'nullable|integer|min:0',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation messages
|
||||
*/
|
||||
public function customValidationMessages()
|
||||
{
|
||||
return [
|
||||
'nama.required' => 'Nama wajib diisi',
|
||||
'name.required' => 'Nama wajib diisi',
|
||||
'link.required' => 'Link wajib diisi',
|
||||
'url.required' => 'Link wajib diisi',
|
||||
'link.url' => 'Link harus berupa URL yang valid',
|
||||
'url.url' => 'Link harus berupa URL yang valid',
|
||||
'kategori.max' => 'Kategori maksimal 100 karakter',
|
||||
'category.max' => 'Kategori maksimal 100 karakter',
|
||||
'deskripsi.max' => 'Deskripsi maksimal 2000 karakter',
|
||||
'description.max' => 'Deskripsi maksimal 2000 karakter',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch size for inserts
|
||||
*/
|
||||
public function batchSize(): int
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk size for reading
|
||||
*/
|
||||
public function chunkSize(): int
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format link to ensure it has protocol
|
||||
*/
|
||||
private function formatLink($link)
|
||||
{
|
||||
if (empty($link)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove any whitespace
|
||||
$link = trim($link);
|
||||
|
||||
// Add protocol if not present
|
||||
if (!preg_match('/^(https?:\/\/)/i', $link)) {
|
||||
$link = 'https://' . $link;
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse status value
|
||||
*/
|
||||
private function parseStatus($status)
|
||||
{
|
||||
if (empty($status)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$status = strtolower(trim($status));
|
||||
|
||||
return in_array($status, ['aktif', '1', 'true', 'yes', 'ya']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse order value
|
||||
*/
|
||||
private function parseUrutan($urutan)
|
||||
{
|
||||
if (empty($urutan)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) $urutan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get import statistics
|
||||
*/
|
||||
public function getImportStats(): array
|
||||
{
|
||||
return [
|
||||
'imported' => $this->importedCount,
|
||||
'failed' => $this->failedCount,
|
||||
'errors' => $this->errors,
|
||||
'success' => $this->importedCount - $this->failedCount,
|
||||
];
|
||||
}
|
||||
}
|
||||
415
app/Imports/SlikImport.php
Normal file
415
app/Imports/SlikImport.php
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Imports;
|
||||
|
||||
use Modules\Lpj\Models\Slik;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithStartRow;
|
||||
use Maatwebsite\Excel\Concerns\WithChunkReading;
|
||||
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
|
||||
|
||||
|
||||
/**
|
||||
* Class SlikImport
|
||||
*
|
||||
* Handle import data Excel untuk modul Slik
|
||||
* Menggunakan Laravel Excel package untuk membaca file Excel
|
||||
* dengan optimasi memory dan chunk processing
|
||||
*
|
||||
* @package Modules\Lpj\app\Imports
|
||||
*/
|
||||
class SlikImport implements ToCollection, WithStartRow, WithBatchInserts, WithChunkReading, WithCustomCsvSettings
|
||||
{
|
||||
/**
|
||||
* Mulai membaca dari baris ke-5 (skip header)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function startRow(): int
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch size untuk insert data dari konfigurasi
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function batchSize(): int
|
||||
{
|
||||
return config('import.slik.batch_size', 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk size untuk membaca file dari konfigurasi
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function chunkSize(): int
|
||||
{
|
||||
return config('import.slik.chunk_size', 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom CSV settings untuk optimasi pembacaan file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCsvSettings(): array
|
||||
{
|
||||
return [
|
||||
'input_encoding' => 'UTF-8',
|
||||
'delimiter' => ',',
|
||||
'enclosure' => '"',
|
||||
'escape_character' => '\\',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process collection data dari Excel dengan optimasi memory
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @return void
|
||||
*/
|
||||
public function collection(Collection $collection)
|
||||
{
|
||||
// Set memory limit dari konfigurasi
|
||||
$memoryLimit = config('import.slik.memory_limit', 1024);
|
||||
$currentMemoryLimit = ini_get('memory_limit');
|
||||
|
||||
if ($currentMemoryLimit !== '-1' && $this->convertToBytes($currentMemoryLimit) < $memoryLimit * 1024 * 1024) {
|
||||
ini_set('memory_limit', $memoryLimit . 'M');
|
||||
}
|
||||
|
||||
// Set timeout handler
|
||||
$timeout = config('import.slik.timeout', 1800);
|
||||
set_time_limit($timeout);
|
||||
|
||||
// Force garbage collection sebelum memulai
|
||||
if (config('import.slik.enable_gc', true)) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
Log::info('SlikImport: Memulai import data', [
|
||||
'total_rows' => $collection->count(),
|
||||
'memory_usage' => memory_get_usage(true),
|
||||
'memory_peak' => memory_get_peak_usage(true),
|
||||
'memory_limit' => ini_get('memory_limit'),
|
||||
'php_version' => PHP_VERSION,
|
||||
'memory_limit_before' => $currentMemoryLimit,
|
||||
'config' => [
|
||||
'memory_limit' => $memoryLimit,
|
||||
'chunk_size' => $this->chunkSize(),
|
||||
'batch_size' => $this->batchSize()
|
||||
]
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$processedRows = 0;
|
||||
$skippedRows = 0;
|
||||
$errorRows = 0;
|
||||
$totalRows = $collection->count();
|
||||
|
||||
foreach ($collection as $index => $row) {
|
||||
// Log progress setiap 25 baris untuk chunk lebih kecil
|
||||
if ($index % 25 === 0) {
|
||||
Log::info('SlikImport: Processing chunk', [
|
||||
'current_row' => $index + 5,
|
||||
'progress' => round(($index / max($totalRows, 1)) * 100, 2) . '%',
|
||||
'processed' => $processedRows,
|
||||
'skipped' => $skippedRows,
|
||||
'errors' => $errorRows,
|
||||
'memory_usage' => memory_get_usage(true),
|
||||
'memory_peak' => memory_get_peak_usage(true),
|
||||
'memory_diff' => memory_get_peak_usage(true) - memory_get_usage(true)
|
||||
]);
|
||||
}
|
||||
|
||||
// Skip baris kosong
|
||||
if ($this->isEmptyRow($row)) {
|
||||
$skippedRows++;
|
||||
Log::debug('SlikImport: Skipping empty row', ['row_number' => $index + 5]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validasi data
|
||||
if (!$this->validateRow($row)) {
|
||||
$errorRows++;
|
||||
Log::warning('SlikImport: Invalid row data', [
|
||||
'row_number' => $index + 5,
|
||||
'data' => $row->toArray()
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Map data dari Excel ke model
|
||||
$slikData = $this->mapRowToSlik($row);
|
||||
|
||||
// Update atau create berdasarkan no_rekening dan cif
|
||||
$slik = Slik::updateOrCreate(
|
||||
[
|
||||
'no_rekening' => $slikData['no_rekening'],
|
||||
'cif' => $slikData['cif']
|
||||
],
|
||||
$slikData
|
||||
);
|
||||
|
||||
$processedRows++;
|
||||
|
||||
// Log detail untuk baris pertama sebagai sample
|
||||
if ($index === 0) {
|
||||
Log::info('SlikImport: Sample data processed', [
|
||||
'slik_id' => $slik->id,
|
||||
'no_rekening' => $slik->no_rekening,
|
||||
'cif' => $slik->cif,
|
||||
'was_recently_created' => $slik->wasRecentlyCreated
|
||||
]);
|
||||
}
|
||||
|
||||
// Force garbage collection setiap 25 baris untuk mengurangi memory
|
||||
if (config('import.slik.enable_gc', true) && $index > 0 && $index % 25 === 0) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
// Unset data yang sudah tidak digunakan untuk mengurangi memory
|
||||
if ($index > 0 && $index % 25 === 0) {
|
||||
unset($slikData, $slik);
|
||||
}
|
||||
|
||||
// Reset collection internal untuk mengurangi memory
|
||||
if ($index > 0 && $index % 100 === 0) {
|
||||
$collection = collect($collection->slice($index + 1)->values());
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$errorRows++;
|
||||
Log::error('SlikImport: Error processing row', [
|
||||
'row_number' => $index + 5,
|
||||
'error' => $e->getMessage(),
|
||||
'data' => $row->toArray(),
|
||||
'memory_usage' => memory_get_usage(true)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
// Force garbage collection setelah selesai
|
||||
if (config('import.slik.enable_gc', true)) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
// Cleanup variables
|
||||
unset($collection);
|
||||
|
||||
Log::info('SlikImport: Import berhasil diselesaikan', [
|
||||
'total_rows' => $totalRows,
|
||||
'processed_rows' => $processedRows,
|
||||
'skipped_rows' => $skippedRows,
|
||||
'error_rows' => $errorRows,
|
||||
'final_memory_usage' => memory_get_usage(true),
|
||||
'peak_memory_usage' => memory_get_peak_usage(true),
|
||||
'memory_saved' => memory_get_peak_usage(true) - memory_get_usage(true),
|
||||
'memory_efficiency' => ($processedRows > 0) ? round(memory_get_peak_usage(true) / $processedRows, 2) : 0
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
// Force garbage collection jika error
|
||||
if (config('import.slik.enable_gc', true)) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
$errorType = 'general';
|
||||
if (str_contains(strtolower($e->getMessage()), 'memory')) {
|
||||
$errorType = 'memory';
|
||||
} elseif (str_contains(strtolower($e->getMessage()), 'timeout') || str_contains(strtolower($e->getMessage()), 'maximum execution time')) {
|
||||
$errorType = 'timeout';
|
||||
}
|
||||
|
||||
Log::error('SlikImport: Error during import', [
|
||||
'error' => $e->getMessage(),
|
||||
'error_type' => $errorType,
|
||||
'exception_type' => get_class($e),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'memory_usage' => memory_get_usage(true),
|
||||
'memory_peak' => memory_get_peak_usage(true),
|
||||
'memory_limit' => ini_get('memory_limit'),
|
||||
'timeout_limit' => ini_get('max_execution_time'),
|
||||
'is_memory_error' => str_contains(strtolower($e->getMessage()), 'memory'),
|
||||
'is_timeout_error' => str_contains(strtolower($e->getMessage()), 'timeout') || str_contains(strtolower($e->getMessage()), 'maximum execution time')
|
||||
]);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert memory limit string ke bytes
|
||||
*
|
||||
* @param string $memoryLimit
|
||||
* @return int
|
||||
*/
|
||||
private function convertToBytes(string $memoryLimit): int
|
||||
{
|
||||
$memoryLimit = trim($memoryLimit);
|
||||
$lastChar = strtolower(substr($memoryLimit, -1));
|
||||
$number = (int) substr($memoryLimit, 0, -1);
|
||||
|
||||
switch ($lastChar) {
|
||||
case 'g':
|
||||
return $number * 1024 * 1024 * 1024;
|
||||
case 'm':
|
||||
return $number * 1024 * 1024;
|
||||
case 'k':
|
||||
return $number * 1024;
|
||||
default:
|
||||
return (int) $memoryLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cek apakah baris kosong
|
||||
*
|
||||
* @param Collection $row
|
||||
* @return bool
|
||||
*/
|
||||
private function isEmptyRow(Collection $row): bool
|
||||
{
|
||||
return $row->filter(function ($value) {
|
||||
return !empty(trim($value));
|
||||
})->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi data baris
|
||||
*
|
||||
* @param Collection $row
|
||||
* @return bool
|
||||
*/
|
||||
private function validateRow(Collection $row): bool
|
||||
{
|
||||
// Validasi minimal: sandi_bank, no_rekening, dan cif harus ada
|
||||
return !empty(trim($row[0])) && // sandi_bank
|
||||
!empty(trim($row[5])) && // no_rekening
|
||||
!empty(trim($row[6])); // cif
|
||||
}
|
||||
|
||||
/**
|
||||
* Map data dari baris Excel ke array untuk model Slik
|
||||
*
|
||||
* @param Collection $row
|
||||
* @return array
|
||||
*/
|
||||
private function mapRowToSlik(Collection $row): array
|
||||
{
|
||||
return [
|
||||
'sandi_bank' => trim($row[0]) ?: null,
|
||||
'tahun' => $this->parseInteger($row[1]),
|
||||
'bulan' => $this->parseInteger($row[2]),
|
||||
'flag_detail' => trim($row[3]) ?: null,
|
||||
'kode_register_agunan' => trim($row[4]) ?: null,
|
||||
'no_rekening' => trim($row[5]) ?: null,
|
||||
'cif' => trim($row[6]) ?: null,
|
||||
'kolektibilitas' => trim($row[7]) ?: null,
|
||||
'fasilitas' => trim($row[8]) ?: null,
|
||||
'jenis_segmen_fasilitas' => trim($row[9]) ?: null,
|
||||
'status_agunan' => trim($row[10]) ?: null,
|
||||
'jenis_agunan' => trim($row[11]) ?: null,
|
||||
'peringkat_agunan' => trim($row[12]) ?: null,
|
||||
'lembaga_pemeringkat' => trim($row[13]) ?: null,
|
||||
'jenis_pengikatan' => trim($row[14]) ?: null,
|
||||
'tanggal_pengikatan' => $this->parseDate($row[15]),
|
||||
'nama_pemilik_agunan' => trim($row[16]) ?: null,
|
||||
'bukti_kepemilikan' => trim($row[17]) ?: null,
|
||||
'alamat_agunan' => trim($row[18]) ?: null,
|
||||
'lokasi_agunan' => trim($row[19]) ?: null,
|
||||
'nilai_agunan' => $this->parseDecimal($row[20]),
|
||||
'nilai_agunan_menurut_ljk' => $this->parseDecimal($row[21]),
|
||||
'tanggal_penilaian_ljk' => $this->parseDate($row[22]),
|
||||
'nilai_agunan_penilai_independen' => $this->parseDecimal($row[23]),
|
||||
'nama_penilai_independen' => trim($row[24]) ?: null,
|
||||
'tanggal_penilaian_penilai_independen' => $this->parseDate($row[25]),
|
||||
'jumlah_hari_tunggakan' => $this->parseInteger($row[26]),
|
||||
'status_paripasu' => trim($row[27]) ?: null,
|
||||
'prosentase_paripasu' => $this->parseDecimal($row[28]),
|
||||
'status_kredit_join' => trim($row[29]) ?: null,
|
||||
'diasuransikan' => trim($row[30]) ?: null,
|
||||
'keterangan' => trim($row[31]) ?: null,
|
||||
'kantor_cabang' => trim($row[32]) ?: null,
|
||||
'operasi_data' => trim($row[33]) ?: null,
|
||||
'kode_cabang' => trim($row[34]) ?: null,
|
||||
'nama_debitur' => trim($row[35]) ?: null,
|
||||
'nama_cabang' => trim($row[36]) ?: null,
|
||||
'flag' => trim($row[37]) ?: null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse integer value
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return int|null
|
||||
*/
|
||||
private function parseInteger($value): ?int
|
||||
{
|
||||
if (empty(trim($value))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse decimal value
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return float|null
|
||||
*/
|
||||
private function parseDecimal($value): ?float
|
||||
{
|
||||
if (empty(trim($value))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove currency formatting
|
||||
$cleaned = str_replace([',', '.'], ['', '.'], $value);
|
||||
$cleaned = preg_replace('/[^0-9.]/', '', $cleaned);
|
||||
|
||||
return (float) $cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse date value
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
private function parseDate($value): ?string
|
||||
{
|
||||
if (empty(trim($value))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to parse various date formats
|
||||
$date = \Carbon\Carbon::parse($value);
|
||||
return $date->format('Y-m-d');
|
||||
} catch (\Exception $e) {
|
||||
Log::warning('SlikImport: Invalid date format', [
|
||||
'value' => $value,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
156
app/Jobs/CleanupInspeksiDataJob.php
Normal file
156
app/Jobs/CleanupInspeksiDataJob.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
|
||||
/**
|
||||
* Job untuk membersihkan data inspeksi yang tidak memiliki dokument_id
|
||||
*
|
||||
* Case: Jika ada data inspeksi yang masuk dengan permohonan_id yang sama
|
||||
* tetapi memiliki dokument_id dan user created_by yang sama, maka
|
||||
* data lama (tanpa dokument_id) akan di-soft delete
|
||||
*/
|
||||
class CleanupInspeksiDataJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 300; // 5 menit
|
||||
public $tries = 3;
|
||||
public $maxExceptions = 3;
|
||||
public $backoff = [60, 120, 300]; // Exponential backoff dalam detik
|
||||
|
||||
protected int $permohonanId;
|
||||
protected int $createdBy;
|
||||
protected ?int $dokumentId;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param int $permohonanId
|
||||
* @param int $createdBy
|
||||
* @param int|null $dokumentId
|
||||
*/
|
||||
public function __construct(int $permohonanId, int $createdBy, ?int $dokumentId = null)
|
||||
{
|
||||
$this->permohonanId = $permohonanId;
|
||||
$this->createdBy = $createdBy;
|
||||
$this->dokumentId = $dokumentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::info('CleanupInspeksiDataJob: Memulai proses cleanup data inspeksi', [
|
||||
'permohonan_id' => $this->permohonanId,
|
||||
'created_by' => $this->createdBy,
|
||||
'dokument_id' => $this->dokumentId
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Cari data inspeksi yang memiliki dokument_id (data baru)
|
||||
$newInspeksi = Inspeksi::where('permohonan_id', $this->permohonanId)
|
||||
->where('created_by', $this->createdBy)
|
||||
->whereNotNull('dokument_id')
|
||||
->whereNull('deleted_at')
|
||||
->first();
|
||||
|
||||
if (!$newInspeksi) {
|
||||
Log::warning('CleanupInspeksiDataJob: Tidak ditemukan data inspeksi baru dengan dokument_id', [
|
||||
'permohonan_id' => $this->permohonanId,
|
||||
'created_by' => $this->createdBy
|
||||
]);
|
||||
DB::rollBack();
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info('CleanupInspeksiDataJob: Data inspeksi baru ditemukan', [
|
||||
'inspeksi_id' => $newInspeksi->id,
|
||||
'dokument_id' => $newInspeksi->dokument_id
|
||||
]);
|
||||
|
||||
// Cari data inspeksi lama yang tidak memiliki dokument_id
|
||||
$oldInspeksiList = Inspeksi::where('permohonan_id', $this->permohonanId)
|
||||
->where('created_by', $this->createdBy)
|
||||
->whereNull('dokument_id')
|
||||
->whereNull('deleted_at')
|
||||
->where('id', '!=', $newInspeksi->id) // Jangan hapus data yang baru saja ditemukan
|
||||
->get();
|
||||
|
||||
if ($oldInspeksiList->isEmpty()) {
|
||||
Log::info('CleanupInspeksiDataJob: Tidak ditemukan data inspeksi lama tanpa dokument_id', [
|
||||
'permohonan_id' => $this->permohonanId,
|
||||
'created_by' => $this->createdBy
|
||||
]);
|
||||
DB::commit();
|
||||
return;
|
||||
}
|
||||
|
||||
$deletedCount = 0;
|
||||
foreach ($oldInspeksiList as $oldInspeksi) {
|
||||
// Soft delete data lama
|
||||
$oldInspeksi->delete(); // Menggunakan soft delete karena model menggunakan SoftDeletes trait
|
||||
|
||||
Log::info('CleanupInspeksiDataJob: Data inspeksi lama berhasil di-soft delete', [
|
||||
'old_inspeksi_id' => $oldInspeksi->id,
|
||||
'permohonan_id' => $oldInspeksi->permohonan_id,
|
||||
'created_by' => $oldInspeksi->created_by,
|
||||
'deleted_at' => now()->toDateTimeString()
|
||||
]);
|
||||
|
||||
$deletedCount++;
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
Log::info('CleanupInspeksiDataJob: Proses cleanup selesai', [
|
||||
'permohonan_id' => $this->permohonanId,
|
||||
'created_by' => $this->createdBy,
|
||||
'deleted_count' => $deletedCount,
|
||||
'new_inspeksi_id' => $newInspeksi->id
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
Log::error('CleanupInspeksiDataJob: Terjadi error saat proses cleanup', [
|
||||
'permohonan_id' => $this->permohonanId,
|
||||
'created_by' => $this->createdBy,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a job failure.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function failed(\Throwable $exception): void
|
||||
{
|
||||
Log::error('CleanupInspeksiDataJob: Job gagal dieksekusi', [
|
||||
'permohonan_id' => $this->permohonanId,
|
||||
'created_by' => $this->createdBy,
|
||||
'error' => $exception->getMessage(),
|
||||
'trace' => $exception->getTraceAsString()
|
||||
]);
|
||||
}
|
||||
}
|
||||
179
app/Jobs/ProcessSlikImport.php
Normal file
179
app/Jobs/ProcessSlikImport.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Lpj\Imports\SlikImport;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
class ProcessSlikImport implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 1800; // 30 menit untuk file besar
|
||||
public $tries = 5; // Tambah retry untuk file sangat besar
|
||||
public $maxExceptions = 5;
|
||||
public $backoff = [60, 300, 900, 1800, 3600]; // Exponential backoff dalam detik
|
||||
|
||||
protected string $filePath;
|
||||
protected int $userId;
|
||||
protected string $importId;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param int $userId
|
||||
* @param string $importId
|
||||
*/
|
||||
public function __construct(string $filePath, int $userId, string $importId)
|
||||
{
|
||||
$this->filePath = $filePath;
|
||||
$this->userId = $userId;
|
||||
$this->importId = $importId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::info('ProcessSlikImport: Memulai proses import via queue', [
|
||||
'file_path' => $this->filePath,
|
||||
'user_id' => $this->userId,
|
||||
'import_id' => $this->importId,
|
||||
'memory_limit' => ini_get('memory_limit'),
|
||||
'max_execution_time' => ini_get('max_execution_time')
|
||||
]);
|
||||
|
||||
try {
|
||||
// Cek file size terlebih dahulu
|
||||
$fileSize = filesize($this->filePath);
|
||||
$maxFileSize = config('import.slik.max_file_size', 50) * 1024 * 1024; // Convert MB to bytes
|
||||
|
||||
if ($fileSize > $maxFileSize) {
|
||||
throw new \Exception('File terlalu besar: ' . number_format($fileSize / 1024 / 1024, 2) . ' MB. Maksimum: ' . config('import.slik.max_file_size', 50) . ' MB');
|
||||
}
|
||||
|
||||
// Set optimasi memory untuk queue processing
|
||||
$memoryLimit = config('import.slik.memory_limit', 1024);
|
||||
ini_set('memory_limit', $memoryLimit . 'M');
|
||||
ini_set('max_execution_time', config('import.slik.timeout', 1800));
|
||||
|
||||
// Set timeout untuk XML Scanner
|
||||
$xmlScannerTimeout = config('import.slik.xml_scanner.timeout', 1800);
|
||||
$xmlScannerMemory = config('import.slik.xml_scanner.memory_limit', 1024);
|
||||
|
||||
// Enable garbage collection jika diizinkan
|
||||
if (config('import.slik.enable_gc', true)) {
|
||||
gc_enable();
|
||||
}
|
||||
|
||||
// Update progress status
|
||||
$this->updateProgress('processing', 0, 'Memproses file Excel...');
|
||||
|
||||
Log::info('SlikImport: Processing file', [
|
||||
'file' => basename($this->filePath),
|
||||
'file_size' => number_format(filesize($this->filePath) / 1024 / 1024, 2) . ' MB',
|
||||
'memory_limit' => $memoryLimit . 'M',
|
||||
'timeout' => config('import.slik.timeout', 1800),
|
||||
'enable_gc' => config('import.slik.enable_gc', true),
|
||||
'xml_scanner_timeout' => config('import.slik.xml_scanner.timeout', 1800),
|
||||
'chunk_size' => config('import.slik.chunk_size', 50),
|
||||
'batch_size' => config('import.slik.batch_size', 50),
|
||||
]);
|
||||
|
||||
// Import file menggunakan SlikImport
|
||||
$import = new SlikImport();
|
||||
Excel::import($import, $this->filePath);
|
||||
|
||||
// Update progress selesai
|
||||
$this->updateProgress('completed', 100, 'Import berhasil diselesaikan');
|
||||
|
||||
Log::info('ProcessSlikImport: Import berhasil diselesaikan', [
|
||||
'import_id' => $this->importId,
|
||||
'file_path' => $this->filePath,
|
||||
'memory_usage' => memory_get_usage(true),
|
||||
'memory_peak' => memory_get_peak_usage(true)
|
||||
]);
|
||||
|
||||
// Hapus file temporary setelah selesai
|
||||
if (config('import.general.cleanup_temp_files', true)) {
|
||||
Storage::delete($this->filePath);
|
||||
Log::info('ProcessSlikImport: File temporary dihapus', ['file_path' => $this->filePath]);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Update progress error
|
||||
$this->updateProgress('failed', 0, 'Error: ' . $e->getMessage());
|
||||
|
||||
Log::error('ProcessSlikImport: Error saat proses import', [
|
||||
'import_id' => $this->importId,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'memory_usage' => memory_get_usage(true)
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update progress import
|
||||
*
|
||||
* @param string $status
|
||||
* @param int $percentage
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
private function updateProgress(string $status, int $percentage, string $message): void
|
||||
{
|
||||
if (config('import.slik.progress.enabled', true)) {
|
||||
$cacheKey = config('import.slik.progress.cache_key', 'slik_import_progress') . '_' . $this->importId;
|
||||
$cacheTtl = config('import.slik.progress.cache_ttl', 3600);
|
||||
|
||||
$progressData = [
|
||||
'status' => $status,
|
||||
'percentage' => $percentage,
|
||||
'message' => $message,
|
||||
'timestamp' => now(),
|
||||
'user_id' => $this->userId
|
||||
];
|
||||
|
||||
cache()->put($cacheKey, $progressData, $cacheTtl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle job failure
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function failed(\Throwable $exception): void
|
||||
{
|
||||
Log::error('ProcessSlikImport: Job failed', [
|
||||
'import_id' => $this->importId,
|
||||
'error' => $exception->getMessage(),
|
||||
'trace' => $exception->getTraceAsString()
|
||||
]);
|
||||
|
||||
// Update progress ke failed
|
||||
$this->updateProgress('failed', 0, 'Import gagal: ' . $exception->getMessage());
|
||||
|
||||
// Cleanup file temporary
|
||||
if (Storage::exists($this->filePath)) {
|
||||
Storage::delete($this->filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,10 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Modules\Lpj\Emails\SendJadwalKunjunganEmail;
|
||||
use Modules\Lpj\Emails\SendJadwalKunjunganEmailPHPMailer;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Exception;
|
||||
|
||||
class SendJadwalKunjunganEmailJob implements ShouldQueue
|
||||
{
|
||||
@@ -28,6 +30,30 @@ class SendJadwalKunjunganEmailJob implements ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Mail::to($this->emailData['email'])->send(new SendJadwalKunjunganEmail($this->emailData));
|
||||
// Buat instance email PHPMailer dengan attachment support
|
||||
$email = new SendJadwalKunjunganEmailPHPMailer($this->emailData);
|
||||
|
||||
// Siapkan attachment jika ada
|
||||
$attachments = [];
|
||||
if (isset($this->emailData['attachments'])) {
|
||||
$attachments = $this->emailData['attachments'];
|
||||
}
|
||||
|
||||
// Kirim email menggunakan PHPMailer
|
||||
$result = $email->sendWithPHPMailer($this->emailData['email'], $attachments);
|
||||
|
||||
if ($result['success']) {
|
||||
Log::info('Email jadwal kunjungan berhasil dikirim menggunakan PHPMailer', [
|
||||
'email' => $this->emailData['email'],
|
||||
'result' => $result
|
||||
]);
|
||||
} else {
|
||||
Log::error('Gagal mengirim email jadwal kunjungan menggunakan PHPMailer', [
|
||||
'email' => $this->emailData['email'],
|
||||
'error' => $result['error'] ?? 'Unknown error'
|
||||
]);
|
||||
|
||||
throw new Exception('Gagal mengirim email: ' . ($result['error'] ?? 'Unknown error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Modules\Lpj\Emails\SendPenawaranKJPPEmail;
|
||||
use Modules\Lpj\Emails\SendPenawaranKJPPEmailPHPMailer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Exception;
|
||||
|
||||
class SendPenawaranKJPPTenderJob implements ShouldQueue
|
||||
{
|
||||
@@ -45,7 +47,7 @@ class SendPenawaranKJPPTenderJob implements ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$email = new SendPenawaranKJPPEmail(
|
||||
$email = new SendPenawaranKJPPEmailPHPMailer(
|
||||
$this->dp1,
|
||||
$this->penawaran,
|
||||
$this->permohonan,
|
||||
@@ -53,20 +55,24 @@ class SendPenawaranKJPPTenderJob implements ShouldQueue
|
||||
$this->districts,
|
||||
$this->cities,
|
||||
$this->provinces,
|
||||
$this->user // Kirim user ke email sebagai cc dan bcc
|
||||
$this->user
|
||||
);
|
||||
|
||||
$email->with([
|
||||
'dp1' => $this->dp1, // Kirim seluruh array dp1 ke email
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user // Kirim user ke email sebagai cc dan bcc
|
||||
]);
|
||||
// Gunakan PHPMailer untuk mengirim email
|
||||
$result = $email->sendWithPHPMailer($this->kjpps);
|
||||
|
||||
$send = Mail::to($this->kjpps)->send($email);
|
||||
if ($result['success']) {
|
||||
Log::info('Email penawaran KJPP berhasil dikirim menggunakan PHPMailer', [
|
||||
'recipients' => $this->kjpps,
|
||||
'result' => $result
|
||||
]);
|
||||
} else {
|
||||
Log::error('Gagal mengirim email penawaran KJPP menggunakan PHPMailer', [
|
||||
'recipients' => $this->kjpps,
|
||||
'error' => $result['error'] ?? 'Unknown error'
|
||||
]);
|
||||
|
||||
throw new Exception('Gagal mengirim email: ' . ($result['error'] ?? 'Unknown error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Modules\Lpj\Emails\SendPenawaranTenderEmail;
|
||||
use Modules\Lpj\Emails\SendPenawaranTenderEmailPHPMailer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Exception;
|
||||
|
||||
class SendPenawaranTenderJob implements ShouldQueue
|
||||
{
|
||||
@@ -43,17 +45,37 @@ class SendPenawaranTenderJob implements ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$email = new SendPenawaranTenderEmail();
|
||||
$email->with([
|
||||
'penawaran' => $this->penawaran,
|
||||
'permohonan' => $this->permohonan,
|
||||
'villages' => $this->villages,
|
||||
'districts' => $this->districts,
|
||||
'cities' => $this->cities,
|
||||
'provinces' => $this->provinces,
|
||||
'user' => $this->user // Kirim user ke email ke properti sebagai additional data
|
||||
]);
|
||||
$email = new SendPenawaranTenderEmailPHPMailer(
|
||||
$this->penawaran,
|
||||
$this->permohonan,
|
||||
$this->villages,
|
||||
$this->districts,
|
||||
$this->cities,
|
||||
$this->provinces,
|
||||
$this->user
|
||||
);
|
||||
|
||||
Mail::to($this->kjpps)->send($email);
|
||||
// Siapkan attachment jika ada
|
||||
$attachments = [];
|
||||
if (isset($this->penawaran['attachments'])) {
|
||||
$attachments = $this->penawaran['attachments'];
|
||||
}
|
||||
|
||||
// Kirim email menggunakan PHPMailer
|
||||
$result = $email->sendWithPHPMailer($this->kjpps, $attachments);
|
||||
|
||||
if ($result['success']) {
|
||||
Log::info('Email penawaran tender berhasil dikirim menggunakan PHPMailer', [
|
||||
'recipients' => $this->kjpps,
|
||||
'result' => $result
|
||||
]);
|
||||
} else {
|
||||
Log::error('Gagal mengirim email penawaran tender menggunakan PHPMailer', [
|
||||
'recipients' => $this->kjpps,
|
||||
'error' => $result['error'] ?? 'Unknown error'
|
||||
]);
|
||||
|
||||
throw new Exception('Gagal mengirim email: ' . ($result['error'] ?? 'Unknown error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ class Bucok extends Base
|
||||
'keterangan_gantung',
|
||||
'lainnya_satu',
|
||||
'lainnya_dua',
|
||||
'nomor_registrasi',
|
||||
'permohonan_id',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ class CategoryDaftarPustaka extends Model
|
||||
];
|
||||
|
||||
public function daftarPustaka(){
|
||||
return $this->hasMany(DaftarPustaka::class);
|
||||
return $this->hasMany(DaftarPustaka::class, 'category_id');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class Inspeksi extends Base
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = ['data_form', 'foto_form', 'denah_form','permohonan_id', 'name', 'status', 'authorized_status', 'authorized_at', 'authorized_by', 'created_by', 'updated_by', 'deleted_by','dokument_id','data_pembanding'];
|
||||
protected $fillable = ['data_form', 'foto_form', 'denah_form','permohonan_id', 'name', 'status', 'authorized_status', 'authorized_at', 'authorized_by', 'created_by', 'updated_by', 'deleted_by','dokument_id','data_pembanding','mig_detail_data_jaminan'];
|
||||
|
||||
public function permohonan()
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ class LampiranDokumen extends Base
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user && $user->hasAnyRole(['penilai', 'administrator', 'Penilai', 'admin','surveyor'])) {
|
||||
if ($user && $user->hasAnyRole(['penilai', 'administrator', 'Penilai', 'admin','surveyor','pemohon-ao','pemohon-eo'])) {
|
||||
$file = $fileData['file'];
|
||||
$fileName = $fileData['nama_file'] ?? time() . '_' . $file->getClientOriginalName();
|
||||
$filePath = $file->storeAs('lampiran_dokumen', $fileName, 'public');
|
||||
|
||||
@@ -18,7 +18,9 @@ class LaporanAdminKredit extends Base
|
||||
'tanggal_kunjungan',
|
||||
'nilai_pasar_wajar',
|
||||
'nilai_likuidasi',
|
||||
'nama_penilai'
|
||||
'nama_penilai',
|
||||
'keterangan',
|
||||
'kolektibilitas'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
||||
114
app/Models/LaporanSlik.php
Normal file
114
app/Models/LaporanSlik.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
|
||||
class LaporanSlik extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'laporan_slik';
|
||||
|
||||
protected $fillable = [
|
||||
'slik_id',
|
||||
'sandi_bank',
|
||||
'kode_kantor',
|
||||
'kode_cabang',
|
||||
'tahun',
|
||||
'bulan',
|
||||
'no_rekening',
|
||||
'cif',
|
||||
'kode_jenis',
|
||||
'kode_jenis_ket',
|
||||
'kode_sifat',
|
||||
'kode_sifat_ket',
|
||||
'kode_valuta',
|
||||
'kode_valuta_ket',
|
||||
'baki_debet',
|
||||
'kolektibilitas',
|
||||
'kolektibilitas_ket',
|
||||
'tanggal_mulai',
|
||||
'tanggal_jatuh_tempo',
|
||||
'tanggal_selesai',
|
||||
'tanggal_restrukturisasi',
|
||||
'kode_sebab_macet',
|
||||
'kode_sebab_macet_ket',
|
||||
'tanggal_macet',
|
||||
'kode_kondisi',
|
||||
'kode_kondisi_ket',
|
||||
'tanggal_kondisi',
|
||||
'nilai_agunan',
|
||||
'nilai_agunan_ket',
|
||||
'jenis_agunan',
|
||||
'kode_agunan',
|
||||
'kode_agunan_ket',
|
||||
'peringkat_agunan',
|
||||
'peringkat_agunan_ket',
|
||||
'nama_debitur',
|
||||
'npwp',
|
||||
'no_ktp',
|
||||
'no_telp',
|
||||
'kode_kab_kota',
|
||||
'kode_kab_kota_ket',
|
||||
'kode_negara_domisili',
|
||||
'kode_negara_domisili_ket',
|
||||
'kode_pos',
|
||||
'alamat',
|
||||
'fasilitas',
|
||||
'status_agunan',
|
||||
'tanggal_lapor',
|
||||
'status',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function slik()
|
||||
{
|
||||
return $this->belongsTo(Slik::class, 'slik_id');
|
||||
}
|
||||
|
||||
public function creator()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function updater()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter berdasarkan status
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('status', 'aktif');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk nilai agunan yang diformat
|
||||
*/
|
||||
public function getNilaiAgunanFormattedAttribute()
|
||||
{
|
||||
return number_format($this->nilai_agunan ?? 0, 0, ',', '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk status badge
|
||||
*/
|
||||
public function getStatusBadgeAttribute()
|
||||
{
|
||||
$status = $this->status ?? 'aktif';
|
||||
$class = $status == 'aktif' ? 'success' : 'danger';
|
||||
|
||||
return '<span class="badge badge-light-' . $class . '">' . ucfirst($status) . '</span>';
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,5 @@
|
||||
class NilaiPlafond extends Base
|
||||
{
|
||||
protected $table = 'nilai_plafond';
|
||||
protected $fillable = ['code', 'name'];
|
||||
protected $fillable = ['code', 'name', 'biaya'];
|
||||
}
|
||||
|
||||
@@ -8,39 +8,7 @@ class Noc extends Base
|
||||
{
|
||||
protected $table = 'noc';
|
||||
|
||||
protected $fillable = [
|
||||
'permohonan_id',
|
||||
'persetujuan_penawaran_id',
|
||||
'nomor_tiket',
|
||||
'bukti_bayar',
|
||||
'nominal_bayar',
|
||||
'total_pembukuan',
|
||||
'status_bayar',
|
||||
'status_kurang_bayar',
|
||||
'nominal_kurang_bayar',
|
||||
'status_lebih_bayar',
|
||||
'nominal_lebih_bayar',
|
||||
'bukti_pengembalian',
|
||||
'tanggal_pembayaran',
|
||||
'nominal_penyelesaian',
|
||||
'status_penyelesaiaan',
|
||||
'tanggal_penyelesaian',
|
||||
'bukti_penyelesaian',
|
||||
'bukti_ksl',
|
||||
'memo_penyelesaian',
|
||||
'memo_penyelesaian_number',
|
||||
'memo_penyelesaian_date',
|
||||
'memo_penyelesaian_payment_date',
|
||||
'memo_penyelesaian_created_at',
|
||||
'catatan_noc',
|
||||
'status',
|
||||
'authorized_status',
|
||||
'authorized_at',
|
||||
'authorized_by',
|
||||
'nomor_rekening_lebih_bayar',
|
||||
'bukti_ksl_lebih_bayar',
|
||||
'bukti_ksl_kurang_bayar'
|
||||
];
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $casts = [
|
||||
'nominal_bayar' => 'decimal:2',
|
||||
@@ -78,4 +46,12 @@ class Noc extends Base
|
||||
{
|
||||
return $this->belongsTo(User::class, 'authorized_by');
|
||||
}
|
||||
|
||||
public function debiture(){
|
||||
return $this->belongsTo(Debiture::class,'debiture_id');
|
||||
}
|
||||
|
||||
public function branch(){
|
||||
return $this->belongsTo(Branch::class,'branch_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Modules\Lpj\Models;
|
||||
|
||||
use Modules\Usermanagement\Models\User;
|
||||
use Modules\Usermanagemenet\Models\User;
|
||||
|
||||
class PersetujuanPenawaran extends Base
|
||||
{
|
||||
@@ -13,9 +13,6 @@
|
||||
'permohonan_id',
|
||||
'penawaran_id',
|
||||
'nomor_proposal_penawaran',
|
||||
'nomor_tiket',
|
||||
'nominal_kurang_bayar',
|
||||
'bukti_ksl_kurang_bayar',
|
||||
'tanggal_proposal_penawaran',
|
||||
'biaya_final',
|
||||
'sla_resume',
|
||||
@@ -49,6 +46,12 @@
|
||||
return $this->belongsTo(Permohonan::class, 'permohonan_id');
|
||||
}
|
||||
|
||||
// Relationship with Region
|
||||
public function region()
|
||||
{
|
||||
return $this->belongsTo(Region::class);
|
||||
}
|
||||
|
||||
// Relationship with User (for authorized_by)
|
||||
public function authorizedBy()
|
||||
{
|
||||
|
||||
152
app/Models/ReferensiLink.php
Normal file
152
app/Models/ReferensiLink.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
|
||||
class ReferensiLink extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'referensi_link';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'link',
|
||||
'kategori',
|
||||
'deskripsi',
|
||||
'is_active',
|
||||
'urutan',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'urutan' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($model) {
|
||||
if (Auth::check()) {
|
||||
$model->created_by = Auth::id();
|
||||
$model->updated_by = Auth::id();
|
||||
}
|
||||
});
|
||||
|
||||
static::updating(function ($model) {
|
||||
if (Auth::check()) {
|
||||
$model->updated_by = Auth::id();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter data aktif
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter berdasarkan kategori
|
||||
*/
|
||||
public function scopeByKategori($query, $kategori)
|
||||
{
|
||||
return $query->where('kategori', $kategori);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk urutkan berdasarkan urutan
|
||||
*/
|
||||
public function scopeOrdered($query)
|
||||
{
|
||||
return $query->orderBy('urutan', 'asc')->orderBy('name', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk pencarian
|
||||
*/
|
||||
public function scopeSearch($query, $search)
|
||||
{
|
||||
return $query->where(function ($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('link', 'like', "%{$search}%")
|
||||
->orWhere('kategori', 'like', "%{$search}%")
|
||||
->orWhere('deskripsi', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Relasi ke user yang membuat
|
||||
*/
|
||||
public function createdBy()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relasi ke user yang update
|
||||
*/
|
||||
public function updatedBy()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk status badge
|
||||
*/
|
||||
public function getStatusBadgeAttribute()
|
||||
{
|
||||
return $this->is_active
|
||||
? '<span class="badge bg-success">Aktif</span>'
|
||||
: '<span class="badge bg-danger">Tidak Aktif</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk link yang diformat
|
||||
*/
|
||||
public function getFormattedLinkAttribute()
|
||||
{
|
||||
return $this->link ? url($this->link) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutator untuk memastikan link valid
|
||||
*/
|
||||
public function setLinkAttribute($value)
|
||||
{
|
||||
// Validasi dan format link
|
||||
if ($value && !preg_match('/^(https?:\/\/)/i', $value)) {
|
||||
$value = 'https://' . $value;
|
||||
}
|
||||
$this->attributes['link'] = $value;
|
||||
}
|
||||
}
|
||||
190
app/Models/Slik.php
Normal file
190
app/Models/Slik.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Model Slik untuk mengelola data SLIK (Sistem Layanan Informasi Keuangan)
|
||||
*
|
||||
* @property int $id
|
||||
* @property string|null $sandi_bank
|
||||
* @property string|null $tahun
|
||||
* @property string|null $bulan
|
||||
* @property string|null $flag_detail
|
||||
* @property string|null $kode_register_agunan
|
||||
* @property string|null $no_rekening
|
||||
* @property string|null $cif
|
||||
* @property string|null $kolektibilitas
|
||||
* @property string|null $fasilitas
|
||||
* @property string|null $jenis_segmen_fasilitas
|
||||
* @property string|null $status_agunan
|
||||
* @property string|null $jenis_agunan
|
||||
* @property string|null $peringkat_agunan
|
||||
* @property string|null $lembaga_pemeringkat
|
||||
* @property string|null $jenis_pengikatan
|
||||
* @property string|null $tanggal_pengikatan
|
||||
* @property string|null $nama_pemilik_agunan
|
||||
* @property string|null $bukti_kepemilikan
|
||||
* @property string|null $alamat_agunan
|
||||
* @property string|null $lokasi_agunan
|
||||
* @property string|null $nilai_agunan
|
||||
* @property string|null $nilai_agunan_menurut_ljk
|
||||
* @property string|null $tanggal_penilaian_ljk
|
||||
* @property string|null $nilai_agunan_penilai_independen
|
||||
* @property string|null $nama_penilai_independen
|
||||
* @property string|null $tanggal_penilaian_penilai_independen
|
||||
* @property string|null $jumlah_hari_tunggakan
|
||||
* @property string|null $status_paripasu
|
||||
* @property string|null $prosentase_paripasu
|
||||
* @property string|null $status_kredit_join
|
||||
* @property string|null $diasuransikan
|
||||
* @property string|null $keterangan
|
||||
* @property string|null $kantor_cabang
|
||||
* @property string|null $operasi_data
|
||||
* @property string|null $kode_cabang
|
||||
* @property string|null $nama_debitur
|
||||
* @property string|null $nama_cabang
|
||||
* @property string|null $flag
|
||||
*/
|
||||
class Slik extends Base
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Nama tabel yang digunakan oleh model
|
||||
*/
|
||||
protected $table = 'sliks';
|
||||
|
||||
/**
|
||||
* Field yang dapat diisi secara mass assignment
|
||||
*/
|
||||
protected $fillable = [
|
||||
'sandi_bank',
|
||||
'tahun',
|
||||
'bulan',
|
||||
'flag_detail',
|
||||
'kode_register_agunan',
|
||||
'no_rekening',
|
||||
'cif',
|
||||
'kolektibilitas',
|
||||
'fasilitas',
|
||||
'jenis_segmen_fasilitas',
|
||||
'status_agunan',
|
||||
'jenis_agunan',
|
||||
'peringkat_agunan',
|
||||
'lembaga_pemeringkat',
|
||||
'jenis_pengikatan',
|
||||
'tanggal_pengikatan',
|
||||
'nama_pemilik_agunan',
|
||||
'bukti_kepemilikan',
|
||||
'alamat_agunan',
|
||||
'lokasi_agunan',
|
||||
'nilai_agunan',
|
||||
'nilai_agunan_menurut_ljk',
|
||||
'tanggal_penilaian_ljk',
|
||||
'nilai_agunan_penilai_independen',
|
||||
'nama_penilai_independen',
|
||||
'tanggal_penilaian_penilai_independen',
|
||||
'jumlah_hari_tunggakan',
|
||||
'status_paripasu',
|
||||
'prosentase_paripasu',
|
||||
'status_kredit_join',
|
||||
'diasuransikan',
|
||||
'keterangan',
|
||||
'kantor_cabang',
|
||||
'operasi_data',
|
||||
'kode_cabang',
|
||||
'nama_debitur',
|
||||
'nama_cabang',
|
||||
'flag',
|
||||
];
|
||||
|
||||
/**
|
||||
* Casting tipe data untuk field tertentu
|
||||
*/
|
||||
protected $casts = [
|
||||
'tanggal_pengikatan' => 'date',
|
||||
'tanggal_penilaian_ljk' => 'date',
|
||||
'tanggal_penilaian_penilai_independen' => 'date',
|
||||
'nilai_agunan' => 'decimal:2',
|
||||
'nilai_agunan_menurut_ljk' => 'decimal:2',
|
||||
'nilai_agunan_penilai_independen' => 'decimal:2',
|
||||
'prosentase_paripasu' => 'decimal:2',
|
||||
'jumlah_hari_tunggakan' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Accessor untuk format nilai agunan dengan currency Indonesia
|
||||
*/
|
||||
public function getNilaiAgunanFormattedAttribute(): string
|
||||
{
|
||||
return $this->nilai_agunan ? 'Rp ' . number_format($this->nilai_agunan, 0, ',', '.') : 'Rp 0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk format nilai agunan menurut LJK dengan currency Indonesia
|
||||
*/
|
||||
public function getNilaiAgunanMenurutLjkFormattedAttribute(): string
|
||||
{
|
||||
return $this->nilai_agunan_menurut_ljk ? 'Rp ' . number_format($this->nilai_agunan_menurut_ljk, 0, ',', '.') : 'Rp 0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk format nilai agunan penilai independen dengan currency Indonesia
|
||||
*/
|
||||
public function getNilaiAgunanPenilaiIndependenFormattedAttribute(): string
|
||||
{
|
||||
return $this->nilai_agunan_penilai_independen ? 'Rp ' . number_format($this->nilai_agunan_penilai_independen, 0, ',', '.') : 'Rp 0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor untuk status badge berdasarkan status agunan
|
||||
*/
|
||||
public function getStatusBadgeAttribute(): string
|
||||
{
|
||||
$statusClass = match($this->status_agunan) {
|
||||
'Aktif' => 'badge-success',
|
||||
'Tidak Aktif' => 'badge-danger',
|
||||
'Pending' => 'badge-warning',
|
||||
default => 'badge-secondary'
|
||||
};
|
||||
|
||||
return '<span class="badge ' . $statusClass . '">' . ($this->status_agunan ?? 'Unknown') . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter berdasarkan tahun
|
||||
*/
|
||||
public function scopeByYear($query, $year)
|
||||
{
|
||||
return $query->where('tahun', $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter berdasarkan bulan
|
||||
*/
|
||||
public function scopeByMonth($query, $month)
|
||||
{
|
||||
return $query->where('bulan', $month);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter berdasarkan sandi bank
|
||||
*/
|
||||
public function scopeBySandiBank($query, $sandiBank)
|
||||
{
|
||||
return $query->where('sandi_bank', $sandiBank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope untuk filter berdasarkan kode cabang
|
||||
*/
|
||||
public function scopeByKodeCabang($query, $kodeCabang)
|
||||
{
|
||||
return $query->where('kode_cabang', $kodeCabang);
|
||||
}
|
||||
|
||||
// Method creator() dan editor() sudah disediakan oleh trait Userstamps
|
||||
}
|
||||
@@ -32,25 +32,46 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Register commands in the format of Command::class
|
||||
*/
|
||||
protected function registerCommands()
|
||||
: void
|
||||
{
|
||||
// $this->commands([]);
|
||||
}
|
||||
* Register commands in the format of Command::class
|
||||
*/
|
||||
protected function registerCommands()
|
||||
: void
|
||||
{
|
||||
$this->commands([
|
||||
\Modules\Lpj\Console\Commands\CleanupInspeksiDataCommand::class,
|
||||
\Modules\Lpj\Console\Commands\CleanupSingleInspeksiCommand::class,
|
||||
\Modules\Lpj\Console\Commands\CleanupInspeksiStatusCommand::class,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register command Schedules.
|
||||
*/
|
||||
protected function registerCommandSchedules()
|
||||
: void
|
||||
{
|
||||
// $this->app->booted(function () {
|
||||
// $schedule = $this->app->make(Schedule::class);
|
||||
// $schedule->command('inspire')->hourly();
|
||||
// });
|
||||
}
|
||||
* Register command Schedules.
|
||||
*/
|
||||
protected function registerCommandSchedules()
|
||||
: void
|
||||
{
|
||||
$this->app->booted(function () {
|
||||
$schedule = $this->app->make(\Illuminate\Console\Scheduling\Schedule::class);
|
||||
|
||||
// Jalankan cleanup inspeksi setiap hari jam 2 pagi
|
||||
$schedule->command('lpj:cleanup-inspeksi --force')
|
||||
->dailyAt('02:00')
|
||||
->withoutOverlapping()
|
||||
->onOneServer()
|
||||
->runInBackground()
|
||||
->appendOutputTo(storage_path('logs/cleanup-inspeksi.log'));
|
||||
|
||||
// Backup cleanup setiap minggu
|
||||
$schedule->command('lpj:cleanup-inspeksi --force')
|
||||
->weekly()
|
||||
->sundays()
|
||||
->at('03:00')
|
||||
->withoutOverlapping()
|
||||
->onOneServer()
|
||||
->runInBackground()
|
||||
->appendOutputTo(storage_path('logs/cleanup-inspeksi-weekly.log'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
|
||||
@@ -50,44 +50,40 @@ class DaftarPustakaService
|
||||
|
||||
// get all with pagination
|
||||
public function getAllDaftarPustaka($request)
|
||||
{
|
||||
$query = DaftarPustaka::query();
|
||||
{
|
||||
$query = DaftarPustaka::query();
|
||||
|
||||
// Filter pencarian
|
||||
if (!empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->orWhere('judul', 'LIKE', "%$search%");
|
||||
});
|
||||
// Filter pencarian
|
||||
if (!empty($request->get('search'))) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->orWhere('judul', 'LIKE', "%$search%");
|
||||
});
|
||||
}
|
||||
|
||||
// Filter kategori
|
||||
if (!empty($request->get('category'))) {
|
||||
$category = explode(',', $request->input('category'));
|
||||
$query->whereIn('category_id', $category);
|
||||
}
|
||||
|
||||
// Default pagination
|
||||
$page = (int) $request->get('page', 1);
|
||||
$size = (int) $request->get('size', 10);
|
||||
|
||||
return $query->paginate($size, ['*'], 'page', $page);
|
||||
}
|
||||
|
||||
// Filter kategori
|
||||
if (!empty($request->get('category'))) {
|
||||
$category = explode(',', $request->input('category'));
|
||||
$query->whereIn('category_id', $category);
|
||||
}
|
||||
|
||||
// Default pagination
|
||||
$page = (int) $request->get('page', 1);
|
||||
$size = (int) $request->get('size', 10);
|
||||
|
||||
return $query->paginate($size, ['*'], 'page', $page);
|
||||
}
|
||||
|
||||
|
||||
private function handleUpload($file)
|
||||
{
|
||||
$today = now();
|
||||
$folderPath = 'daftar_pustaka/' . $today->format('Y/m/d');
|
||||
|
||||
if (!file_exists(public_path($folderPath))) {
|
||||
mkdir(public_path($folderPath), 0755, true);
|
||||
}
|
||||
|
||||
$fileName = $file->getClientOriginalName();
|
||||
$file->move(public_path($folderPath), $fileName);
|
||||
$filePath = $file->storeAs($folderPath, $fileName, 'public');
|
||||
|
||||
return $folderPath . '/' . $fileName;
|
||||
return $filePath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
73
app/Services/ImageResizeService.php
Normal file
73
app/Services/ImageResizeService.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
|
||||
class ImageResizeService
|
||||
{
|
||||
/**
|
||||
* Resize an image and store it.
|
||||
*
|
||||
* @param string $originalPath
|
||||
* @param int $width
|
||||
* @param int|null $height
|
||||
* @param int $quality
|
||||
* @return string
|
||||
*/
|
||||
public function resize(string $originalPath, ?int $width, ?int $height = null, int $quality = 80): string
|
||||
{
|
||||
if (empty($originalPath) || !Storage::disk('public')->exists($originalPath)) {
|
||||
Log::warning("Image Service: Original path is empty or does not exist: {$originalPath}");
|
||||
return '';
|
||||
}
|
||||
|
||||
$height = null;
|
||||
|
||||
$pathinfo = pathinfo($originalPath);
|
||||
|
||||
// Kembali menggunakan direktori 'resized' dan menyertakan dimensi di nama file
|
||||
$resizedPath = "{$pathinfo['dirname']}/resized/{$pathinfo['filename']}_{$width}x{$height}_{$quality}.{$pathinfo['extension']}";
|
||||
|
||||
if (Storage::disk('public')->exists($resizedPath)) {
|
||||
return $resizedPath;
|
||||
}
|
||||
|
||||
try {
|
||||
$originalFullPath = Storage::disk('public')->path($originalPath);
|
||||
$newFullPath = Storage::disk('public')->path($resizedPath);
|
||||
|
||||
$image = Image::read($originalFullPath);
|
||||
|
||||
// Resize dengan menjaga aspek rasio jika height null
|
||||
if ($width && $height) {
|
||||
// Paksa resize ke dimensi yang ditentukan, abaikan aspek rasio
|
||||
$image->resize($width, $height);
|
||||
} elseif ($width && !$height) {
|
||||
// Resize hanya berdasarkan width, tinggi menyesuaikan aspek rasio
|
||||
$image->scale(width: $width);
|
||||
} elseif (!$width && $height) {
|
||||
// Resize hanya berdasarkan height, lebar menyesuaikan aspek rasio
|
||||
$image->scale(height: $height);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!Storage::disk('public')->exists(dirname($resizedPath))) {
|
||||
Storage::disk('public')->makeDirectory(dirname($resizedPath));
|
||||
}
|
||||
|
||||
// Simpan dengan kualitas yang ditentukan
|
||||
$image->save($newFullPath, $quality);
|
||||
|
||||
Log::info("Image Service: Successfully resized {$originalPath} to {$resizedPath} with quality {$quality}%.");
|
||||
|
||||
return $resizedPath;
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Image Service: Resize failed for {$originalPath}. Error: " . $e->getMessage());
|
||||
return $originalPath; // Fallback ke gambar asli jika gagal
|
||||
}
|
||||
}
|
||||
}
|
||||
236
app/Services/ImportProgressService.php
Normal file
236
app/Services/ImportProgressService.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ImportProgressService
|
||||
{
|
||||
protected string $cacheKeyPrefix;
|
||||
protected int $cacheTtl;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->cacheKeyPrefix = config('import.slik.progress.cache_key', 'slik_import_progress');
|
||||
$this->cacheTtl = config('import.slik.progress.cache_ttl', 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start new import progress
|
||||
*
|
||||
* @param string $importId
|
||||
* @param int $userId
|
||||
* @param string $filename
|
||||
* @param int $totalRows
|
||||
* @return array
|
||||
*/
|
||||
public function start(string $importId, int $userId, string $filename, int $totalRows): array
|
||||
{
|
||||
$progressData = [
|
||||
'import_id' => $importId,
|
||||
'user_id' => $userId,
|
||||
'filename' => $filename,
|
||||
'total_rows' => $totalRows,
|
||||
'processed_rows' => 0,
|
||||
'skipped_rows' => 0,
|
||||
'error_rows' => 0,
|
||||
'status' => 'started',
|
||||
'percentage' => 0,
|
||||
'message' => 'Memulai import...',
|
||||
'started_at' => now(),
|
||||
'updated_at' => now()
|
||||
];
|
||||
|
||||
$cacheKey = $this->getCacheKey($importId);
|
||||
Cache::put($cacheKey, $progressData, $this->cacheTtl);
|
||||
|
||||
Log::info('ImportProgressService: Import started', $progressData);
|
||||
|
||||
return $progressData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update progress import
|
||||
*
|
||||
* @param string $importId
|
||||
* @param int $processedRows
|
||||
* @param int $skippedRows
|
||||
* @param int $errorRows
|
||||
* @param string|null $message
|
||||
* @return array
|
||||
*/
|
||||
public function update(string $importId, int $processedRows, int $skippedRows, int $errorRows, ?string $message = null): array
|
||||
{
|
||||
$cacheKey = $this->getCacheKey($importId);
|
||||
$progressData = Cache::get($cacheKey);
|
||||
|
||||
if (!$progressData) {
|
||||
Log::warning('ImportProgressService: Progress data not found', ['import_id' => $importId]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$totalRows = $progressData['total_rows'];
|
||||
$percentage = $totalRows > 0 ? round(($processedRows / $totalRows) * 100, 2) : 0;
|
||||
|
||||
$progressData = array_merge($progressData, [
|
||||
'processed_rows' => $processedRows,
|
||||
'skipped_rows' => $skippedRows,
|
||||
'error_rows' => $errorRows,
|
||||
'percentage' => $percentage,
|
||||
'message' => $message ?? "Memproses baris {$processedRows} dari {$totalRows}...",
|
||||
'updated_at' => now()
|
||||
]);
|
||||
|
||||
Cache::put($cacheKey, $progressData, $this->cacheTtl);
|
||||
|
||||
// Log progress setiap 10%
|
||||
if ($percentage % 10 === 0) {
|
||||
Log::info('ImportProgressService: Progress update', [
|
||||
'import_id' => $importId,
|
||||
'percentage' => $percentage,
|
||||
'processed' => $processedRows,
|
||||
'total' => $totalRows
|
||||
]);
|
||||
}
|
||||
|
||||
return $progressData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark import as completed
|
||||
*
|
||||
* @param string $importId
|
||||
* @param string|null $message
|
||||
* @return array
|
||||
*/
|
||||
public function complete(string $importId, ?string $message = null): array
|
||||
{
|
||||
$cacheKey = $this->getCacheKey($importId);
|
||||
$progressData = Cache::get($cacheKey);
|
||||
|
||||
if (!$progressData) {
|
||||
Log::warning('ImportProgressService: Progress data not found for completion', ['import_id' => $importId]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$progressData = array_merge($progressData, [
|
||||
'status' => 'completed',
|
||||
'percentage' => 100,
|
||||
'message' => $message ?? 'Import berhasil diselesaikan',
|
||||
'completed_at' => now(),
|
||||
'updated_at' => now()
|
||||
]);
|
||||
|
||||
Cache::put($cacheKey, $progressData, $this->cacheTtl);
|
||||
|
||||
Log::info('ImportProgressService: Import completed', [
|
||||
'import_id' => $importId,
|
||||
'total_rows' => $progressData['total_rows'],
|
||||
'processed_rows' => $progressData['processed_rows'],
|
||||
'skipped_rows' => $progressData['skipped_rows'],
|
||||
'error_rows' => $progressData['error_rows']
|
||||
]);
|
||||
|
||||
return $progressData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark import as failed
|
||||
*
|
||||
* @param string $importId
|
||||
* @param string $errorMessage
|
||||
* @return array
|
||||
*/
|
||||
public function fail(string $importId, string $errorMessage): array
|
||||
{
|
||||
$cacheKey = $this->getCacheKey($importId);
|
||||
$progressData = Cache::get($cacheKey);
|
||||
|
||||
if (!$progressData) {
|
||||
Log::warning('ImportProgressService: Progress data not found for failure', ['import_id' => $importId]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$progressData = array_merge($progressData, [
|
||||
'status' => 'failed',
|
||||
'message' => 'Import gagal: ' . $errorMessage,
|
||||
'failed_at' => now(),
|
||||
'updated_at' => now()
|
||||
]);
|
||||
|
||||
Cache::put($cacheKey, $progressData, $this->cacheTtl);
|
||||
|
||||
Log::error('ImportProgressService: Import failed', [
|
||||
'import_id' => $importId,
|
||||
'error' => $errorMessage,
|
||||
'progress_data' => $progressData
|
||||
]);
|
||||
|
||||
return $progressData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get progress data
|
||||
*
|
||||
* @param string $importId
|
||||
* @return array|null
|
||||
*/
|
||||
public function getProgress(string $importId): ?array
|
||||
{
|
||||
$cacheKey = $this->getCacheKey($importId);
|
||||
return Cache::get($cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active imports for user
|
||||
*
|
||||
* @param int $userId
|
||||
* @return array
|
||||
*/
|
||||
public function getUserImports(int $userId): array
|
||||
{
|
||||
$pattern = $this->cacheKeyPrefix . '_*';
|
||||
$keys = Cache::get($pattern);
|
||||
|
||||
$imports = [];
|
||||
foreach ($keys as $key) {
|
||||
$data = Cache::get($key);
|
||||
if ($data && $data['user_id'] === $userId) {
|
||||
$imports[] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
return $imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear progress data
|
||||
*
|
||||
* @param string $importId
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $importId): bool
|
||||
{
|
||||
$cacheKey = $this->getCacheKey($importId);
|
||||
$result = Cache::forget($cacheKey);
|
||||
|
||||
Log::info('ImportProgressService: Progress data cleared', ['import_id' => $importId]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cache key
|
||||
*
|
||||
* @param string $importId
|
||||
* @return string
|
||||
*/
|
||||
private function getCacheKey(string $importId): string
|
||||
{
|
||||
return $this->cacheKeyPrefix . '_' . $importId;
|
||||
}
|
||||
}
|
||||
83
app/Services/InspeksiCleanupService.php
Normal file
83
app/Services/InspeksiCleanupService.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Jobs\CleanupInspeksiDataJob;
|
||||
|
||||
/**
|
||||
* Service untuk membersihkan data inspeksi yang tidak memiliki dokument_id
|
||||
*
|
||||
* Class ini menyediakan method untuk menjalankan cleanup data inspeksi
|
||||
* ketika ada data baru dengan dokument_id yang sama
|
||||
*/
|
||||
class InspeksiCleanupService
|
||||
{
|
||||
/**
|
||||
* Dispatch job untuk cleanup data inspeksi
|
||||
*
|
||||
* @param int $permohonanId
|
||||
* @param int $createdBy
|
||||
* @param int|null $dokumentId
|
||||
* @param bool $sync
|
||||
* @return void
|
||||
*/
|
||||
public function cleanupInspeksiData(int $permohonanId, int $createdBy, ?int $dokumentId = null, bool $sync = false): void
|
||||
{
|
||||
Log::info('InspeksiCleanupService: Memulai cleanup data inspeksi', [
|
||||
'permohonan_id' => $permohonanId,
|
||||
'created_by' => $createdBy,
|
||||
'dokument_id' => $dokumentId,
|
||||
'sync' => $sync
|
||||
]);
|
||||
|
||||
try {
|
||||
$job = new CleanupInspeksiDataJob($permohonanId, $createdBy, $dokumentId);
|
||||
|
||||
if ($sync) {
|
||||
// Jalankan secara synchronous (langsung)
|
||||
$job->handle();
|
||||
Log::info('InspeksiCleanupService: Cleanup selesai dijalankan secara sync');
|
||||
} else {
|
||||
// Dispatch ke queue
|
||||
dispatch($job);
|
||||
Log::info('InspeksiCleanupService: Cleanup job berhasil di-dispatch ke queue');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('InspeksiCleanupService: Gagal menjalankan cleanup', [
|
||||
'permohonan_id' => $permohonanId,
|
||||
'created_by' => $createdBy,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch job untuk cleanup data inspeksi secara async (default)
|
||||
*
|
||||
* @param int $permohonanId
|
||||
* @param int $createdBy
|
||||
* @param int|null $dokumentId
|
||||
* @return void
|
||||
*/
|
||||
public function cleanupAsync(int $permohonanId, int $createdBy, ?int $dokumentId = null): void
|
||||
{
|
||||
$this->cleanupInspeksiData($permohonanId, $createdBy, $dokumentId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jalankan cleanup data inspeksi secara sync
|
||||
*
|
||||
* @param int $permohonanId
|
||||
* @param int $createdBy
|
||||
* @param int|null $dokumentId
|
||||
* @return void
|
||||
*/
|
||||
public function cleanupSync(int $permohonanId, int $createdBy, ?int $dokumentId = null): void
|
||||
{
|
||||
$this->cleanupInspeksiData($permohonanId, $createdBy, $dokumentId, true);
|
||||
}
|
||||
}
|
||||
736
app/Services/PreviewLaporanService.php
Normal file
736
app/Services/PreviewLaporanService.php
Normal file
@@ -0,0 +1,736 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Services;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Helpers\Lpj;
|
||||
use Modules\Lpj\Models\Denah;
|
||||
use Modules\Lpj\Models\Teams;
|
||||
use Modules\Lpj\Models\Branch;
|
||||
use Modules\Lpj\Models\Lantai;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Modules\Lpj\Models\Analisa;
|
||||
use Modules\Lpj\Models\Penilai;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
use Modules\Lpj\Models\Surveyor;
|
||||
use Modules\Lpj\Models\ViewUnit;
|
||||
use Modules\Location\Models\City;
|
||||
use Modules\Lpj\Models\JenisUnit;
|
||||
use Modules\Lpj\Models\Penilaian;
|
||||
use Modules\Lpj\Models\Perizinan;
|
||||
use Modules\Lpj\Models\BentukUnit;
|
||||
use Modules\Lpj\Models\JenisKapal;
|
||||
use Modules\Lpj\Models\LantaiUnit;
|
||||
use Modules\Lpj\Models\Lingkungan;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\PosisiUnit;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\AnalisaUnit;
|
||||
use Modules\Lpj\Models\BentukTanah;
|
||||
use Modules\Lpj\Models\FotoJaminan;
|
||||
use Modules\Lpj\Models\KonturTanah;
|
||||
use Modules\Lpj\Models\RuteJaminan;
|
||||
use Modules\Location\Models\Village;
|
||||
use Modules\Lpj\Models\AnalisaFakta;
|
||||
use Modules\Lpj\Models\JenisJaminan;
|
||||
use Modules\Lpj\Models\JenisPesawat;
|
||||
use Modules\Lpj\Models\ObjekJaminan;
|
||||
use Modules\Lpj\Models\SpekBangunan;
|
||||
use Modules\Lpj\Models\TerletakArea;
|
||||
use Modules\Location\Models\District;
|
||||
use Modules\Location\Models\Province;
|
||||
use Modules\Lpj\Models\ArahMataAngin;
|
||||
use Modules\Lpj\Models\JenisBangunan;
|
||||
use Modules\Lpj\Models\PosisiKavling;
|
||||
use Modules\Lpj\Models\SifatBangunan;
|
||||
use Modules\Lpj\Models\DokumenJaminan;
|
||||
use Modules\Lpj\Models\FasilitasObjek;
|
||||
use Modules\Lpj\Models\JenisKendaraan;
|
||||
use Modules\Lpj\Models\ModelAlatBerat;
|
||||
use Modules\Lpj\Models\KetinggianTanah;
|
||||
use Modules\Lpj\Models\KondisiBangunan;
|
||||
use Modules\Lpj\Models\LaporanExternal;
|
||||
use Modules\Lpj\Models\MerupakanDaerah;
|
||||
use Modules\Lpj\Models\PerkerasanJalan;
|
||||
use Modules\Lpj\Models\SaranaPelengkap;
|
||||
use Modules\Lpj\Models\TujuanPenilaian;
|
||||
use Modules\Lpj\Models\FotoObjekJaminan;
|
||||
use Modules\Lpj\Models\LaluLintasLokasi;
|
||||
use Modules\Lpj\Models\TingkatKeramaian;
|
||||
use Modules\Lpj\Models\AnalisaLingkungan;
|
||||
use Modules\Lpj\Models\KondisiFisikTanah;
|
||||
use Modules\Lpj\Models\AnalisaTanahBagunan;
|
||||
use Modules\Lpj\Models\GolonganMasySekitar;
|
||||
use Modules\Lpj\Models\SpekBangunanAnalisa;
|
||||
use Modules\Lpj\Models\DetailDokumenJaminan;
|
||||
use Modules\Lpj\Models\SpekKategoritBangunan;
|
||||
use Modules\Lpj\Http\Requests\SurveyorRequest;
|
||||
use Modules\Lpj\Models\HubunganPemilikJaminan;
|
||||
use Modules\Lpj\Models\HubunganPenghuniJaminan;
|
||||
use Modules\Lpj\Models\SpekBagunanAnalisaDetail;
|
||||
use Modules\Lpj\Jobs\SendJadwalKunjunganEmailJob;
|
||||
use Modules\Lpj\Http\Requests\FormSurveyorRequest;
|
||||
|
||||
class PreviewLaporanService
|
||||
{
|
||||
/**
|
||||
* Preview Laporan dan unduh foto terkait dengan logika fallback path.
|
||||
*
|
||||
* Menghasilkan PDF atau paket unduhan foto berdasarkan status laporan.
|
||||
* Jika file foto asli tidak ditemukan dan status LPJ adalah 1, maka
|
||||
* sistem akan mencoba menggunakan fallback path dengan pola
|
||||
* `surveyor/001/{lastTwoParts}` untuk meminimalisir gambar hilang.
|
||||
*
|
||||
* @param int $permohonan_id ID Permohonan
|
||||
* @param int $dokumen_id ID Dokumen
|
||||
* @param int $jaminan_id ID Jaminan
|
||||
* @param string $back URL atau rute kembali
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function previewLaporan($permohonan_id, $dokumen_id, $jaminan_id, $back)
|
||||
{
|
||||
$permohonan = $this->getPermohonanJaminanId(
|
||||
$permohonan_id,
|
||||
$dokumen_id,
|
||||
$jaminan_id
|
||||
);
|
||||
// $tipeLaporanResponse = $this->checkPrintOutLaporan($permohonan_id, $document_id);
|
||||
// $tipeLaporan = $tipeLaporanResponse->getData();
|
||||
|
||||
// if (!$tipeLaporan->status) {
|
||||
// return redirect()->back()->with('error', 'Laporan tidak valid');
|
||||
// }
|
||||
$basicData = $this->getCommonData();
|
||||
|
||||
$inspeksi = Inspeksi::where('permohonan_id', $permohonan_id)->where('dokument_id', $dokumen_id)->first();
|
||||
$lpj = Penilai::where('permohonan_id', $permohonan_id)->where('dokument_id', $dokumen_id)->first();
|
||||
|
||||
$mig_permohonan = json_decode($permohonan->mig_permohonan);
|
||||
|
||||
$nomorLaporan = getNomorLaporan($permohonan_id, $dokumen_id);
|
||||
$tanggalLaporan = $permohonan->is_mig
|
||||
? ($mig_permohonan->mig_mst_jaminan_tgl_laporan ?? $mig_permohonan->mig_mst_jaminan_tgl_create ?? null)
|
||||
: ($lpj->created_at ?? null);
|
||||
$forminspeksi = null;
|
||||
$lpjData = null;
|
||||
$formFoto = null;
|
||||
|
||||
// if ($inspeksi) {
|
||||
$forminspeksi = json_decode($inspeksi?->data_form, true) ?? null;
|
||||
|
||||
$formFoto = json_decode($inspeksi?->foto_form, true) ?? null;
|
||||
// $denahForm = json_decode($data->denah_form, true);
|
||||
$dataPembanding = json_decode($inspeksi?->data_pembanding, true) ?? null;
|
||||
// }
|
||||
|
||||
// if ($lpj) {
|
||||
$lpjData = json_decode($lpj?->lpj, true) ?? null;
|
||||
$memo = json_decode($lpj?->memo, true) ?? null;
|
||||
$resumeData = json_decode($lpj?->resume, true) ?? null;
|
||||
$rap = json_decode($lpj?->rap, true);
|
||||
$report = json_decode($lpj?->call_report, true) ?? null;
|
||||
// }
|
||||
|
||||
$inputAddress = $forminspeksi['asset']['alamat']['sesuai'] ?? $forminspeksi['asset']['alamat']['tidak sesuai'] ?? [];
|
||||
|
||||
$alamat = [
|
||||
'address' => $inputAddress['address'] ?? null,
|
||||
'village_code' => getWilayahName($inputAddress['village_code'] ?? null, 'village'),
|
||||
'district_code' => getWilayahName($inputAddress['district_code'] ?? null, 'district'),
|
||||
'city_code' => getWilayahName($inputAddress['city_code'] ?? null, 'city'),
|
||||
'province_code' => getWilayahName($inputAddress['province_code'] ?? null, 'province')
|
||||
];
|
||||
|
||||
$statusLpj = 0;
|
||||
|
||||
// $viewLaporan = $this->getViewLaporan($tipeLaporan);
|
||||
return view('lpj::component.show-laporan-inspeksi', compact('permohonan', 'basicData', 'forminspeksi', 'alamat', 'lpjData', 'memo', 'resumeData', 'rap', 'report', 'lpj', 'formFoto', 'nomorLaporan', 'tanggalLaporan', 'dataPembanding', 'inspeksi', 'statusLpj', 'permohonan_id', 'back', ));
|
||||
}
|
||||
|
||||
public function printOutLaporan($permohonan_id, $document_id, $jaminan_id)
|
||||
{
|
||||
$tipeLaporanResponse = $this->checkPrintOutLaporan($permohonan_id, $document_id);
|
||||
$tipeLaporan = $tipeLaporanResponse->getData();
|
||||
|
||||
//dd($permohonan_id, $document_id, $tipeLaporan);
|
||||
|
||||
if (!$tipeLaporan->status) {
|
||||
//return redirect()->back()->with('error', 'Laporan tidak valid');
|
||||
}
|
||||
$permohonan = $this->getPermohonanJaminanId(
|
||||
$permohonan_id,
|
||||
$document_id,
|
||||
$jaminan_id
|
||||
);
|
||||
|
||||
$basicData = $this->getCommonData();
|
||||
|
||||
$inspeksi = Inspeksi::where('permohonan_id', $permohonan_id)->where('dokument_id', $document_id)->first();
|
||||
$lpj = Penilai::where('permohonan_id', $permohonan_id)->first(); //->where('dokument_id', $document_id)->first();
|
||||
|
||||
$mig_permohonan = json_decode($permohonan->mig_permohonan);
|
||||
$nomorLaporan = getNomorLaporan($permohonan_id, $document_id);
|
||||
|
||||
//Carbon::createFromFormat('d/m/Y H:i:s', $mig_permohonan->mig_mst_jaminan_tgl_laporan)->format('Y-m-d H:i:s');
|
||||
|
||||
$tanggalLaporan = $permohonan->is_mig
|
||||
? ($mig_permohonan->mig_mst_jaminan_tgl_laporan
|
||||
? Carbon::createFromFormat('d/m/Y H:i:s', $mig_permohonan->mig_mst_jaminan_tgl_laporan)->format('Y-m-d H:i:s')
|
||||
: ($mig_permohonan->mig_mst_jaminan_tgl_oto
|
||||
? Carbon::createFromFormat('d/m/Y H:i:s', $mig_permohonan->mig_mst_jaminan_tgl_oto)->format('Y-m-d H:i:s')
|
||||
: null))
|
||||
: ($lpj->created_at ?? null);
|
||||
|
||||
|
||||
$forminspeksi = null;
|
||||
$lpjData = null;
|
||||
$formFoto = null;
|
||||
|
||||
$dataPembanding ='';
|
||||
if ($inspeksi) {
|
||||
$forminspeksi = json_decode($inspeksi->data_form, true);
|
||||
$formFoto = json_decode($inspeksi->foto_form, true);
|
||||
// $denahForm = json_decode($data->denah_form, true);
|
||||
$dataPembanding = json_decode($inspeksi->data_pembanding, true);
|
||||
}
|
||||
|
||||
|
||||
if ($lpj) {
|
||||
$lpjData = json_decode($lpj->lpj, true);
|
||||
$memo = json_decode($lpj->memo, true);
|
||||
$resumeData = json_decode($lpj->resume, true);
|
||||
$rap = json_decode($lpj->rap, true);
|
||||
$report = json_decode($lpj->call_report, true);
|
||||
}
|
||||
|
||||
$inputAddress = $forminspeksi['asset']['alamat']['sesuai'] ?? $forminspeksi['asset']['alamat']['tidak sesuai'] ?? [];
|
||||
|
||||
$inputAddress = $forminspeksi['asset']['alamat']['sesuai'] ?? $forminspeksi['asset']['alamat']['tidak sesuai'] ?? [];
|
||||
$alamat = [
|
||||
'address' => $inputAddress['address'] ?? null,
|
||||
'village_code' => getWilayahName($inputAddress['village_code'] ?? null, 'village'),
|
||||
'district_code' => getWilayahName($inputAddress['district_code'] ?? null, 'district'),
|
||||
'city_code' => getWilayahName($inputAddress['city_code'] ?? null, 'city'),
|
||||
'province_code' => getWilayahName($inputAddress['province_code'] ?? null, 'province')
|
||||
];
|
||||
|
||||
$viewLaporan = $this->getViewLaporan($tipeLaporan->status);
|
||||
|
||||
$statusLpj = 1;
|
||||
|
||||
$mig_permohonan = json_decode($permohonan->mig_permohonan);
|
||||
$nilaiPasar = $mig_permohonan->mig_mst_lpj_tot_nilai_pasar ?? null;
|
||||
|
||||
if(($tipeLaporan->status === 'memo' && $permohonan->mig_permohonan && $permohonan->is_mig) || ($nilaiPasar !== null && $nilaiPasar < 1 && $permohonan->is_mig)){
|
||||
$paths = $formFoto['upload_foto'] ?? null;
|
||||
|
||||
if (!is_array($paths) || empty($paths)) {
|
||||
return response()->json(['error' => 'No files to download'], 404);
|
||||
}
|
||||
|
||||
$files = [];
|
||||
foreach ($paths as $path) {
|
||||
if (!$path['path']) {
|
||||
Log::warning('PreviewLaporanService: Path kosong terdeteksi dalam daftar paths.');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Logika fallback untuk path file
|
||||
$originalPath = $path['path'];
|
||||
$fallbackPath = null;
|
||||
|
||||
// Jika file asli tidak ditemukan, buat fallback path
|
||||
if ($statusLpj == 1) {
|
||||
$fullOriginalPath = storage_path('app/public/' . $originalPath);
|
||||
|
||||
if (!file_exists($fullOriginalPath)) {
|
||||
// Ekstrak bagian akhir path (contoh: 251051/251051_2_2.png)
|
||||
$pathParts = explode('/', $originalPath);
|
||||
if (count($pathParts) >= 2) {
|
||||
$lastTwoParts = array_slice($pathParts, -2);
|
||||
$fallbackPath = 'surveyor/001/' . implode('/', $lastTwoParts);
|
||||
Log::info('PreviewLaporanService: Menggunakan fallback path kandidat.', [
|
||||
'original' => $originalPath,
|
||||
'fallback' => $fallbackPath,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tentukan path yang akan digunakan
|
||||
$pathToUse = ($fallbackPath && $statusLpj == 1 && file_exists(storage_path('app/public/' . $fallbackPath)))
|
||||
? $fallbackPath
|
||||
: $originalPath;
|
||||
|
||||
$fullPath = storage_path('app/public/' . $pathToUse);
|
||||
if (!file_exists($fullPath)) {
|
||||
Log::warning('PreviewLaporanService: File tidak ditemukan untuk original maupun fallback.', [
|
||||
'original' => $originalPath,
|
||||
'fallback' => $fallbackPath,
|
||||
'resolved' => $pathToUse,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log::info('PreviewLaporanService: Menambahkan file untuk diunduh.', [
|
||||
'resolved' => $pathToUse,
|
||||
'fullPath' => $fullPath,
|
||||
]);
|
||||
$files[] = $fullPath;
|
||||
}
|
||||
|
||||
if (empty($files)) {
|
||||
Log::warning('PreviewLaporanService: Tidak ada file valid ditemukan setelah resolusi path.');
|
||||
return response()->json(['error' => 'No valid files found'], 404);
|
||||
}
|
||||
|
||||
// For single file, download directly
|
||||
if (count($files) === 1) {
|
||||
Log::info('PreviewLaporanService: Mengunduh single file.', ['file' => $files[0]]);
|
||||
return response()->download($files[0]);
|
||||
}
|
||||
|
||||
// For multiple files, create zip and download
|
||||
$zipName = 'photos_' . time() . '.zip';
|
||||
$zipPath = storage_path('app/public/' . $zipName);
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
if ($zip->open($zipPath, \ZipArchive::CREATE) === true) {
|
||||
foreach ($files as $file) {
|
||||
$zip->addFile($file, basename($file));
|
||||
}
|
||||
$zip->close();
|
||||
Log::info('PreviewLaporanService: Zip file berhasil dibuat.', ['zip' => $zipPath, 'count' => count($files)]);
|
||||
return response()->download($zipPath)->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
Log::error('PreviewLaporanService: Gagal membuat zip file.');
|
||||
return response()->json(['error' => 'Failed to create zip file'], 500);
|
||||
}
|
||||
try {
|
||||
$pdf = $this->generatePDF($viewLaporan, compact(
|
||||
'permohonan',
|
||||
'forminspeksi',
|
||||
'lpjData',
|
||||
'formFoto',
|
||||
'basicData',
|
||||
'inspeksi',
|
||||
'lpj',
|
||||
'statusLpj',
|
||||
'alamat',
|
||||
'dataPembanding',
|
||||
'nomorLaporan',
|
||||
'memo',
|
||||
'resumeData',
|
||||
'tanggalLaporan',
|
||||
'rap',
|
||||
'report'
|
||||
));
|
||||
|
||||
$cleanNomorLaporan = str_replace(['/', '\\'], '-', $nomorLaporan);
|
||||
$filename = 'Laporan_' . $tipeLaporan->status . '_' . $permohonan->debiture->name . '_' . $cleanNomorLaporan;
|
||||
|
||||
return $pdf->download($filename . '_data.pdf');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('PDF generation failed: ' . $e->getMessage());
|
||||
return response()->json(['error' => 'Failed to generate PDF. Please check the log for details.'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
private function generatePDF(string $viewLaporan, array $data)
|
||||
{
|
||||
//return view('lpj::' . $viewLaporan, $data);
|
||||
|
||||
$pdf = PDF::loadView('lpj::' . $viewLaporan, $data);
|
||||
$pdf->setPaper('A4', 'portrait');
|
||||
$pdf->set_option('isHtml5ParserEnabled', true);
|
||||
$pdf->set_option('isPhpEnabled', true);
|
||||
$pdf->set_option('dpi', '96');
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
|
||||
private function getViewLaporan($tipe)
|
||||
{
|
||||
$viewMap = [
|
||||
'sederhana' => 'penilai.components.print-out-sederhana',
|
||||
'standar' => 'penilai.components.print-out-standar',
|
||||
'resume' => 'penilai.components.print-resume',
|
||||
'memo' => 'penilai.components.print-memo',
|
||||
'rap' => 'penilai.components.print-out-rap',
|
||||
'call-report' => 'penilai.components.print-out-call-report'
|
||||
];
|
||||
return $viewMap[$tipe] ?? 'penilai.components.print-resume';
|
||||
}
|
||||
|
||||
public function checkPrintOutLaporan($permohonan_id, $dokumen_id)
|
||||
{
|
||||
|
||||
// Ambil data berdasarkan ID
|
||||
$statusLpj = Penilai::where('permohonan_id', $permohonan_id)
|
||||
//->where('dokument_id', $dokumen_id)
|
||||
->first();
|
||||
|
||||
|
||||
$permohonan = Permohonan::where('id', $permohonan_id)->first();
|
||||
|
||||
// Jika data tidak ditemukan, kembalikan status null
|
||||
if (!$statusLpj) {
|
||||
return response()->json(['status' => null]);
|
||||
}
|
||||
|
||||
// Tentukan tipe berdasarkan kondisi
|
||||
$type = $statusLpj->type_penilai ?? null;
|
||||
|
||||
if ($type === 'memo' && $permohonan->is_mig!=1) {
|
||||
return $this->checkDataMemo($type, $statusLpj);
|
||||
}
|
||||
|
||||
if ($type === 'resume') {
|
||||
return $this->checkDataResume($type, $statusLpj);
|
||||
}
|
||||
|
||||
|
||||
if ($type === 'standar' || $type === 'sederhana') {
|
||||
return $this->checkDataLpj($type, $statusLpj);
|
||||
}
|
||||
|
||||
if ($type === 'rap') {
|
||||
return $this->checkDataRap($type, $statusLpj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Kembalikan respons dengan tipe yang sesuai
|
||||
return response()->json(['status' => $type]);
|
||||
}
|
||||
|
||||
public function checkDataMemo($type, $statusLpj)
|
||||
{
|
||||
// Ambil data JSON dari statusLpj
|
||||
$data = json_decode($statusLpj->memo, true) ?? [];
|
||||
|
||||
|
||||
$validationRules = [
|
||||
'memo' => [
|
||||
'kepada',
|
||||
'dari',
|
||||
'nomor_memo',
|
||||
'tanggal',
|
||||
'perihal',
|
||||
'jenis_asset_tidak_sesuai',
|
||||
'lokasi.lokasi',
|
||||
'lokasi.province_code',
|
||||
'lokasi.city_code',
|
||||
'lokasi.district_code',
|
||||
'lokasi.village_code',
|
||||
'lokasi.penilai',
|
||||
'terlampir',
|
||||
'hasil_survey',
|
||||
'kesimpulan_saran',
|
||||
],
|
||||
];
|
||||
|
||||
// Validasi data JSON
|
||||
if (isset($validationRules[$type])) {
|
||||
$missingFields = [];
|
||||
|
||||
foreach ($validationRules[$type] as $field) {
|
||||
$keys = explode('.', $field);
|
||||
$value = $data;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($value[$key])) {
|
||||
$missingFields[] = $field;
|
||||
break;
|
||||
}
|
||||
$value = $value[$key];
|
||||
}
|
||||
}
|
||||
|
||||
// Jika ada field yang kosong, kembalikan error
|
||||
if (!empty($missingFields)) {
|
||||
return response()->json([
|
||||
'status' => null,
|
||||
'message' => "Silahkan lengkapi data memo terlebih dahulu.",
|
||||
'missing_fields' => $missingFields,
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
// Jika data valid
|
||||
return response()->json([
|
||||
'status' => $type,
|
||||
'message' => "Data memo valid.",
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function checkDataResume($type, $statusLpj)
|
||||
{
|
||||
// Ambil data JSON dari statusLpj
|
||||
$data = json_decode($statusLpj->resume, true) ?? [];
|
||||
|
||||
$validationRules = [
|
||||
'resume' => [
|
||||
'fisik'
|
||||
],
|
||||
];
|
||||
|
||||
// Validasi data JSON
|
||||
if (isset($validationRules[$type])) {
|
||||
$missingFields = [];
|
||||
|
||||
foreach ($validationRules[$type] as $field) {
|
||||
$keys = explode('.', $field);
|
||||
$value = $data;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($value[$key])) {
|
||||
$missingFields[] = $field;
|
||||
break;
|
||||
}
|
||||
$value = $value[$key];
|
||||
}
|
||||
|
||||
// Validasi khusus untuk array fisik dan sesuai_imb
|
||||
if ($field === 'fisik' || $field === 'sesuai_imb') {
|
||||
if (empty($value) || !is_array($value)) {
|
||||
$missingFields[] = $field;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validasi struktur data di dalam array
|
||||
foreach ($value as $item) {
|
||||
$requiredKeys = ['sertifikat', 'luas_tanah', 'nilai'];
|
||||
foreach ($requiredKeys as $requiredKey) {
|
||||
if (!isset($item[$requiredKey])) {
|
||||
$missingFields[] = $field . '.' . $requiredKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Jika ada field yang kosong, kembalikan error
|
||||
if (!empty($missingFields)) {
|
||||
return response()->json([
|
||||
'status' => null,
|
||||
'message' => "Silahkan lengkapi data resume terlebih dahulu.",
|
||||
'missing_fields' => $missingFields,
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
// Jika data valid
|
||||
return response()->json([
|
||||
'status' => $type,
|
||||
'message' => "Data resume valid.",
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkDataLpj($type, $statusLpj)
|
||||
{
|
||||
// Ambil data JSON dari statusLpj
|
||||
$data = json_decode($statusLpj->lpj, true) ?? [];
|
||||
|
||||
$validationRules = [
|
||||
'lpj' => [
|
||||
'luas_tanah',
|
||||
'nilai_tanah_1',
|
||||
'nilai_tanah_2',
|
||||
'luas_bangunan',
|
||||
'nilai_bangunan_1',
|
||||
'nilai_bangunan_2',
|
||||
'total_nilai_pasar_wajar',
|
||||
'likuidasi',
|
||||
'likuidasi_nilai_1',
|
||||
'likuidasi_nilai_2',
|
||||
'asuransi_luas_bangunan',
|
||||
'asuransi_nilai_1',
|
||||
'asuransi_nilai_2',
|
||||
'npw_tambahan'
|
||||
],
|
||||
];
|
||||
|
||||
// Validasi data JSON
|
||||
if (isset($validationRules[$type])) {
|
||||
$missingFields = [];
|
||||
|
||||
foreach ($validationRules[$type] as $field) {
|
||||
// Penanganan khusus untuk field yang boleh null
|
||||
if (in_array($field, ['sarana_pelengkap_penilai', 'nilai_sarana_pelengkap_1', 'nilai_sarana_pelengkap_2'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($data[$field])) {
|
||||
$missingFields[] = $field;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validasi khusus untuk npw_tambahan
|
||||
if ($field === 'npw_tambahan' && is_array($data[$field])) {
|
||||
foreach ($data[$field] as $index => $item) {
|
||||
$requiredKeys = ['name', 'luas', 'nilai_1', 'nilai_2'];
|
||||
foreach ($requiredKeys as $key) {
|
||||
if (!isset($item[$key])) {
|
||||
$missingFields[] = "npw_tambahan[$index].$key";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Jika ada field yang kosong, kembalikan error
|
||||
if (!empty($missingFields)) {
|
||||
return response()->json([
|
||||
'status' => null,
|
||||
'message' => "Silahkan lengkapi data LPJ terlebih dahulu.",
|
||||
'missing_fields' => $missingFields,
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
// Jika data valid
|
||||
return response()->json([
|
||||
'status' => $type,
|
||||
'message' => "Data LPJ valid.",
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkDataRap($type, $statusLpj)
|
||||
{
|
||||
// Ambil data JSON dari statusLpj
|
||||
$data = json_decode($statusLpj->rap, true) ?? [];
|
||||
|
||||
$requiredFields = [
|
||||
'dari',
|
||||
'kepada',
|
||||
'perihal',
|
||||
'tanggal',
|
||||
'nomor_rap'
|
||||
];
|
||||
|
||||
// Cek apakah ada field yang kosong
|
||||
$missingFields = [];
|
||||
foreach ($requiredFields as $field) {
|
||||
if (!isset($data[$field]) || empty($data[$field])) {
|
||||
$missingFields[] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
// Jika ada field yang kosong, kembalikan error
|
||||
if (!empty($missingFields)) {
|
||||
return response()->json([
|
||||
'status' => null,
|
||||
'message' => "Silahkan lengkapi data RAP terlebih dahulu.",
|
||||
'missing_fields' => $missingFields
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Jika semua data terisi
|
||||
return response()->json([
|
||||
'status' => $type,
|
||||
'message' => "Data RAP valid."
|
||||
]);
|
||||
}
|
||||
private function getPermohonanJaminanId($id, $dokumentId, $jaminanId)
|
||||
{
|
||||
return Permohonan::with([
|
||||
'user',
|
||||
'debiture.province',
|
||||
'debiture.city',
|
||||
'debiture.district',
|
||||
'debiture.village',
|
||||
'branch',
|
||||
'tujuanPenilaian',
|
||||
'penilaian',
|
||||
'penawaran',
|
||||
'debiture.documents' => function ($query) use ($dokumentId, $jaminanId) {
|
||||
$query->where('id', $dokumentId)
|
||||
->where('jenis_jaminan_id', $jaminanId);
|
||||
}
|
||||
])->findOrFail($id);
|
||||
}
|
||||
|
||||
public function getCommonData()
|
||||
{
|
||||
return [
|
||||
'branches' => Branch::all(),
|
||||
'bentukTanah' => BentukTanah::all(),
|
||||
'konturTanah' => KonturTanah::all(),
|
||||
'posisiKavling' => PosisiKavling::all(),
|
||||
'ketinggianTanah' => KetinggianTanah::all(),
|
||||
'kondisiFisikTanah' => KondisiFisikTanah::all(),
|
||||
'jenisBangunan' => JenisBangunan::all(),
|
||||
'kondisiBangunan' => KondisiBangunan::all(),
|
||||
'sifatBangunan' => SifatBangunan::all(),
|
||||
'spekKategoriBangunan' => SpekKategoritBangunan::all(),
|
||||
'spekBangunan' => SpekBangunan::all(),
|
||||
'saranaPelengkap' => SaranaPelengkap::all(),
|
||||
'arahMataAngin' => ArahMataAngin::all(),
|
||||
'lantai' => Lantai::all(),
|
||||
'viewUnit' => ViewUnit::all(),
|
||||
'golMasySekitar' => GolonganMasySekitar::all(),
|
||||
'tingkatKeramaian' => TingkatKeramaian::all(),
|
||||
'laluLintasLokasi' => LaluLintasLokasi::all(),
|
||||
'jenisPesawat' => JenisPesawat::all(),
|
||||
'modelAlatBerat' => ModelAlatBerat::all(),
|
||||
'jenisKapal' => JenisKapal::all(),
|
||||
'jenisKendaraan' => JenisKendaraan::all(),
|
||||
'terletakArea' => TerletakArea::all(),
|
||||
'posisiUnit' => PosisiUnit::all(),
|
||||
'bentukUnit' => BentukUnit::all(),
|
||||
'fasilitasObjek' => FasilitasObjek::all(),
|
||||
'merupakanDaerah' => MerupakanDaerah::all(),
|
||||
'jenisUnit' => JenisUnit::all(),
|
||||
'jenisJaminan' => JenisJaminan::all(),
|
||||
'hubCadeb' => HubunganPemilikJaminan::all(),
|
||||
'hubPenghuni' => HubunganPenghuniJaminan::all(),
|
||||
'perkerasanJalan' => PerkerasanJalan::all(),
|
||||
'terletakDiArea' => TerletakArea::all(),
|
||||
'tujuanPenilaian' => TujuanPenilaian::all(),
|
||||
'perizinan' => Perizinan::all(),
|
||||
'foto' => FotoObjekJaminan::all()
|
||||
];
|
||||
}
|
||||
|
||||
private const HEADERS = [
|
||||
'bentuk-tanah' => ['Bentuk Tanah', 'bentuk-tanah'],
|
||||
'kontur-tanah' => ['Kontur Tanah', 'kontur-tanah'],
|
||||
'posisi-kavling' => ['Posisi Kavling', 'posisi-kavling'],
|
||||
'ketinggian-tanah' => ['Ketinggian Tanah', 'ketinggian-tanah'],
|
||||
'kondisi-fisik-tanah' => ['Kondisi Fisik Tanah', 'kondisi-fisik-tanah'],
|
||||
'jenis-bangunan' => ['Jenis Bangunan', 'jenis-bangunan'],
|
||||
'kondisi-bangunan' => ['Kondisi Bangunan', 'kondisi-bangunan'],
|
||||
'sifat-bangunan' => ['Sifat Bangunan', 'sifat-bangunan'],
|
||||
'sarana-pelengkap' => ['Sarana Pelengkap', 'sarana-pelengkap'],
|
||||
'lalu-lintas-lokasi' => ['Lalu Lintas Depan Lokasi', 'lalu-lintas-lokasi'],
|
||||
'tingkat-keramaian' => ['Tingkat Keramaian', 'tingkat-keramaian'],
|
||||
'gol-mas-sekitar' => ['Golongan Masyarakat Sekitar', 'gol-mas-sekitar'],
|
||||
'spek-kategori-bangunan' => ['Spek Kategori Bangunan', 'spek-kategori-bangunan'],
|
||||
'spek-bangunan' => ['Spek Bangunan', 'spek-bangunan'],
|
||||
'lantai-unit' => ['Lantai Unit', 'lantai-unit'],
|
||||
'view-unit' => ['View Unit', 'view-unit'],
|
||||
'perkerasan-jalan' => ['Perkerasan jalan', 'perkerasan-jalan'],
|
||||
'jenis-pesawat' => ['Jenis pesawat', 'jenis-pesawat'],
|
||||
'model-alat-berat' => ['Model alat berat', 'model-alat-berat'],
|
||||
'jenis-kapal' => ['Jenis kapal', 'jenis-kapal'],
|
||||
'jenis-kendaraan' => ['Jenis kendaraan', 'jenis-kendaraan'],
|
||||
'jenis-unit' => ['Jenis unit', 'jenis-unit'],
|
||||
'terletak-area' => ['Terletak di Area', 'terletak-area'],
|
||||
'merupakan-daerah' => ['Merupakan Daerah', 'merupakan-daerah'],
|
||||
'posisi-unit' => ['Posisi unit', 'posisi-unit'],
|
||||
'bentuk-unit' => ['Bentuk unit', 'bentuk-unit'],
|
||||
'fasilitas-objek' => ['Fasilitas Umum Dekat Objek', 'fasilitas-objek'],
|
||||
'foto-objek-jaminan' => ['Foto Objek Jaminan', 'foto-objek-jaminan'],
|
||||
'perizinan' => ['Perizinan', 'perizinan'],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -450,8 +450,6 @@ class SaveFormInspesksiService
|
||||
|
||||
$data['perizinan'] = $perizinanData;
|
||||
|
||||
|
||||
|
||||
$partisiResult = [];
|
||||
if (isset($data['partisi'])) {
|
||||
foreach ($data['partisi'] as $name => $values) {
|
||||
|
||||
107
app/Services/TypeLaporanService.php
Normal file
107
app/Services/TypeLaporanService.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Services;
|
||||
|
||||
class TypeLaporanService
|
||||
{
|
||||
public function handleRequest($type, $permohonanId, $documentId, $jaminanId, $surveyorController)
|
||||
{
|
||||
$permohonan = $this->getPermohonanData($surveyorController, $permohonanId, $documentId, $jaminanId);
|
||||
|
||||
if ($permohonan->status == 'proses-laporan') {
|
||||
throw new \Exception('Masih dalam proses laporan');
|
||||
}
|
||||
|
||||
$nomorLaporan = $this->generateNomorLaporan($surveyorController, $permohonan, $documentId, $type);
|
||||
$inspeksi = $this->getInspeksi($permohonanId, $documentId);
|
||||
$this->updatePenilai($permohonanId, $documentId, $type);
|
||||
|
||||
$debitur = Debiture::find($permohonan->debiture_id);
|
||||
$forminspeksi = $inspeksi ? json_decode($inspeksi->data_form, true) : null;
|
||||
|
||||
$locationCodes = $this->getAlamatData($debitur, $forminspeksi);
|
||||
$locationData = $this->getLocationData(
|
||||
$locationCodes['provinceCode'],
|
||||
$locationCodes['cityCode'],
|
||||
$locationCodes['districtCode'],
|
||||
$forminspeksi
|
||||
);
|
||||
|
||||
return [
|
||||
'permohonan' => $permohonan,
|
||||
'nomorLaporan' => $nomorLaporan,
|
||||
'basicData' => $surveyorController->getCommonData(),
|
||||
'cities' => $locationData['cities'],
|
||||
'districts' => $locationData['districts'],
|
||||
'villages' => $locationData['villages'],
|
||||
'forminspeksi' => $forminspeksi,
|
||||
];
|
||||
}
|
||||
|
||||
private function getPermohonanData($surveyorController, $permohonanId, $documentId, $jaminanId)
|
||||
{
|
||||
return $surveyorController->getPermohonanData($permohonanId, $documentId, $jaminanId);
|
||||
}
|
||||
|
||||
private function generateNomorLaporan($surveyorController, $permohonan, $documentId, $type)
|
||||
{
|
||||
return $surveyorController->generateNomorLaporan($permohonan, $documentId, $type);
|
||||
}
|
||||
|
||||
private function getInspeksi($permohonanId, $documentId)
|
||||
{
|
||||
return Inspeksi::where('permohonan_id', $permohonanId)
|
||||
->where('document_id', $documentId)
|
||||
->first();
|
||||
}
|
||||
|
||||
private function updatePenilai($permohonanId, $documentId, $type)
|
||||
{
|
||||
// Logika untuk memperbarui data berdasarkan tipe laporan
|
||||
}
|
||||
|
||||
private function getAlamatData($debitur, $forminspeksi)
|
||||
{
|
||||
return [
|
||||
'provinceCode' => $debitur->province_id ?? $forminspeksi['province'],
|
||||
'cityCode' => $debitur->city_id ?? $forminspeksi['city'],
|
||||
'districtCode' => $debitur->district_id ?? $forminspeksi['district'],
|
||||
];
|
||||
}
|
||||
|
||||
private function getLocationData($provinceCode, $cityCode, $districtCode, $forminspeksi)
|
||||
{
|
||||
return [
|
||||
'cities' => $this->fetchCityData($provinceCode),
|
||||
'districts' => $this->fetchDistrictData($cityCode),
|
||||
'villages' => $this->fetchVillageData($districtCode),
|
||||
];
|
||||
}
|
||||
|
||||
private function fetchCityData($provinceCode)
|
||||
{
|
||||
return City::where('province_code', $provinceCode)->get();
|
||||
}
|
||||
private function fetchDistrictData($cityCode)
|
||||
{
|
||||
return District::where('city_code', $cityCode)->get();
|
||||
}
|
||||
private function fetchVillageData($districtCode)
|
||||
{
|
||||
return Village::where('district_code', $districtCode)->get();
|
||||
}
|
||||
|
||||
private function updateOrCreatePenilai($permohonan_id, $document_id, $type, $data)
|
||||
{
|
||||
return Penilai::updateOrCreate(
|
||||
[
|
||||
'permohonan_id' => $permohonan_id,
|
||||
'dokument_id' => $document_id,
|
||||
|
||||
],
|
||||
[
|
||||
$type => json_encode($data),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,62 @@
|
||||
|
||||
return [
|
||||
'name' => 'Lpj',
|
||||
'import' => [
|
||||
'slik' => [
|
||||
// Memory limit untuk import (dalam MB)
|
||||
'memory_limit' => env('SLIK_IMPORT_MEMORY_LIMIT', 1024),
|
||||
|
||||
// Ukuran chunk untuk processing (jumlah baris per chunk)
|
||||
'chunk_size' => env('SLIK_IMPORT_CHUNK_SIZE', 50),
|
||||
|
||||
// Ukuran batch untuk database insert
|
||||
'batch_size' => env('SLIK_IMPORT_BATCH_SIZE', 50),
|
||||
|
||||
// Timeout untuk import (dalam detik)
|
||||
'timeout' => env('SLIK_IMPORT_TIMEOUT', 1800), // 30 menit untuk file besar
|
||||
|
||||
// Maksimum file size yang diizinkan (dalam MB)
|
||||
'max_file_size' => env('SLIK_IMPORT_MAX_FILE_SIZE', 50),
|
||||
|
||||
// Enable garbage collection untuk optimasi memory
|
||||
'enable_gc' => env('SLIK_IMPORT_ENABLE_GC', true),
|
||||
|
||||
// Enable progress logging
|
||||
'enable_progress_logging' => env('SLIK_IMPORT_ENABLE_PROGRESS_LOGGING', true),
|
||||
|
||||
// Enable detailed error logging
|
||||
'enable_error_logging' => env('SLIK_IMPORT_ENABLE_ERROR_LOGGING', true),
|
||||
|
||||
// XML Scanner settings untuk optimasi memory
|
||||
'xml_scanner' => [
|
||||
'timeout' => env('SLIK_XML_SCANNER_TIMEOUT', 1800), // 30 menit
|
||||
'memory_limit' => env('SLIK_XML_SCANNER_MEMORY_LIMIT', 1024), // 1GB
|
||||
'chunk_size' => env('SLIK_XML_SCANNER_CHUNK_SIZE', 50), // Lebih kecil untuk XML
|
||||
],
|
||||
|
||||
// Queue processing untuk file besar
|
||||
'queue' => [
|
||||
'enabled' => env('SLIK_IMPORT_QUEUE_ENABLED', false),
|
||||
'connection' => env('SLIK_IMPORT_QUEUE_CONNECTION', 'database'),
|
||||
'queue_name' => env('SLIK_IMPORT_QUEUE_NAME', 'imports'),
|
||||
'chunk_size' => env('SLIK_IMPORT_QUEUE_CHUNK_SIZE', 500),
|
||||
],
|
||||
|
||||
// Progress tracking
|
||||
'progress' => [
|
||||
'enabled' => env('SLIK_IMPORT_PROGRESS_ENABLED', true),
|
||||
'update_interval' => env('SLIK_IMPORT_PROGRESS_INTERVAL', 50), // update setiap 50 baris
|
||||
'cache_key' => 'slik_import_progress',
|
||||
'cache_ttl' => 3600, // 1 jam
|
||||
],
|
||||
],
|
||||
|
||||
// General import settings
|
||||
'general' => [
|
||||
'default_memory_limit' => env('IMPORT_DEFAULT_MEMORY_LIMIT', 128),
|
||||
'max_execution_time' => env('IMPORT_MAX_EXECUTION_TIME', 300000),
|
||||
'temp_directory' => env('IMPORT_TEMP_DIRECTORY', storage_path('app/temp')),
|
||||
'cleanup_temp_files' => env('IMPORT_CLEANUP_TEMP_FILES', true),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class () extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('debitures', function (Blueprint $table) {
|
||||
// nullable
|
||||
$table->string('cif', 16)
|
||||
->nullable()
|
||||
->default('0000000000')
|
||||
->comment('asal data LPJ.PRM_DEBITUR.KODE_CIF. Pada KODE_CIF ada yang digitnya 16 => 3372040405810002')
|
||||
->change();
|
||||
$table->unsignedBigInteger('branch_id')->nullable()->change();
|
||||
$table->string('nomor_id', 50)->nullable()->change();
|
||||
|
||||
$table->unsignedBigInteger('mig_kd_debitur_seq')->nullable()->comment('asal data LPJ.PRM_DEBITUR.KD_DEBITUR_SEQ. Berguna untuk update debitur_id menggunakan KD_DEBITUR_SEQ nya');
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->char('is_mig', 1)->nullable()->comment('untuk menandakan row ini dari LPJ OLD');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('debitures', function (Blueprint $table) {
|
||||
// Kembalikan kolom branch_id agar tidak nullable (asumsi awal NOT NULL)
|
||||
$table->unsignedBigInteger('branch_id')->nullable(false)->change();
|
||||
|
||||
// Kembalikan kolom yang diubah nullable menjadi NOT NULL
|
||||
$table->string('cif', 10)->nullable(false)->change();
|
||||
$table->string('nomor_id', 50)->nullable(false)->change();
|
||||
|
||||
// Hapus kolom tambahan yang dibuat di up()
|
||||
$table->dropColumn([
|
||||
'mig_kd_debitur_seq',
|
||||
'mig_urut_seq_addr',
|
||||
'mig_urut_seq_comm',
|
||||
'processed_at',
|
||||
'is_mig'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class () extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('permohonan', function (Blueprint $table) {
|
||||
// nullable
|
||||
$table->unsignedBigInteger('branch_id')->nullable()->change();
|
||||
$table->unsignedBigInteger('user_id')->nullable()->change();
|
||||
|
||||
|
||||
$table->unsignedBigInteger('mig_kd_debitur_seq')->nullable()->comment('asal data LPJ.PRM_DEBITUR.KD_DEBITUR_SEQ. Berguna untuk update debitur_id menggunakan KD_DEBITUR_SEQ nya');
|
||||
$table->unsignedBigInteger('nomor_lpj')->nullable();
|
||||
$table->string('mig_nama_ao')->nullable();
|
||||
$table->char('is_mig', 1)->nullable()->comment('untuk menandakan row ini dari LPJ OLD');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('permohonan', function (Blueprint $table) {
|
||||
// Kembalikan kolom branch_id agar tidak nullable (asumsi awal NOT NULL)
|
||||
$table->unsignedBigInteger('branch_id')->nullable(false)->change();
|
||||
$table->unsignedBigInteger('user_id')->nullable(false)->change();
|
||||
|
||||
|
||||
|
||||
// Hapus kolom tambahan yang dibuat di up()
|
||||
$table->dropColumn([
|
||||
'mig_kd_debitur_seq',
|
||||
'nomor_lpj',
|
||||
'mig_nama_ao',
|
||||
'is_mig'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class () extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('dokumen_jaminan', function (Blueprint $table) {
|
||||
// nullable
|
||||
|
||||
$table->unsignedBigInteger('mig_kd_debitur_seq')->nullable()->comment('asal data LPJ.PRM_DEBITUR.KD_DEBITUR_SEQ. Berguna untuk update debitur_id menggunakan KD_DEBITUR_SEQ nya');
|
||||
$table->unsignedBigInteger('nomor_lpj')->nullable();
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->char('is_mig', 1)->nullable()->comment('untuk menandakan row ini dari LPJ OLD');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('dokumen_jaminan', function (Blueprint $table) {
|
||||
|
||||
|
||||
// Hapus kolom tambahan yang dibuat di up()
|
||||
$table->dropColumn([
|
||||
'nomor_lpj',
|
||||
'mig_kd_debitur_seq',
|
||||
'processed_at',
|
||||
'is_mig'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class () extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('pemilik_jaminan', function (Blueprint $table) {
|
||||
// nullable
|
||||
|
||||
$table->unsignedBigInteger('mig_kd_debitur_seq')->nullable()->comment('asal data LPJ.PRM_DEBITUR.KD_DEBITUR_SEQ. Berguna untuk update debitur_id menggunakan KD_DEBITUR_SEQ nya');
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->char('is_mig', 1)->nullable()->comment('untuk menandakan row ini dari LPJ OLD');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('pemilik_jaminan', function (Blueprint $table) {
|
||||
|
||||
|
||||
// Hapus kolom tambahan yang dibuat di up()
|
||||
$table->dropColumn([
|
||||
'mig_kd_debitur_seq',
|
||||
'processed_at',
|
||||
'is_mig'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('inspeksi', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('nomor_lpj')->nullable();
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->char('is_mig', 1)->nullable()->comment('untuk menandakan row ini dari LPJ OLD');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('inspeksi', function (Blueprint $table) {
|
||||
$table->dropColumn('nomor_lpj');
|
||||
$table->dropColumn('processed_at');
|
||||
$table->dropColumn('is_mig');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('penilai', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('nomor_lpj')->nullable();
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->char('is_mig', 1)->nullable()->comment('untuk menandakan row ini dari LPJ OLD');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('penilai', function (Blueprint $table) {
|
||||
$table->dropColumn('nomor_lpj');
|
||||
$table->dropColumn('processed_at');
|
||||
$table->dropColumn('is_mig');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('noc', function (Blueprint $table) {
|
||||
$table->bigInteger('branch_id')->nullable()->after('debiture_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('noc', function (Blueprint $table) {
|
||||
$table->dropColumn('branch_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
88
database/migrations/2025_09_15_092525_create_sliks_table.php
Normal file
88
database/migrations/2025_09_15_092525_create_sliks_table.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* Migration untuk membuat tabel sliks dengan semua field yang diperlukan
|
||||
* berdasarkan header Excel yang diberikan untuk import data SLIK
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('sliks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
// Field utama berdasarkan header Excel
|
||||
$table->string('sandi_bank')->nullable(); // Sandi Bank
|
||||
$table->string('tahun')->nullable(); // Tahun
|
||||
$table->string('bulan')->nullable(); // Bulan
|
||||
$table->string('flag_detail')->nullable(); // Flag Detail
|
||||
$table->string('kode_register_agunan')->nullable(); // Kode Register Agunan
|
||||
$table->string('no_rekening')->nullable(); // No Rekening
|
||||
$table->string('cif')->nullable(); // CIF
|
||||
$table->string('kolektibilitas')->nullable(); // Kolektibilitas
|
||||
$table->string('fasilitas')->nullable(); // Fasilitas
|
||||
$table->string('jenis_segmen_fasilitas')->nullable(); // Jenis Segmen Fasilitas
|
||||
$table->string('status_agunan')->nullable(); // Status Agunan
|
||||
$table->string('jenis_agunan')->nullable(); // Jenis Agunan
|
||||
$table->string('peringkat_agunan')->nullable(); // Peringkat Agunan
|
||||
$table->string('lembaga_pemeringkat')->nullable(); // Lembaga Pemeringkat
|
||||
$table->string('jenis_pengikatan')->nullable(); // Jenis Pengikatan
|
||||
$table->string('tanggal_pengikatan')->nullable(); // Tanggal Pengikatan
|
||||
$table->string('nama_pemilik_agunan')->nullable(); // Nama Pemilik Agunan
|
||||
$table->string('bukti_kepemilikan')->nullable(); // Bukti Kepemilikan
|
||||
$table->text('alamat_agunan')->nullable(); // Alamat Agunan
|
||||
$table->string('lokasi_agunan')->nullable(); // Lokasi Agunan
|
||||
$table->string('nilai_agunan')->nullable(); // Nilai Agunan
|
||||
$table->string('nilai_agunan_menurut_ljk')->nullable(); // Nilai Agunan Menurut LJK
|
||||
$table->string('tanggal_penilaian_ljk')->nullable(); // Tanggal Penilaian LJK
|
||||
$table->string('nilai_agunan_penilai_independen')->nullable(); // Nilai Agunan Penilai Independen
|
||||
$table->string('nama_penilai_independen')->nullable(); // Nama Penilai Independen
|
||||
$table->string('tanggal_penilaian_penilai_independen')->nullable(); // Tanggal Penilaian Penilai Independen
|
||||
$table->string('jumlah_hari_tunggakan')->nullable(); // Jumlah Hari Tunggakan
|
||||
$table->string('status_paripasu')->nullable(); // Status Paripasu
|
||||
$table->string('prosentase_paripasu')->nullable(); // Prosentase Paripasu
|
||||
$table->string('status_kredit_join')->nullable(); // Status Kredit Join
|
||||
$table->string('diasuransikan')->nullable(); // Diasuransikan
|
||||
$table->text('keterangan')->nullable(); // Keterangan
|
||||
$table->string('kantor_cabang')->nullable(); // Kantor Cabang
|
||||
$table->string('operasi_data')->nullable(); // Operasi Data
|
||||
$table->string('kode_cabang')->nullable(); // Kode Cabang
|
||||
$table->string('nama_debitur')->nullable(); // Nama Debitur
|
||||
$table->string('nama_cabang')->nullable(); // Nama Cabang
|
||||
$table->string('flag')->nullable(); // Flag
|
||||
|
||||
// Standard Laravel fields
|
||||
$table->timestamps();
|
||||
$table->string('created_by')->nullable();
|
||||
$table->string('updated_by')->nullable();
|
||||
$table->string('deleted_by')->nullable();
|
||||
$table->softDeletes();
|
||||
|
||||
// Indexes untuk performa query
|
||||
$table->index(['sandi_bank']);
|
||||
$table->index(['tahun']);
|
||||
$table->index(['bulan']);
|
||||
$table->index(['no_rekening']);
|
||||
$table->index(['cif']);
|
||||
$table->index(['kode_register_agunan']);
|
||||
$table->index(['nama_debitur']);
|
||||
$table->index(['kode_cabang']);
|
||||
$table->index(['created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* Menghapus tabel sliks jika migration di-rollback
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('sliks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('laporan_slik', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('slik_id')->nullable();
|
||||
$table->string('sandi_bank', 10)->nullable();
|
||||
$table->string('kode_kantor', 10)->nullable();
|
||||
$table->string('kode_cabang', 10)->nullable();
|
||||
$table->string('tahun', 4)->nullable();
|
||||
$table->string('bulan', 2)->nullable();
|
||||
$table->string('no_rekening', 50)->nullable();
|
||||
$table->string('cif', 50)->nullable();
|
||||
$table->string('kode_jenis', 10)->nullable();
|
||||
$table->string('kode_jenis_ket', 100)->nullable();
|
||||
$table->string('kode_sifat', 10)->nullable();
|
||||
$table->string('kode_sifat_ket', 100)->nullable();
|
||||
$table->string('kode_valuta', 5)->nullable();
|
||||
$table->string('kode_valuta_ket', 50)->nullable();
|
||||
$table->string('baki_debet', 20)->nullable();
|
||||
$table->string('kolektibilitas', 5)->nullable();
|
||||
$table->string('kolektibilitas_ket', 50)->nullable();
|
||||
$table->string('tanggal_mulai', 10)->nullable();
|
||||
$table->string('tanggal_jatuh_tempo', 10)->nullable();
|
||||
$table->string('tanggal_selesai', 10)->nullable();
|
||||
$table->string('tanggal_restrukturisasi', 10)->nullable();
|
||||
$table->string('kode_sebab_macet', 10)->nullable();
|
||||
$table->string('kode_sebab_macet_ket', 100)->nullable();
|
||||
$table->string('tanggal_macet', 10)->nullable();
|
||||
$table->string('kode_kondisi', 10)->nullable();
|
||||
$table->string('kode_kondisi_ket', 100)->nullable();
|
||||
$table->string('tanggal_kondisi', 10)->nullable();
|
||||
$table->string('nilai_agunan', 20)->nullable();
|
||||
$table->string('nilai_agunan_ket', 100)->nullable();
|
||||
$table->string('jenis_agunan', 50)->nullable();
|
||||
$table->string('kode_agunan', 10)->nullable();
|
||||
$table->string('kode_agunan_ket', 100)->nullable();
|
||||
$table->string('peringkat_agunan', 10)->nullable();
|
||||
$table->string('peringkat_agunan_ket', 100)->nullable();
|
||||
$table->string('nama_debitur', 100)->nullable();
|
||||
$table->string('npwp', 50)->nullable();
|
||||
$table->string('no_ktp', 50)->nullable();
|
||||
$table->string('no_telp', 50)->nullable();
|
||||
$table->string('kode_kab_kota', 10)->nullable();
|
||||
$table->string('kode_kab_kota_ket', 100)->nullable();
|
||||
$table->string('kode_negara_domisili', 10)->nullable();
|
||||
$table->string('kode_negara_domisili_ket', 100)->nullable();
|
||||
$table->string('kode_pos', 10)->nullable();
|
||||
$table->string('alamat', 200)->nullable();
|
||||
$table->string('fasilitas', 100)->nullable();
|
||||
$table->string('status_agunan', 20)->nullable();
|
||||
$table->string('tanggal_lapor', 10)->nullable();
|
||||
$table->string('status', 20)->default('aktif');
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('slik_id');
|
||||
$table->index('no_rekening');
|
||||
$table->index('cif');
|
||||
$table->index('nama_debitur');
|
||||
$table->index(['tahun', 'bulan']);
|
||||
$table->index('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('laporan_slik');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('laporan_admin_kredit', function (Blueprint $table) {
|
||||
$table->text('keterangan')->nullable()->comment('Keterangan tambahan untuk laporan admin kredit');
|
||||
$table->string('kolektibilitas', 10)->nullable()->comment('Status kolektibilitas kredit');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('laporan_admin_kredit', function (Blueprint $table) {
|
||||
$table->dropColumn(['keterangan', 'kolektibilitas']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* Menambahkan kolom nomor_registrasi dan permohonan_id pada table bucoks
|
||||
* untuk menghubungkan data bucok dengan permohonan penilaian
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('bucoks', function (Blueprint $table) {
|
||||
// Menambahkan kolom nomor_registrasi untuk tracking nomor registrasi
|
||||
$table->string('nomor_registrasi')->nullable()->after('nomor_tiket')->comment('Nomor registrasi terkait');
|
||||
|
||||
// Menambahkan kolom permohonan_id sebagai foreign key ke table permohonan
|
||||
$table->unsignedBigInteger('permohonan_id')->nullable()->after('nomor_registrasi')->comment('ID permohonan terkait');
|
||||
|
||||
// Menambahkan foreign key constraint ke table permohonan
|
||||
$table->foreign('permohonan_id')->references('id')->on('permohonan')->onDelete('set null');
|
||||
|
||||
// Menambahkan index untuk performa query
|
||||
$table->index(['nomor_registrasi']);
|
||||
$table->index(['permohonan_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* Menghapus kolom nomor_registrasi dan permohonan_id dari table bucoks
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bucoks', function (Blueprint $table) {
|
||||
// Menghapus foreign key constraint terlebih dahulu
|
||||
$table->dropForeign(['permohonan_id']);
|
||||
|
||||
// Menghapus index
|
||||
$table->dropIndex(['nomor_registrasi']);
|
||||
$table->dropIndex(['permohonan_id']);
|
||||
|
||||
// Menghapus kolom
|
||||
$table->dropColumn(['nomor_registrasi', 'permohonan_id']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
return new class extends \Illuminate\Database\Migrations\Migration
|
||||
{
|
||||
/**
|
||||
* Tambah kolom biaya pada tabel nilai_plafond.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
\Illuminate\Support\Facades\Schema::table('nilai_plafond', function (\Illuminate\Database\Schema\Blueprint $table) {
|
||||
// PostgreSQL akan memetakan decimal ke numeric
|
||||
$table->decimal('biaya', 15, 2)->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Menghapus kolom biaya jika dilakukan rollback.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
\Illuminate\Support\Facades\Schema::table('nilai_plafond', function (\Illuminate\Database\Schema\Blueprint $table) {
|
||||
$table->dropColumn('biaya');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Tambahkan kolom biaya pada tabel nilai_plafond
|
||||
*
|
||||
* Catatan:
|
||||
* - Menggunakan tipe decimal(15,2) agar kompatibel dengan PostgreSQL (numeric)
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('nilai_plafond', function (Blueprint $table) {
|
||||
// Tambahkan kolom biaya sebagai decimal dengan 2 pecahan
|
||||
$table->decimal('biaya', 15, 2)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback perubahan: hapus kolom biaya dari tabel nilai_plafond
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasColumn('nilai_plafond', 'biaya')) {
|
||||
Schema::table('nilai_plafond', function (Blueprint $table) {
|
||||
$table->dropColumn('biaya');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('referensi_link', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 255)->nullable(false)->comment('Nama referensi link');
|
||||
$table->text('link')->nullable(false)->comment('URL link referensi');
|
||||
$table->string('kategori', 100)->nullable()->comment('Kategori referensi (misal: regulasi, panduan, dll)');
|
||||
$table->text('deskripsi')->nullable()->comment('Deskripsi lengkap referensi');
|
||||
$table->boolean('is_active')->default(true)->comment('Status aktif referensi');
|
||||
$table->integer('urutan')->default(0)->comment('Urutan tampil referensi');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('ID user yang membuat');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('ID user yang update terakhir');
|
||||
$table->timestamps();
|
||||
|
||||
// Indexes
|
||||
$table->index('kategori');
|
||||
$table->index('is_active');
|
||||
$table->index('urutan');
|
||||
$table->index('created_by');
|
||||
|
||||
// Foreign keys
|
||||
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
|
||||
$table->foreign('updated_by')->references('id')->on('users')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('referensi_link');
|
||||
}
|
||||
};
|
||||
@@ -13,24 +13,25 @@ class HubunganPemilikJaminanSeeder extends Seeder
|
||||
public function run(): void
|
||||
{
|
||||
$hubungan_pemilik_jaminan = [
|
||||
[
|
||||
'name' => 'Milik Pribadi'
|
||||
],
|
||||
[
|
||||
'name' => 'Suami/Istri'
|
||||
],
|
||||
[
|
||||
'name' => 'Anak'
|
||||
],
|
||||
[
|
||||
'name' => 'Saudara Kandung'
|
||||
],
|
||||
[
|
||||
'name' => 'Ayah'
|
||||
],
|
||||
[
|
||||
'name' => 'Ibu'
|
||||
]
|
||||
['name' => 'Milik Pribadi'],
|
||||
['name' => 'Suami'],
|
||||
['name' => 'Anak'],
|
||||
['name' => 'Saudara'],
|
||||
['name' => 'Ayah'],
|
||||
['name' => 'Ibu'],
|
||||
['name' => 'Nenek'],
|
||||
['name' => 'Penjual/Developer'],
|
||||
[ 'name' => 'Kakak/adik kandung'],
|
||||
[ 'name' => 'Orang tua'],
|
||||
[ 'name' => 'Mitra Usaha'],
|
||||
[ 'name' => 'Pihak lain'],
|
||||
[ 'name' => 'Negara'],
|
||||
[ 'name' => 'Nenek/kakek'],
|
||||
[ 'name' => 'Milik Keluarga'],
|
||||
[ 'name' => 'Kakak/adik Orangtua'],
|
||||
[ 'name' => 'Istri'],
|
||||
[ 'name' => 'Pengurus'],
|
||||
[ 'name' => 'Lain - lain'],
|
||||
];
|
||||
|
||||
foreach ($hubungan_pemilik_jaminan as $hpj) {
|
||||
|
||||
@@ -69,6 +69,49 @@ class JenisFasilitasKreditSeeder extends Seeder
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'code' => 'JFK009',
|
||||
'name' => 'UMKM',
|
||||
'status' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'code' => 'JFK010',
|
||||
'name' => 'KORPORASI',
|
||||
'status' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'code' => 'JFK011',
|
||||
'name' => 'KPR 2',
|
||||
'status' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'code' => 'JFK012',
|
||||
'name' => 'KONSUMER',
|
||||
'status' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'code' => 'JFK013',
|
||||
'name' => 'KOMERSIL',
|
||||
'status' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'code' => 'JFK014',
|
||||
'name' => 'KPR REGULER',
|
||||
'status' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,36 +12,50 @@ class LpjDatabaseSeeder extends Seeder
|
||||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
BranchSeeder::class,
|
||||
CurrencySeeder::class,
|
||||
HolidayCalendarSeeder::class,
|
||||
JenisFasilitasKreditSeeder::class,
|
||||
JenisLegalitasJaminanSeeder::class,
|
||||
JenisJaminanSeeder::class,
|
||||
JenisDokumenSeeder::class,
|
||||
TujuanPenilaianSeeder::class,
|
||||
NilaiPlatformSeeder::class,
|
||||
HubunganPemilikJaminanSeeder::class,
|
||||
HubunganPenghuniJaminanSeeder::class,
|
||||
ArahMataAnginSeeder::class,
|
||||
StatusPermohonanSeeder::class,
|
||||
RegionSeeder::class,
|
||||
TeamsSeeder::class,
|
||||
TeamUsersSeeder::class,
|
||||
JenisPenilaianSeeder::class,
|
||||
IjinUsahaSeeder::class,
|
||||
TujuanPenilaianKJPPSeeder::class,
|
||||
KJPPSeeder::class,
|
||||
JenisLaporanSeeder::class,
|
||||
DebitureSeeder::class,
|
||||
PemilikJaminanSeeder::class,
|
||||
DokumenJaminanSeeder::class,
|
||||
DetailDokumenJaminanSeeder::class,
|
||||
PermohonanSeeder::class,
|
||||
FotoObjekJaminanSeeder::class,
|
||||
|
||||
// BranchSeeder::class,
|
||||
// CurrencySeeder::class,
|
||||
// HolidayCalendarSeeder::class,
|
||||
// JenisFasilitasKreditSeeder::class,
|
||||
// JenisLegalitasJaminanSeeder::class,
|
||||
// JenisJaminanSeeder::class,
|
||||
// JenisDokumenSeeder::class,
|
||||
// TujuanPenilaianSeeder::class,
|
||||
// NilaiPlatformSeeder::class,
|
||||
// HubunganPemilikJaminanSeeder::class,
|
||||
// HubunganPenghuniJaminanSeeder::class,
|
||||
// ArahMataAnginSeeder::class,
|
||||
// StatusPermohonanSeeder::class,
|
||||
// RegionSeeder::class,
|
||||
// TeamsSeeder::class,
|
||||
// TeamUsersSeeder::class,
|
||||
// MasterDataSurveyorSeeder::class
|
||||
// JenisPenilaianSeeder::class,
|
||||
// IjinUsahaSeeder::class,
|
||||
// TujuanPenilaianKJPPSeeder::class,
|
||||
// KJPPSeeder::class,
|
||||
// JenisLaporanSeeder::class,
|
||||
// DebitureSeeder::class,
|
||||
// PemilikJaminanSeeder::class,
|
||||
// DokumenJaminanSeeder::class,
|
||||
// DetailDokumenJaminanSeeder::class,
|
||||
// PermohonanSeeder::class,
|
||||
// FotoObjekJaminanSeeder::class,
|
||||
// PenawaranSeeder::class,
|
||||
// DetailPenawaranSeeder::class,
|
||||
// PenilaianSeeder::class,
|
||||
// MigrationDebitureSeeder::class,
|
||||
// MigrationPermohonanSeeder::class,
|
||||
// MigrationDokumentJaminanSeeder::class,
|
||||
// MigrationDetailDokumenJaminanSeeder::class,
|
||||
// MigPenilaianAndPenilainTeamSeeder::class,
|
||||
// MigrationInpseksiSeeder::class,
|
||||
MigrationGambarInspeksiSeeder::class,
|
||||
// MigrationPembandingSeeder::class,
|
||||
// MigrationPenilaiSeeder::class
|
||||
|
||||
// ini untuk penilaian external
|
||||
// MigExternalPenawaranSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
45
database/seeders/MasterDataSurveyorSeeder.php
Normal file
45
database/seeders/MasterDataSurveyorSeeder.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MasterDataSurveyorSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
|
||||
$nameTable = [
|
||||
'fasilitas_objek',
|
||||
'gol_mas_sekitar',
|
||||
'jenis_bangunan',
|
||||
'jenis_kapal',
|
||||
'jenis_kendaraan',
|
||||
'jenis_pesawat',
|
||||
'kondisi_bangunan',
|
||||
'kondisi_fisik_tanah',
|
||||
'kontur_tanah',
|
||||
'lalu_lintas_lokasi',
|
||||
'lantai',
|
||||
'merupakan_daerah',
|
||||
'perkerasan_jalan',
|
||||
'sifat_bangunan',
|
||||
'model_alat_berat',
|
||||
'posisi_kavling',
|
||||
'posisi_unit',
|
||||
'tingkat_keramaian',
|
||||
'sarana_pelengkap',
|
||||
'spek_kategori_bangunan',
|
||||
'spek_bangunan',
|
||||
'terletak_diarea',
|
||||
'view_unit'
|
||||
];
|
||||
|
||||
foreach ($nameTable as $table) {
|
||||
DB::unprepared(file_get_contents(__DIR__ . '/sql/' . $table . '.sql'));
|
||||
}
|
||||
}
|
||||
}
|
||||
306
database/seeders/MigExternalPenawaranSeeder.php
Normal file
306
database/seeders/MigExternalPenawaranSeeder.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\PenawaranTender;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
use Modules\Lpj\Models\Penilaian;
|
||||
use Modules\Lpj\Models\PenilaianTeam;
|
||||
|
||||
class MigExternalPenawaranSeeder extends Seeder
|
||||
{
|
||||
protected $errorLogFile = __DIR__ . '/csv/penawaran/mig_penawaran_external_error.csv';
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// Bersihkan/Inisialisasi file error log
|
||||
$this->initializeErrorLog();
|
||||
// Path ke file csv
|
||||
$filePath = realpath(__DIR__ . '/csv/penawaran/mig_penawaran_external.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/permohonan/mig_penawaran_external.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; // Ukuran batch
|
||||
$userDataChace = [];
|
||||
$nomorRegisCahce = [];
|
||||
$errorCount = 0; // Inisialisasi variabel errorCount
|
||||
$errorDebitureIds = []; // Inisialisasi array errorDebitureIds
|
||||
$totalData = 0;
|
||||
|
||||
// Menghitung total data di file CSV
|
||||
while (($data = fgetcsv($handle, 0, '~')) !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle); // Reset pointer ke awal file
|
||||
fgetcsv($handle, 0, '~'); // Skip header
|
||||
|
||||
$batchCount = 0;
|
||||
$currentRow = 0;
|
||||
|
||||
while (($data = fgetcsv($handle, 0, '~')) !== false) {
|
||||
if (count($data) != count($header)) {
|
||||
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = array_combine($header, $data);
|
||||
$currentRow++;
|
||||
|
||||
// Jika sudah mencapai batch size, proses batch
|
||||
if (count($rows) >= $batchSize) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $nomorRegisCahce, $userDataChace, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Proses sisa data jika ada
|
||||
// print_r($rows[0]);
|
||||
if (!empty($rows)) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $nomorRegisCahce, $userDataChace, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
$this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}, Total error: {$errorCount}");
|
||||
}
|
||||
|
||||
|
||||
private function processBatch(
|
||||
array $rows,
|
||||
array &$userDataChace,
|
||||
array &$nomorRegisCahce,
|
||||
int &$errorCount,
|
||||
array &$errorDebitureIds,
|
||||
int $totalData,
|
||||
int $batchCount,
|
||||
int $currentRow
|
||||
) {
|
||||
$userData = [];
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
// Jalankan setiap baris dengan transaksi sendiri
|
||||
DB::beginTransaction();
|
||||
|
||||
// Cek apakah sudah diproses
|
||||
$existingRecord = PenawaranTender::where('nomor_registrasi', $row['mig_nomor_jaminan'])->first();
|
||||
if ($existingRecord && $existingRecord->created_at) {
|
||||
$this->command->info('Data sudah diproses sebelumnya: ' . $row['mig_nomor_jaminan']);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ambil nomor registrasi
|
||||
$nomor_registrasi = $this->getNomorRegistrasiPermohonan($row['mig_nomor_jaminan'], $nomorRegisCahce);
|
||||
if (!$nomor_registrasi) {
|
||||
throw new \Exception("Nomor registrasi tidak valid");
|
||||
}
|
||||
|
||||
|
||||
$userIdUpdate = $this->getUserIdData($row['mig_created_by'] ?? null, $userData)['id'];
|
||||
$userIdOtorisasi = $this->getUserIdData($row['mig_authorized_by'] ?? null, $userData)['id'];
|
||||
|
||||
if (!$userIdUpdate || !$userIdOtorisasi) {
|
||||
// $this->logError($userIdUpdate, 'Salah satu user tidak ditemukan');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mapping field user
|
||||
$mapUser = [
|
||||
'created_by' => $userIdUpdate,
|
||||
'updated_by' => $userIdUpdate,
|
||||
'authorized_by' => $userIdOtorisasi,
|
||||
];
|
||||
|
||||
// create random code
|
||||
|
||||
$tgl = $this->parseTimestamp($row['mig_tgl_penyerahan']);
|
||||
|
||||
// ambil nilai tahun terakhir contoh 2023 maka ambil 23
|
||||
$year = date('y', strtotime($tgl));
|
||||
|
||||
// random code berdasarkan tgl contoh NP2300001
|
||||
|
||||
$code = 'NP' . $year . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT); // outputnya NP2300001:
|
||||
// Cek apakah kode sudah ada
|
||||
$existingCode = PenawaranTender::where('code', $code)->first();
|
||||
if ($existingCode) {
|
||||
$code = 'NP' . $year . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
|
||||
// Buat penilaian
|
||||
$penilaian = PenawaranTender::create([
|
||||
'code' => $code,
|
||||
'nama_kjpp_sebelumnya' => $row['mig_nama_kjpp'],
|
||||
'biaya_kjpp_sebelumnya' => $row['mig_tot_jasa'],
|
||||
'tanggal_penilaian_sebelumnya' => $this->parseTimestamp($row['mig_tgl_penyerahan']),
|
||||
'nomor_registrasi' => $nomor_registrasi,
|
||||
'tujuan_penilaian_kjpp_id' => $this->checkTujuanPenilaian($row['mig_mst_jaminan_kd_tujuan_seq']),
|
||||
'jenis_laporan_id' => 1,
|
||||
'start_date' => $this->parseTimestamp($row['mig_tgl_penyerahan']),
|
||||
'end_date' => $this->parseTimestamp($row['mig_terima_laporan']),
|
||||
'catatan' => $row['mig_catatan'],
|
||||
'status' => 'spk',
|
||||
'created_at' => $this->parseTimestamp($row['mig_created_at']),
|
||||
'updated_at' => $this->parseTimestamp($row['mig_updated_at']),
|
||||
'authorized_by' => $mapUser['authorized_by'],
|
||||
'created_by' => $mapUser['created_by'],
|
||||
'updated_by' => $mapUser['updated_by'],
|
||||
]);
|
||||
|
||||
|
||||
|
||||
// Commit transaksi
|
||||
DB::commit();
|
||||
$this->command->info('Proses data penilaian ' . $row['mig_nomor_jaminan'] . ' (' . ($index + 1) . '/' . count($rows) . ')');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Rollback jika ada error
|
||||
DB::rollBack();
|
||||
|
||||
Log::error('Error pada baris: ' . json_encode($row) . '. Pesan: ' . $e->getMessage());
|
||||
$errorCount++;
|
||||
$errorDebitureIds[] = $row['mig_team_id'] ?? '-';
|
||||
$this->logError($row['mig_team_id'] ?? '-', $e->getMessage());
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info("Batch {$batchCount} selesai. Total error: " . count($errorDebitureIds));
|
||||
}
|
||||
|
||||
|
||||
private function getNomorRegistrasiPermohonan($nomor_registrasi_id, $nomorRegisCahce)
|
||||
{
|
||||
if (isset($nomorRegisCahce[$nomor_registrasi_id])) {
|
||||
return $nomorRegisCahce[$nomor_registrasi_id];
|
||||
}
|
||||
|
||||
$nomorRegis = Permohonan::where('nomor_registrasi', $nomor_registrasi_id)->first();
|
||||
|
||||
if (!$nomorRegis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$nomorRegisCahce[$nomor_registrasi_id] = $nomorRegis->nomor_registrasi;
|
||||
return $nomorRegis->nomor_registrasi;
|
||||
}
|
||||
|
||||
private function getUserIdData(?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 getUserId($mig_user_id, $userDataChace)
|
||||
{
|
||||
if (isset($userDataChace[$mig_user_id])) {
|
||||
return $userDataChace[$mig_user_id];
|
||||
}
|
||||
|
||||
$userId = User::where('nik', $mig_user_id)->first();
|
||||
|
||||
if (!$userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$userDataChace[$mig_user_id] = $userId->id;
|
||||
return $userId->id;
|
||||
}
|
||||
|
||||
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)
|
||||
return \Carbon\Carbon::createFromFormat('Y-m-d 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);
|
||||
}
|
||||
|
||||
private function checkTujuanPenilaian($code): int
|
||||
{
|
||||
$mapping = [
|
||||
1 => 7,
|
||||
2 => 5,
|
||||
3 => 8,
|
||||
6 => 6,
|
||||
];
|
||||
|
||||
return $mapping[$code] ?? 7;
|
||||
}
|
||||
}
|
||||
1036
database/seeders/MigInspeksiHelper.php
Normal file
1036
database/seeders/MigInspeksiHelper.php
Normal file
File diff suppressed because it is too large
Load Diff
350
database/seeders/MigPenilaianAndPenilainTeamSeeder.php
Normal file
350
database/seeders/MigPenilaianAndPenilainTeamSeeder.php
Normal file
@@ -0,0 +1,350 @@
|
||||
<?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\Penilaian;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
use Modules\Lpj\Models\PenilaianTeam;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
|
||||
class MigPenilaianAndPenilainTeamSeeder extends Seeder
|
||||
{
|
||||
protected $errorLogFile = __DIR__ . '/csv/penilaian/penilaian.team.fix.new_20251012_error.csv';
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// Bersihkan/Inisialisasi file error log
|
||||
$this->initializeErrorLog();
|
||||
// Path ke file csv
|
||||
$filePath = realpath(__DIR__ . '/csv/penilaian/penilaian.team.fix.new_20251012.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/penilaian/penilaian.team.fix.new_20251012.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; // Ukuran batch
|
||||
$userDataChace = [];
|
||||
$nomorRegisCahce = [];
|
||||
$errorCount = 0; // Inisialisasi variabel errorCount
|
||||
$errorDebitureIds = []; // Inisialisasi array errorDebitureIds
|
||||
$totalData = 0;
|
||||
|
||||
// Menghitung total data di file CSV
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle); // Reset pointer ke awal file
|
||||
fgetcsv($handle, 0, ','); // Skip header
|
||||
|
||||
$batchCount = 0;
|
||||
$currentRow = 0;
|
||||
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
if (count($data) != count($header)) {
|
||||
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = array_combine($header, $data);
|
||||
$currentRow++;
|
||||
|
||||
// Jika sudah mencapai batch size, proses batch
|
||||
if (count($rows) >= $batchSize) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $nomorRegisCahce, $userDataChace, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Proses sisa data jika ada
|
||||
// print_r($rows[0]);
|
||||
if (!empty($rows)) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $nomorRegisCahce, $userDataChace, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
$this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}, Total error: {$errorCount}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private function processBatch(
|
||||
array $rows,
|
||||
array &$userDataChace,
|
||||
array &$nomorRegisCahce,
|
||||
int &$errorCount,
|
||||
array &$errorDebitureIds,
|
||||
int $totalData,
|
||||
int $batchCount,
|
||||
int $currentRow
|
||||
) {
|
||||
$userData = [];
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
// Jalankan setiap baris dengan transaksi sendiri
|
||||
//DB::beginTransaction();
|
||||
|
||||
// Cek apakah sudah diproses
|
||||
$existingRecord = Penilaian::where('nomor_registrasi', $row['mig_nomor_registrasi'])->first();
|
||||
if ($existingRecord && $existingRecord->created_at) {
|
||||
$this->command->info('Data sudah diproses sebelumnya: ' . $row['mig_nomor_registrasi']);
|
||||
//continue;
|
||||
}
|
||||
|
||||
// Ambil nomor registrasi
|
||||
$nomor_registrasi = $this->getNomorRegistrasiPermohonan($row['mig_nomor_registrasi'], $nomorRegisCahce);
|
||||
if (!$nomor_registrasi) {
|
||||
throw new \Exception($row['mig_nomor_registrasi']."Nomor registrasi tidak valid");
|
||||
}
|
||||
|
||||
// Ambil user ID
|
||||
$userId = $this->getUserId($row['mig_user_id'], $userDataChace);
|
||||
if (!$userId) {
|
||||
// $this->logError($row['mig_user_id'], 'Salah satu user tidak ditemukan');
|
||||
throw new \Exception($row['mig_user_id'] . " User tidak ditemukan");
|
||||
continue;
|
||||
}
|
||||
|
||||
$userIdUpdate = $this->getUserIdData($row['mig_created_by'] ?? null, $userData)['id'];
|
||||
$userIdOtorisasi = $this->getUserIdData($row['mig_authorized_by'] ?? null, $userData)['id'];
|
||||
|
||||
if (!$userIdUpdate || !$userIdOtorisasi) {
|
||||
// $this->logError($userIdUpdate, 'Salah satu user tidak ditemukan');
|
||||
//continue;
|
||||
}
|
||||
|
||||
// Mapping field user
|
||||
$mapUser = [
|
||||
'created_by' => $userIdUpdate,
|
||||
'updated_by' => $userIdUpdate,
|
||||
'authorized_by' => $userIdOtorisasi,
|
||||
];
|
||||
|
||||
// Ambil team ID
|
||||
$teamId = $this->checkTeams($row['mig_team_id']);
|
||||
if (!$teamId) {
|
||||
throw new \Exception("Team tidak ditemukan");
|
||||
}
|
||||
|
||||
$idPenilaian = Penilaian::orderBy('id', 'desc')->first()->id;
|
||||
|
||||
$data = [
|
||||
'id' => $idPenilaian+1,
|
||||
'nomor_registrasi' => $nomor_registrasi,
|
||||
'jenis_penilaian_id' => 1,
|
||||
'tanggal_kunjungan' => $this->parseTimestamp($row['mig_tanggal_kunjungan']) ?? now(),
|
||||
'waktu_penilaian' => $this->parseTimestamp($row['mig_waktu_penilaian']),
|
||||
'status' => 'done',
|
||||
'keterangan' => null,
|
||||
'created_at' => $this->parseTimestamp($row['mig_created_at']),
|
||||
'updated_at' => $this->parseTimestamp($row['mig_updated_at']),
|
||||
'authorized_by' => $mapUser['authorized_by'],
|
||||
'created_by' => $mapUser['created_by'],
|
||||
'updated_by' => $mapUser['updated_by']
|
||||
];
|
||||
|
||||
if($nomor_registrasi=='251722'){
|
||||
Log::info("Data penilaian 251722 : ",$data);
|
||||
}
|
||||
|
||||
// Buat penilaian
|
||||
$penilaian = Penilaian::updateOrCreate([
|
||||
'nomor_registrasi' => $nomor_registrasi
|
||||
],[
|
||||
'id' => $idPenilaian+1,
|
||||
'nomor_registrasi' => $nomor_registrasi,
|
||||
'jenis_penilaian_id' => 1,
|
||||
'tanggal_kunjungan' => $this->parseTimestamp($row['mig_tanggal_kunjungan']) ?? now(),
|
||||
'waktu_penilaian' => $this->parseTimestamp($row['mig_waktu_penilaian']),
|
||||
'status' => 'done',
|
||||
'keterangan' => null,
|
||||
'created_at' => $this->parseTimestamp($row['mig_created_at']),
|
||||
'updated_at' => $this->parseTimestamp($row['mig_updated_at']),
|
||||
'authorized_by' => $mapUser['authorized_by'],
|
||||
'created_by' => $mapUser['created_by'],
|
||||
'updated_by' => $mapUser['updated_by'],
|
||||
]);
|
||||
|
||||
//DB::commit();
|
||||
|
||||
// Buat tim penilaian
|
||||
$roles = ['surveyor', 'penilai'];
|
||||
foreach ($roles as $role) {
|
||||
$idPenilaianTeam = PenilaianTeam::orderBy('id', 'desc')->first()->id;
|
||||
|
||||
PenilaianTeam::updateOrCreate([
|
||||
'penilaian_id' => $penilaian->id
|
||||
],[
|
||||
'id' => $idPenilaianTeam+1,
|
||||
'penilaian_id' => $penilaian->id,
|
||||
'user_id' => $userId,
|
||||
'role' => $role,
|
||||
'team_id' => $teamId,
|
||||
'created_at' => $this->parseTimestamp($row['mig_created_at']),
|
||||
'updated_at' => $this->parseTimestamp($row['mig_updated_at']),
|
||||
]);
|
||||
}
|
||||
|
||||
// Commit transaksi
|
||||
//DB::commit();
|
||||
$this->command->info('Proses data penilaian ' . $row['mig_nomor_lpj'] . ' (' . ($index + 1) . '/' . count($rows) . ')');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Rollback jika ada error
|
||||
//DB::rollBack();
|
||||
|
||||
Log::error('Error pada baris: ' . json_encode($row) . '. Pesan: ' . $e->getMessage());
|
||||
$errorCount++;
|
||||
$errorDebitureIds[] = $row['mig_team_id'] ?? '-';
|
||||
$this->logError($row['mig_team_id'] ?? '-', $e->getMessage());
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info("Batch {$batchCount} selesai. Total error: " . count($errorDebitureIds));
|
||||
}
|
||||
|
||||
|
||||
private function getNomorRegistrasiPermohonan($nomor_registrasi_id, $nomorRegisCahce)
|
||||
{
|
||||
if (isset($nomorRegisCahce[$nomor_registrasi_id])) {
|
||||
return $nomorRegisCahce[$nomor_registrasi_id];
|
||||
}
|
||||
|
||||
$nomorRegis = Permohonan::where('nomor_registrasi', $nomor_registrasi_id)->first();
|
||||
|
||||
if (!$nomorRegis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$nomorRegisCahce[$nomor_registrasi_id] = $nomorRegis->nomor_registrasi;
|
||||
return $nomorRegis->nomor_registrasi;
|
||||
}
|
||||
private function getUserIdData(?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 getUserId($mig_user_id, $userDataChace)
|
||||
{
|
||||
if (isset($userDataChace[$mig_user_id])) {
|
||||
return $userDataChace[$mig_user_id];
|
||||
}
|
||||
|
||||
$userId = User::where('nik', $mig_user_id)->first();
|
||||
|
||||
if (!$userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$userDataChace[$mig_user_id] = $userId->id;
|
||||
return $userId->id;
|
||||
}
|
||||
|
||||
private function checkTeams($code): int
|
||||
{
|
||||
$mapping = [
|
||||
'01' => 1,
|
||||
'02' => 2,
|
||||
'04' => 4,
|
||||
'07' => 5,
|
||||
'06' => 3,
|
||||
];
|
||||
return $mapping[(string)$code] ?? 1;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
356
database/seeders/MigrationDetailDokumenJaminanSeeder.php
Normal file
356
database/seeders/MigrationDetailDokumenJaminanSeeder.php
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Location\Models\City;
|
||||
use Modules\Location\Models\District;
|
||||
use Modules\Location\Models\Province;
|
||||
use Modules\Location\Models\Village;
|
||||
use Modules\Lpj\Models\DokumenJaminan;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\JenisJaminan;
|
||||
use Modules\Lpj\Models\JenisLegalitasJaminan;
|
||||
use Modules\Lpj\Models\PemilikJaminan;
|
||||
use Modules\Lpj\Models\DetailDokumenJaminan;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class MigrationDetailDokumenJaminanSeeder extends Seeder
|
||||
{
|
||||
protected $errorLogFile = __DIR__ . '/csv/detail.dokumen.jaminan_20251016_error.csv';
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->initializeErrorLog();
|
||||
// Path ke file csv
|
||||
$filePath = realpath(__DIR__ . '/csv/detail.dokumen.jaminan_20251016.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/detail.dokumen.jaminan_20251016.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 = 1000; // Ukuran batch
|
||||
$permohonanCache = [];
|
||||
$jenisJaminanCache = [];
|
||||
$dokumenJaminanCache = [];
|
||||
$provinceCache = [];
|
||||
$cityCache = [];
|
||||
$districtCache = [];
|
||||
$subdistrictCache = [];
|
||||
$totalData = 0;
|
||||
$errorCount = 0;
|
||||
$errorDebitureIds = [];
|
||||
$hubunganPemilikCache = [];
|
||||
|
||||
// Menghitung total data di file CSV
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle); // Reset pointer ke awal file
|
||||
fgetcsv($handle, 0, ','); // Skip header
|
||||
|
||||
$batchCount = 0;
|
||||
$currentRow = 0;
|
||||
|
||||
|
||||
// Membaca setiap baris dalam CSV
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
if (count($data) != count($header)) {
|
||||
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
|
||||
$errorCount++;
|
||||
$errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = array_combine($header, $data);
|
||||
$currentRow++;
|
||||
|
||||
// print_r($rows);
|
||||
|
||||
|
||||
if (count($rows) >= $batchSize) {
|
||||
$errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error
|
||||
$this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $dokumenJaminanCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
|
||||
// print_r($rows[0]);
|
||||
if (!empty($rows)) {
|
||||
$this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $dokumenJaminanCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
$this->command->info('Data debiture berhasil dimigrasikan.');
|
||||
}
|
||||
|
||||
private function processBatch(
|
||||
array $rows,
|
||||
array &$permohonanCache,
|
||||
array &$jenisJaminanCache,
|
||||
array &$dokumenJaminanCache,
|
||||
int $batchCount,
|
||||
int $currentRow,
|
||||
int $totalData,
|
||||
int &$errorCount,
|
||||
array &$errorDebitureIds
|
||||
) {
|
||||
$groupedRows = $this->groupRowsByNomorLaporanAndKeterangan($rows);
|
||||
|
||||
// print_r($groupedRows);
|
||||
|
||||
foreach ($groupedRows as $nomorLaporan => $dataPerGrup) {
|
||||
|
||||
// print_r($dataPerGrup);
|
||||
try {
|
||||
// Ambil salah satu baris untuk referensi (misal baris pertama dari grup)
|
||||
$firstRow = reset($dataPerGrup)['details'] ? reset($dataPerGrup) : reset($dataPerGrup);
|
||||
$debiturId = null;
|
||||
$nomorJaminan = null;
|
||||
|
||||
|
||||
// Cari debitur ID dari salah satu field
|
||||
foreach ($rows as $row) {
|
||||
if ($row['mig_nomor_laporan'] == $nomorLaporan) {
|
||||
$debiturId = $row['mig_kd_debitur_seq'] ?? null;
|
||||
$nomorJaminan = $row['mig_nomor_jaminan'] ?? null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$debiturId) {
|
||||
throw new \Exception('Debitur ID tidak ditemukan');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Ambil ID dokumen jaminan
|
||||
$dokumenJaminanId = $this->getDokumenJaminanId($debiturId,$nomorJaminan, $dokumenJaminanCache);
|
||||
if (!$dokumenJaminanId) {
|
||||
throw new \Exception('Dokumen jaminan tidak ditemukan');
|
||||
}
|
||||
|
||||
//dd($debiturId,$nomorJaminan);
|
||||
|
||||
foreach ($dataPerGrup as $groupKey => $rowData) {
|
||||
// Ambil legalitas jaminan ID berdasarkan grup keterangan
|
||||
$legalitasJaminanId = $this->getLegalitasJaminanId($groupKey, $jenisJaminanCache);
|
||||
|
||||
if (!$legalitasJaminanId) {
|
||||
throw new \Exception("Legalitas jaminan tidak ditemukan untuk grup: {$groupKey}");
|
||||
}
|
||||
|
||||
|
||||
// Simpan ke database
|
||||
$detail =DetailDokumenJaminan::updateOrCreate(
|
||||
[
|
||||
'name' => $groupKey,
|
||||
'dokumen_jaminan_id' => $dokumenJaminanId,
|
||||
],
|
||||
[
|
||||
'details' => json_encode($rowData['details']),
|
||||
'jenis_legalitas_jaminan_id' => $legalitasJaminanId,
|
||||
'dokumen_jaminan' => !empty($rowData['documents']) ? json_encode($rowData['documents']) : null,
|
||||
'dokumen_nomor' => $nomorLaporan,
|
||||
'keterangan' => $groupKey,
|
||||
]
|
||||
);
|
||||
|
||||
if($nomorJaminan=='253339'){
|
||||
//dd($debiturId,$nomorJaminan,$legalitasJaminanId, $detail, $dokumenJaminanId);
|
||||
}
|
||||
}
|
||||
|
||||
// Info progress
|
||||
$this->command->info("Proses data detail dokumen (Nomor Laporan: {$nomorLaporan}, batch ke: {$batchCount}, total dari: {$currentRow}/{$totalData})");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error pada nomor laporan {$nomorLaporan}: " . $e->getMessage());
|
||||
$this->logError($debiturId ?? '-', $e->getMessage());
|
||||
$errorDebitureIds[] = $debiturId ?? '-';
|
||||
$errorCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function groupRowsByNomorLaporann(array $rows): array
|
||||
{
|
||||
$grouped = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$nomorJaminan = $row['mig_nomor_laporan'] ?? null;
|
||||
if (!empty($nomorJaminan)) {
|
||||
$grouped[$nomorJaminan][] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
|
||||
// private function groupRowsByNomorLaporanAndKeterangan(array $rows): array
|
||||
// {
|
||||
// $groupedByNomorLaporan = $this->groupRowsByNomorLaporann($rows);
|
||||
// $finalGrouped = [];
|
||||
|
||||
// foreach ($groupedByNomorLaporan as $nomorLaporan => $rowList) {
|
||||
// $groupedByGrpKeterangan = [];
|
||||
|
||||
// // Urutkan berdasarkan mig_urutan_sub (ascending)
|
||||
// usort($rowList, function ($a, $b) {
|
||||
// return $a['mig_urutan_sub'] <=> $b['mig_urutan_sub'];
|
||||
// });
|
||||
|
||||
// foreach ($rowList as $row) {
|
||||
// $grpKeterangan = $row['mig_grp_keterangan'] ?? 'UNKNOWN_GROUP';
|
||||
|
||||
// if (!isset($groupedByGrpKeterangan[$grpKeterangan])) {
|
||||
// $groupedByGrpKeterangan[$grpKeterangan] = [
|
||||
// 'details' => [],
|
||||
// 'documents' => []
|
||||
// ];
|
||||
// }
|
||||
|
||||
// $keyKeterangan = $row['mig_key_keterangan'] ?? 'UNKNOWN_KEY';
|
||||
// $value = $row['mig_nilai'] ?? null;
|
||||
|
||||
// // Jika belum ada detail, inisialisasi sebagai array asosiatif
|
||||
// if (empty($groupedByGrpKeterangan[$grpKeterangan]['details'])) {
|
||||
// $groupedByGrpKeterangan[$grpKeterangan]['details'][] = [];
|
||||
// }
|
||||
|
||||
// // Masukkan ke dalam object tunggal (bukan item baru)
|
||||
// if ($value !== null) {
|
||||
// $lastIndex = count($groupedByGrpKeterangan[$grpKeterangan]['details']) - 1;
|
||||
// $groupedByGrpKeterangan[$grpKeterangan]['details'][$lastIndex][$keyKeterangan] = $value;
|
||||
// }
|
||||
|
||||
// // Tambahkan dokumen jika ada
|
||||
// $urlFile = $row['mig_url_file'] ?? null;
|
||||
// if (!empty($urlFile)) {
|
||||
// $groupedByGrpKeterangan[$grpKeterangan]['documents'][] = $urlFile;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $finalGrouped[$nomorLaporan] = $groupedByGrpKeterangan;
|
||||
// }
|
||||
|
||||
// // print_r($finalGrouped);
|
||||
// return $finalGrouped;
|
||||
// }
|
||||
|
||||
|
||||
private function groupRowsByNomorLaporanAndKeterangan(array $rows): array
|
||||
{
|
||||
$groupedByNomorLaporan = $this->groupRowsByNomorLaporann($rows);
|
||||
$finalGrouped = [];
|
||||
|
||||
foreach ($groupedByNomorLaporan as $nomorLaporan => $rowList) {
|
||||
$groupedByGrpKeterangan = [];
|
||||
|
||||
// Urutkan berdasarkan mig_urutan_sub
|
||||
usort($rowList, function ($a, $b) {
|
||||
return $a['mig_urutan_sub'] <=> $b['mig_urutan_sub'];
|
||||
});
|
||||
|
||||
foreach ($rowList as $row) {
|
||||
$grpKeterangan = $row['mig_grp_keterangan'] ?? 'UNKNOWN_GROUP';
|
||||
$keyKeterangan = $row['mig_key_keterangan'] ?? 'UNKNOWN_KEY';
|
||||
$value = $row['mig_nilai'] ?? null;
|
||||
|
||||
if (!isset($groupedByGrpKeterangan[$grpKeterangan])) {
|
||||
$groupedByGrpKeterangan[$grpKeterangan] = [
|
||||
'details' => [],
|
||||
'documents' => []
|
||||
];
|
||||
}
|
||||
|
||||
// Masukkan sebagai object baru ke dalam details
|
||||
if ($value !== null) {
|
||||
$groupedByGrpKeterangan[$grpKeterangan]['details'][] = [
|
||||
$keyKeterangan => $value
|
||||
];
|
||||
}
|
||||
|
||||
// Dokumen tetap masuk sebagai array string
|
||||
$urlFile = $row['mig_url_file'] ?? null;
|
||||
if (!empty($urlFile)) {
|
||||
$groupedByGrpKeterangan[$grpKeterangan]['documents'][] = $urlFile;
|
||||
}
|
||||
}
|
||||
|
||||
$finalGrouped[$nomorLaporan] = $groupedByGrpKeterangan;
|
||||
}
|
||||
|
||||
return $finalGrouped;
|
||||
}
|
||||
private function getDokumenJaminanId(string $code,string $nomorLaporan, array &$dokumenJaminanCache)
|
||||
{
|
||||
$dokumen = DokumenJaminan::where('mig_kd_debitur_seq', $code)
|
||||
->where('nomor_lpj', $nomorLaporan)
|
||||
->first();
|
||||
|
||||
if ($dokumen) {
|
||||
$dokumenJaminanCache[$code] = $dokumen->id;
|
||||
return $dokumen->id;
|
||||
}
|
||||
}
|
||||
|
||||
private function getLegalitasJaminanId(string $code, array &$legalitasJaminanCache)
|
||||
{
|
||||
|
||||
if (isset($legalitasJaminanCache[$code])) {
|
||||
return $legalitasJaminanCache[$code];
|
||||
}
|
||||
|
||||
$legalitas = JenisLegalitasJaminan::whereRaw('LOWER(name) = ?', [strtolower($code)])->first();
|
||||
|
||||
if ($legalitas) {
|
||||
$legalitasJaminanCache[$code] = $legalitas->id;
|
||||
return $legalitas->id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function initializeErrorLog()
|
||||
{
|
||||
$file = $this->errorLogFile;
|
||||
|
||||
if (file_exists($file)) {
|
||||
unlink($file); // Hapus file lama
|
||||
}
|
||||
|
||||
$handle = fopen($file, 'w');
|
||||
fputcsv($handle, ['mig_kd_debitur_seq', 'Error']);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function logError(string $kode, string $message)
|
||||
{
|
||||
Log::error("Error migrasi dokumen jaminan [$kode]: $message");
|
||||
|
||||
$handle = fopen($this->errorLogFile, 'a');
|
||||
fputcsv($handle, [$kode, $message]);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
}
|
||||
508
database/seeders/MigrationDokumentJaminanSeeder.php
Normal file
508
database/seeders/MigrationDokumentJaminanSeeder.php
Normal file
@@ -0,0 +1,508 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Location\Models\City;
|
||||
use Modules\Location\Models\District;
|
||||
use Modules\Location\Models\Province;
|
||||
use Modules\Location\Models\Village;
|
||||
use Modules\Lpj\Models\DokumenJaminan;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\JenisJaminan;
|
||||
use Modules\Lpj\Models\PemilikJaminan;
|
||||
use Modules\Lpj\Models\HubunganPemilikJaminan;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
||||
class MigrationDokumentJaminanSeeder extends Seeder
|
||||
{
|
||||
protected $errorLogFile = __DIR__ . '/csv/dokumen_20251022_error_log.csv';
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->initializeErrorLog();
|
||||
// Path ke file csv
|
||||
$filePath = realpath(__DIR__ . '/csv/dokumen_20251022.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/dokumen_20251022.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 = 1000; // Ukuran batch
|
||||
$permohonanCache = [];
|
||||
$jenisJaminanCache = [];
|
||||
$pemilikJaminanCache = [];
|
||||
$provinceCache = [];
|
||||
$cityCache = [];
|
||||
$districtCache = [];
|
||||
$subdistrictCache = [];
|
||||
$totalData = 0;
|
||||
$errorCount = 0;
|
||||
$errorDebitureIds = [];
|
||||
$hubunganPemilikCache = [];
|
||||
// Menghitung total data di file CSV
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle); // Reset pointer ke awal file
|
||||
fgetcsv($handle, 0, ','); // Skip header
|
||||
|
||||
$batchCount = 0;
|
||||
$currentRow = 0;
|
||||
|
||||
|
||||
// Membaca setiap baris dalam CSV
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
if (count($data) != count($header)) {
|
||||
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
|
||||
$errorCount++;
|
||||
$errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = array_combine($header, $data);
|
||||
$currentRow++;
|
||||
|
||||
// print_r($rows);
|
||||
|
||||
|
||||
if (count($rows) >= $batchSize) {
|
||||
$errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error
|
||||
$this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $pemilikJaminanCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds, $hubunganPemilikCache);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
|
||||
// print_r($rows[0]);
|
||||
if (!empty($rows)) {
|
||||
$this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $pemilikJaminanCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds, $hubunganPemilikCache);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
$this->command->info('Data debiture berhasil dimigrasikan.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Proses batch data.
|
||||
*/
|
||||
|
||||
private function processBatch(
|
||||
array $rows,
|
||||
array &$permohonanCache,
|
||||
array &$jenisJaminanCache,
|
||||
array &$pemilikJaminanCache,
|
||||
array &$provinceCache,
|
||||
array &$cityCache,
|
||||
array &$districtCache,
|
||||
array &$subdistrictCache,
|
||||
int $batchCount,
|
||||
int $currentRow,
|
||||
int $totalData,
|
||||
int &$errorCount,
|
||||
array &$errorDebitureIds,
|
||||
array &$hubunganPemilikCache
|
||||
) {
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
// Jalankan transaksi per-baris
|
||||
// DB::beginTransaction();
|
||||
|
||||
// Cari permohonan
|
||||
|
||||
$permohonan = Permohonan::where('nomor_registrasi', $row['mig_nomor_jaminan'])->first();
|
||||
|
||||
if (empty($permohonan)) {
|
||||
throw new \Exception('Missing debiture_id' . $row['mig_nomor_jaminan']);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pastikan permohonan_id belum digunakan di dokumen_jaminan
|
||||
$existingDokumen = DokumenJaminan::where('permohonan_id', $permohonan->id)->first();
|
||||
if ($existingDokumen) {
|
||||
//throw new \Exception("permohonan_id {$permohonan->id} sudah digunakan di dokumen_jaminan");
|
||||
//continue;
|
||||
}
|
||||
|
||||
// Ambil lokasi
|
||||
|
||||
// jika external silahkan matikan ini
|
||||
|
||||
$proviceCode = $this->getProvinceCode($row['mig_province_name'], $provinceCache);
|
||||
$cityCode = $this->getCityCode($row['mig_city_name'], $cityCache);
|
||||
$districtCode = $this->getDistrictCode($row['mig_district_name'], $districtCache);
|
||||
$subdistrict = $this->getSubdistrictCode($row['mig_village_name'], $subdistrictCache, $districtCache);
|
||||
// $hubunganPemilik = $this->getHubunganPemilikJaminanId($row['mig_hubungan_pemilik_jaminan'], $hubunganPemilikCache);
|
||||
|
||||
|
||||
$pemilik_jaminan_name = $this->getDebitureId($row['mig_kd_debitur_seq'], $pemilikJaminanCache);
|
||||
// Buat Pemilik Jaminan
|
||||
$pemilik_jaminan = PemilikJaminan::updateOrCreate([
|
||||
'debiture_id' => $permohonan->debiture_id,
|
||||
'hubungan_pemilik_jaminan_id' => 1,
|
||||
// 'name' => $row['name'],
|
||||
'name' => $pemilik_jaminan_name,
|
||||
'detail_sertifikat' => null,
|
||||
'npwp' => null,
|
||||
'nomor_id' => null,
|
||||
'email' => null,
|
||||
'phone' => null,
|
||||
// jika external silahkan matikan ini
|
||||
'province_code' => $proviceCode,
|
||||
'city_code' => $cityCode,
|
||||
'district_code' => $districtCode,
|
||||
'village_code' => $subdistrict['code'],
|
||||
'postal_code' => $subdistrict['postal_code'],
|
||||
'address' => $row['address'],
|
||||
'created_at' => $this->parseTimestamp($row['created_at']),
|
||||
'updated_at' => $this->parseTimestamp($row['updated_at']),
|
||||
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
|
||||
'processed_at' => now(),
|
||||
'is_mig' => 1
|
||||
]);
|
||||
|
||||
|
||||
// Buat Dokumen Jaminan
|
||||
$dokumenJaminan = DokumenJaminan::updateOrCreate([
|
||||
'permohonan_id' => $permohonan->id,
|
||||
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
|
||||
'nomor_lpj' => $row['mig_nomor_jaminan']
|
||||
],[
|
||||
'debiture_id' => $permohonan->debiture_id,
|
||||
'permohonan_id' => $permohonan->id,
|
||||
'jenis_jaminan_id' => $this->getJaminanId($row['mig_jenis_seq']) ?? '',
|
||||
'pemilik_jaminan_id' => $pemilik_jaminan->id,
|
||||
// jika external silahkan matikan ini
|
||||
'province_code' => $proviceCode ?? '',
|
||||
'city_code' => $cityCode ?? '',
|
||||
'district_code' => $districtCode ?? '',
|
||||
'village_code' => $subdistrict['code'] ?? '',
|
||||
'postal_code' => $subdistrict['postal_code'] ?? '',
|
||||
'address' => $row['address'],
|
||||
'created_at' => $this->parseTimestamp($row['created_at']),
|
||||
'updated_at' => $this->parseTimestamp($row['updated_at']),
|
||||
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
|
||||
'processed_at' => now(),
|
||||
'nomor_lpj' => $row['mig_nomor_jaminan'],
|
||||
'is_mig' => 1
|
||||
]);
|
||||
|
||||
// Commit jika semua sukses
|
||||
// DB::commit();
|
||||
$this->command->info("Proses dokumen jaminan: " . $row['mig_kd_debitur_seq'] . "Batch: {$batchCount} Baris: {$currentRow} Total: {$totalData} Error: {$errorCount}");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Rollback hanya untuk baris ini
|
||||
Log::error("Error pada baris: " . json_encode($row) . ". Pesan: " . $e->getMessage());
|
||||
$this->logError($row['mig_kd_debitur_seq'] ?? '-', $e->getMessage());
|
||||
$errorDebitureIds[] = $row['mig_kd_debitur_seq'] ?? '-';
|
||||
// DB::rollBack();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info("Batch {$batchCount} selesai. Total error: " . count($errorDebitureIds));
|
||||
}
|
||||
// private function getPermohonanId($code,$cache)
|
||||
// {
|
||||
// if (isset($cache[$code])) {
|
||||
// return $cache[$code];
|
||||
// }
|
||||
|
||||
// $permohonan = Permohonan::where('mig_kd_debitur_seq', $code)->where('nomor_registrasi', $mig_nomor_jaminan)->first();
|
||||
|
||||
// if ($permohonan) {
|
||||
// $cache[$code] = $permohonan;
|
||||
// return $permohonan;
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// }
|
||||
|
||||
|
||||
private function getJaminanId($code): ?int
|
||||
{
|
||||
/*$mapping = [
|
||||
7 => 17,
|
||||
8 => 13,
|
||||
6 => 32,
|
||||
1 => 17,
|
||||
2 => 26,
|
||||
3 => 27,
|
||||
4 => 50,
|
||||
5 => 21,
|
||||
138051314724 => 23,
|
||||
138027243057 => 34,
|
||||
138027664224 => 35,
|
||||
138027738489 => 10,
|
||||
138051485796 => 48,
|
||||
138051492883 => 47,
|
||||
138051515419 => 40,
|
||||
138051753311 => 41,
|
||||
138051754843 => 46,
|
||||
138051759078 => 42,
|
||||
138051480538 => 45,
|
||||
123382184742 => 18,
|
||||
138051483711 => 44,
|
||||
991 => 52
|
||||
];*/
|
||||
|
||||
$mapping = [
|
||||
1 => 1,
|
||||
2 => 17,
|
||||
3 => 19,
|
||||
4 => 15,
|
||||
5 => 18,
|
||||
6 => 26,
|
||||
7 => 26,
|
||||
8 => 26,
|
||||
9 => 26,
|
||||
10 => 24,
|
||||
11 => 28,
|
||||
12 => 29,
|
||||
13 => 32,
|
||||
991001 => 17,
|
||||
121965631354 => 17,
|
||||
122267387302 => 13,
|
||||
122267391891 => 27,
|
||||
123242566528 => 1,
|
||||
123391628912 => 18,
|
||||
123779076991 => 26,
|
||||
123779092232 => 26,
|
||||
123837866231 => 19,
|
||||
124159228236 => 14,
|
||||
124280447242 => 32,
|
||||
124385048902 => 30,
|
||||
124539856281 => 22,
|
||||
124635294016 => 13,
|
||||
124963468687 => 18,
|
||||
125178371127 => 31,
|
||||
125228814911 => 17,
|
||||
125749523699 => 27,
|
||||
126156105725 => 15,
|
||||
127407367039 => 15,
|
||||
132065123922 => 32,
|
||||
138027244724 => 33,
|
||||
138027246193 => 34,
|
||||
138027693348 => 35,
|
||||
138027764236 => 10,
|
||||
138050882693 => 15,
|
||||
138050910670 => 20,
|
||||
138051316169 => 23,
|
||||
138051517359 => 36,
|
||||
138051519318 => 37,
|
||||
138051522331 => 38,
|
||||
138051601738 => 39,
|
||||
138051602831 => 40,
|
||||
138051773783 => 41,
|
||||
138051776693 => 46,
|
||||
138051780489 => 42,
|
||||
164921358499 => 32,
|
||||
165216289979 => 49,
|
||||
165216294371 => 49,
|
||||
173035895092 => 24
|
||||
];
|
||||
|
||||
return $mapping[$code] ?? 17;
|
||||
}
|
||||
|
||||
private function getPemilikJaminanId(string $code, array &$cache): ?int
|
||||
{
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$jaminan = PemilikJaminan::where('mig_kd_debitur_seq', $code)->first();
|
||||
|
||||
if ($jaminan) {
|
||||
$cache[$code] = $jaminan->id;
|
||||
return $jaminan->id;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function getDebitureId(string $code, array &$cache): ?string
|
||||
{
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$debiture = Debiture::where('mig_kd_debitur_seq', $code)->first();
|
||||
|
||||
if ($debiture) {
|
||||
$cache[$code] = $debiture->name;
|
||||
return $debiture->name;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private function getProvinceCode(string $name, array &$cache): ?string
|
||||
{
|
||||
|
||||
$normalizedName = strtolower($name);
|
||||
if (isset($cache[$normalizedName])) {
|
||||
return $cache[$normalizedName];
|
||||
}
|
||||
|
||||
$province = Province::whereRaw('LOWER(name) = ?', [strtolower($name)])->first();
|
||||
|
||||
if ($province) {
|
||||
$cache[$normalizedName] = $province->code;
|
||||
return $province->code;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getCityCode(string $name, array &$cache): ?string
|
||||
{
|
||||
$normalizedName = strtolower($name);
|
||||
if (isset($cache[$normalizedName])) {
|
||||
return $cache[$normalizedName];
|
||||
}
|
||||
|
||||
$city = City::whereRaw('LOWER(name) = ?', [strtolower($name)])->first();
|
||||
|
||||
if ($city) {
|
||||
$cache[$normalizedName] = $city->code;
|
||||
return $city->code;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getDistrictCode(string $name, array &$cache): ?string
|
||||
{
|
||||
$normalizedName = strtolower($name);
|
||||
if (isset($cache[$normalizedName])) {
|
||||
return $cache[$normalizedName];
|
||||
}
|
||||
|
||||
$district = District::whereRaw('LOWER(name) = ?', [strtolower($name)])->first();
|
||||
|
||||
if ($district) {
|
||||
$cache[$normalizedName] = $district->code;
|
||||
return $district->code;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getSubdistrictCode(string $name, array &$cache, array &$districtCache): ?array
|
||||
{
|
||||
$normalizedName = strtolower($name);
|
||||
|
||||
// Pastikan cache menyimpan array, bukan hanya kode
|
||||
if (isset($cache[$normalizedName])) {
|
||||
return $cache[$normalizedName];
|
||||
}
|
||||
|
||||
// Ambil subdistrict dari database
|
||||
$subdistrict = Village::whereRaw('LOWER(name) = ?', [$normalizedName])->first();
|
||||
|
||||
// Jika ditemukan, simpan ke dalam cache sebagai array lengkap
|
||||
if ($subdistrict) {
|
||||
$cache[$normalizedName] = [
|
||||
'code' => $subdistrict->code,
|
||||
'postal_code' => $subdistrict->postal_code
|
||||
];
|
||||
return $cache[$normalizedName];
|
||||
}
|
||||
|
||||
// Jika tidak ditemukan, kembalikan null
|
||||
return [
|
||||
'code' => null,
|
||||
'postal_code' => null
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
private function getHubunganPemilikJaminanId(string $code, array &$cache): ?int
|
||||
{
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$jaminan = HubunganPemilikJaminan::whereRaw('LOWER(name) = ?', [strtolower($code)])->first();
|
||||
|
||||
if ($jaminan) {
|
||||
$cache[$code] = $jaminan->id;
|
||||
return $jaminan->id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mengonversi nilai TIMESTAMP menjadi format datetime yang valid.
|
||||
*/
|
||||
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()
|
||||
{
|
||||
$file = $this->errorLogFile;
|
||||
|
||||
if (file_exists($file)) {
|
||||
unlink($file); // Hapus file lama
|
||||
}
|
||||
|
||||
$handle = fopen($file, 'w');
|
||||
fputcsv($handle, ['mig_kd_debitur_seq', 'Error']);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function logError(string $kode, string $message)
|
||||
{
|
||||
Log::error("Error migrasi dokumen jaminan [$kode]: $message");
|
||||
|
||||
$handle = fopen($this->errorLogFile, 'a');
|
||||
fputcsv($handle, [$kode, $message]);
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
297
database/seeders/MigrationGambarInspeksiSeeder.php
Normal file
297
database/seeders/MigrationGambarInspeksiSeeder.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\Inspeksi;
|
||||
use Modules\Basicdata\Models\Branch;
|
||||
use Modules\Lpj\Models\DokumenJaminan;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use DateTime;
|
||||
|
||||
class MigrationGambarInspeksiSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
protected $errorLogFile = __DIR__ . '/csv/inspeksi/foto_20251014-error.csv';
|
||||
/**
|
||||
* Migrasi data gambar inspeksi dari file csv ke tabel inspeksi_gambar
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->initializeErrorLog();
|
||||
// Path ke file csv
|
||||
$filePath = realpath(__DIR__ . '/csv/inspeksi/foto_20251014.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/foto_20251014.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 = []; // <-- Gunakan sebagai cache
|
||||
$totalData = 0;
|
||||
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
fgetcsv($handle, 0, ','); // Skip header
|
||||
|
||||
$batchCount = 0;
|
||||
$currentRow = 0;
|
||||
$errorCount = 0;
|
||||
$errorDebitureIds = [];
|
||||
|
||||
while (($data = fgetcsv($handle, 0, ',')) !== false) {
|
||||
if (count($data) != count($header)) {
|
||||
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
|
||||
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, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
// info_harga_laporan_202505021522.csv
|
||||
// print_r($rows[0]);
|
||||
if (!empty($rows)) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $branchCache, $userData, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
$this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}");
|
||||
}
|
||||
|
||||
private function processBatch(array $rows, array &$branchCache, array &$userData, int &$errorCount, array &$errorDebitureIds, int $totalData, int $batchCount, int $currentRow)
|
||||
{
|
||||
// Kelompokkan berdasarkan mig_nomor_jaminan
|
||||
$groupedRows = $this->groupRowsByJaminan($rows);
|
||||
|
||||
foreach ($groupedRows as $nomorJaminan => $groupRows) {
|
||||
try {
|
||||
// Ambil informasi permohonan dan dokument_id
|
||||
$nomorRegis = $this->getNomorRegistrasiPermohonan($nomorJaminan, $branchCache);
|
||||
if (!$nomorRegis) {
|
||||
Log::warning("Nomor registrasi tidak ditemukan untuk nomor jaminan: {$nomorJaminan}");
|
||||
$errorCount++;
|
||||
$errorDebitureIds[] = $nomorJaminan;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bangun JSON foto_form
|
||||
$fotoJson = $this->checkFoto($groupRows);
|
||||
|
||||
|
||||
Inspeksi::updateOrCreate(
|
||||
[
|
||||
'permohonan_id' => $nomorRegis['id'],
|
||||
'dokument_id' => $nomorRegis['dokument_id']
|
||||
],
|
||||
[
|
||||
'foto_form' => $fotoJson,
|
||||
'updated_at' => now()
|
||||
]
|
||||
);
|
||||
|
||||
$this->command->info("Berhasil update foto_form untuk nomor jaminan: {$nomorJaminan}");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error pada nomor jaminan {$nomorJaminan}: " . $e->getMessage());
|
||||
$errorCount++;
|
||||
$errorDebitureIds[] = $nomorJaminan;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
private function groupRowsByJaminan(array $rows): array
|
||||
{
|
||||
$grouped = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$nomorJaminan = $row['mig_nomor_jaminan'] ?? null;
|
||||
|
||||
if (!empty($nomorJaminan)) {
|
||||
$grouped[$nomorJaminan][] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private function checkFoto(array $rows)
|
||||
{
|
||||
// Inisialisasi kelompok untuk tiap kategori
|
||||
$kategoriPrioritas = [
|
||||
'PETA LOKASI' => [],
|
||||
'GAMBAR SITUASI / SURAT UKUR' => [],
|
||||
'FOTO JAMINAN' => [],
|
||||
'MAK' => [],
|
||||
'lainnya' => []
|
||||
];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
// Ambil kolom penting
|
||||
$namaFoto = trim($row['mig_nama_gambar'] ?? '');
|
||||
$pathFoto = trim($row['mig_url_gambar'] ?? '');
|
||||
$kategori = trim($row['mig_kategori'] ?? 'lainnya');
|
||||
$urutan = (int)($row['mig_urutan_gambar'] ?? 999);
|
||||
$tgl = trim($row['mig_tgl'] ?? '');
|
||||
$nomorLpj = trim($row['mig_nomor_laporan'] ?? '');
|
||||
|
||||
if (empty($pathFoto) || empty($tgl)) {
|
||||
continue; // Lewati jika tidak lengkap
|
||||
}
|
||||
|
||||
try {
|
||||
$tanggal = \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $tgl)->startOfDay();
|
||||
if (!$tanggal) {
|
||||
throw new \Exception("Tanggal tidak valid");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
continue; // Lewati jika tanggal tidak valid
|
||||
}
|
||||
|
||||
$tahun = $tanggal->format('Y');
|
||||
$bulanAngka = $tanggal->format('n');
|
||||
$bulanNama = [
|
||||
1 => 'JANUARI', 2 => 'FEBRUARI', 3 => 'MARET',
|
||||
4 => 'APRIL', 5 => 'MEI', 6 => 'JUNI',
|
||||
7 => 'JULI', 8 => 'AGUSTUS', 9 => 'SEPTEMBER',
|
||||
10 => 'OKTOBER', 11 => 'NOVEMBER', 12 => 'DESEMBER'
|
||||
][(int)$bulanAngka] ?? 'UNKNOWN';
|
||||
|
||||
$tanggalFormat = $tanggal->format('dmY');
|
||||
|
||||
if (empty($namaFoto)) {
|
||||
$namaFoto = basename($pathFoto) ?: 'image.jpg';
|
||||
}
|
||||
|
||||
// Gunakan '/' sebagai separator path
|
||||
$finalPath = "surveyor/{$tahun}/{$bulanNama}/{$tanggalFormat}/{$nomorLpj}/{$pathFoto}";
|
||||
|
||||
$fotoItem = [
|
||||
'urutan' => $urutan,
|
||||
'name' => $namaFoto,
|
||||
'path' => $finalPath,
|
||||
'category' => $kategori,
|
||||
'sub' => null,
|
||||
'description' => '',
|
||||
'created_by' => null,
|
||||
'created_at' => null
|
||||
];
|
||||
|
||||
// Masukkan ke dalam kelompok kategori
|
||||
if (isset($kategoriPrioritas[$kategori])) {
|
||||
$kategoriPrioritas[$kategori][] = $fotoItem;
|
||||
} else {
|
||||
$kategoriPrioritas['lainnya'][] = $fotoItem;
|
||||
}
|
||||
}
|
||||
|
||||
// Urutkan masing-masing kategori berdasarkan urutan
|
||||
foreach ($kategoriPrioritas as &$kelompok) {
|
||||
usort($kelompok, function ($a, $b) {
|
||||
return $a['urutan'] <=> $b['urutan'];
|
||||
});
|
||||
}
|
||||
|
||||
// Gabungkan dengan urutan prioritas: PETA LOKASI -> GAMBAR SITUASI -> FOTO JAMINAN -> lainnya
|
||||
$finalFotos = array_merge(
|
||||
$kategoriPrioritas['PETA LOKASI'],
|
||||
$kategoriPrioritas['GAMBAR SITUASI / SURAT UKUR'],
|
||||
$kategoriPrioritas['FOTO JAMINAN'],
|
||||
$kategoriPrioritas['lainnya']
|
||||
);
|
||||
|
||||
// Hapus indeks 'urutan'
|
||||
$finalFotos = array_map(function ($foto) {
|
||||
unset($foto['urutan']);
|
||||
return $foto;
|
||||
}, $finalFotos);
|
||||
|
||||
return json_encode([
|
||||
'upload_foto' => $finalFotos
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
private function getNomorRegistrasiPermohonan($nomor_jaminan_id, array &$cache)
|
||||
{
|
||||
// Cek apakah sudah ada di cache
|
||||
if (isset($cache[$nomor_jaminan_id])) {
|
||||
return $cache[$nomor_jaminan_id];
|
||||
}
|
||||
|
||||
// Cari di tabel Permohonan berdasarkan nomor registrasi
|
||||
$permohonan = Permohonan::where('nomor_registrasi', $nomor_jaminan_id)->first();
|
||||
|
||||
if (!$permohonan) {
|
||||
// Tidak ditemukan
|
||||
$cache[$nomor_jaminan_id] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Cari dokument jaminan terkait
|
||||
$dokumenJaminan = DokumenJaminan::where('permohonan_id', $permohonan->id)->first();
|
||||
|
||||
// Simpan hasil ke cache
|
||||
$result = [
|
||||
'id' => $permohonan->id,
|
||||
'dokument_id' => $dokumenJaminan ? $dokumenJaminan->id : null
|
||||
];
|
||||
|
||||
$cache[$nomor_jaminan_id] = $result;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
private function initializeErrorLog()
|
||||
{
|
||||
$file = $this->errorLogFile;
|
||||
|
||||
if (file_exists($file)) {
|
||||
unlink($file); // Hapus file lama
|
||||
}
|
||||
|
||||
$handle = fopen($file, 'w');
|
||||
fputcsv($handle, ['mig_kd_debitur_seq', 'Error']);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function logError(string $kode, string $message)
|
||||
{
|
||||
Log::error("Error migrasi dokumen jaminan [$kode]: $message");
|
||||
|
||||
$handle = fopen($this->errorLogFile, 'a');
|
||||
fputcsv($handle, [$kode, $message]);
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
1001
database/seeders/MigrationInpseksiSeeder.php
Normal file
1001
database/seeders/MigrationInpseksiSeeder.php
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user