Files
lpj/app/Http/Controllers/MemoController.php
Daeng Deni Mardaeni 0c2c0c9e20 feat(memo): Tambah halaman preview dan PDF memo penyelesaian
Menambahkan fitur preview memo penyelesaian sebelum data disimpan ke database, untuk memastikan user dapat memeriksa kembali detail permohonan dan biaya terkait.

Perubahan pada Controller:
- Menambahkan method `preview()` di MemoController untuk menampilkan halaman preview memo penyelesaian.
- Menambahkan method `generatePdf()` untuk menghasilkan PDF resmi memo dengan format sesuai standar bank.
- Mengimplementasikan validasi input lengkap sebelum proses preview dan PDF generation.
- Mengintegrasikan helper fungsi terbilang untuk konversi total biaya ke dalam format huruf.

Perubahan pada View:
- Menambahkan view `preview.blade.php` sebagai template resmi memo penyelesaian dengan layout AGI header, informasi memo, daftar permohonan, total biaya PJ, dan tanda tangan.
- Menampilkan tabel daftar permohonan terlampir dengan informasi debitur, cabang, AO, dan nominal biaya PJ.
- Menyediakan fitur print preview dengan styling khusus untuk pencetakan dokumen resmi.
- Menambahkan UI interaktif untuk mempermudah navigasi sebelum menyimpan data.

Perubahan pada Form Create:
- Mengubah alur form `create.blade.php` agar mengarahkan ke halaman preview terlebih dahulu sebelum penyimpanan.
- Menambahkan tombol untuk melanjutkan proses ke simpan atau kembali ke pengisian form.

Routing dan Navigasi:
- Menambahkan route baru `memo.preview` untuk preview dan `memo.generate-pdf` untuk generate PDF memo penyelesaian.
- Menambahkan breadcrumb untuk halaman preview agar navigasi lebih jelas.

Fitur Tambahan:
- Implementasi JavaScript untuk interaksi form preview, print action, dan konfirmasi user.
- Penambahan fitur terbilang untuk memudahkan verifikasi nominal dalam format teks.
- Penanganan error secara komprehensif dengan logging dan rollback pada transaksi jika terjadi kesalahan.
- Menjamin data tetap konsisten meskipun user hanya melakukan preview tanpa menyimpan.

Tujuan Perubahan:
- Memberikan fasilitas preview agar user dapat memverifikasi data sebelum menyimpan memo penyelesaian.
- Memastikan output memo dalam format PDF resmi yang sesuai dengan template bank.
- Meningkatkan user experience dan mengurangi potensi kesalahan input sebelum proses finalisasi.
- Mempermudah proses cetak memo dengan fitur print-friendly dan PDF yang siap dikirim atau diarsipkan.
2025-07-17 09:55:06 +07:00

461 lines
16 KiB
PHP

<?php
namespace Modules\Lpj\Http\Controllers;
use Exception;
use Modules\Lpj\Models\Noc;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Lpj\Models\Permohonan;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
class MemoController extends Controller
{
public $user;
/**
* Menampilkan halaman index memo penyelesaian
*
* @return \Illuminate\View\View
*/
public function index()
{
Log::info('MemoController: Mengakses halaman index memo penyelesaian');
return view('lpj::memo.index');
}
/**
* Menampilkan form untuk membuat memo penyelesaian dengan data yang dipilih
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
Log::info('MemoController: Mengakses halaman create memo penyelesaian');
$selectedIds = $request->get('selected_ids', []);
// Pastikan $selectedIds selalu berupa array
if (is_string($selectedIds)) {
$selectedIds = explode(',', $selectedIds);
}
// Filter array untuk menghilangkan nilai kosong
$selectedIds = array_filter($selectedIds, function($id) {
return !empty(trim($id));
});
$permohonanList = [];
$totalBiayaPJ = 0;
if (!empty($selectedIds) && count($selectedIds) > 0) {
try {
$permohonanList = Permohonan::with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->whereIn('id', $selectedIds)->get();
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = Noc::whereIn('permohonan_id', $selectedIds)
->sum('nominal_bayar');
Log::info('MemoController: Total Biaya PJ dihitung: ' . $totalBiayaPJ);
} catch (Exception $e) {
Log::error('MemoController: Error saat mengambil data permohonan - ' . $e->getMessage());
return redirect()->back()->with('error', 'Terjadi kesalahan saat memuat data');
}
}
return view('lpj::memo.create', compact('permohonanList', 'totalBiayaPJ'));
}
/**
* Menyimpan memo penyelesaian yang telah dibuat
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
Log::info('MemoController: Memulai proses penyimpanan memo penyelesaian');
DB::beginTransaction();
try {
// Validasi input
$request->validate([
'permohonan_ids' => 'required|array',
'permohonan_ids.*' => 'exists:permohonan,id',
'memo_number' => 'required|string|max:255',
'payment_date' => 'required|date',
'memo_date' => 'required|date'
]);
$permohonanIds = $request->permohonan_ids;
$memoNumber = $request->memo_number;
$paymentDate = $request->payment_date;
$memoDate = $request->memo_date;
// Update status permohonan yang dipilih
foreach ($permohonanIds as $permohonanId) {
$permohonan = Permohonan::find($permohonanId);
if ($permohonan) {
$permohonan->status = 'memo-penyelesaian';
$permohonan->memo_penyelesaian_number = $memoNumber;
$permohonan->memo_penyelesaian_date = $memoDate;
$permohonan->memo_penyelesaian_payment_date = $paymentDate;
$permohonan->memo_penyelesaian_created_at = now();
$permohonan->save();
Log::info('MemoController: Berhasil update permohonan ID: ' . $permohonanId);
}
}
DB::commit();
Log::info('MemoController: Berhasil menyimpan memo penyelesaian untuk ' . count($permohonanIds) . ' permohonan');
return redirect()->route('memo.index')
->with('success', 'Memo penyelesaian berhasil dibuat untuk ' . count($permohonanIds) . ' permohonan');
} catch (Exception $e) {
DB::rollback();
Log::error('MemoController: Error saat menyimpan memo penyelesaian - ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Terjadi kesalahan saat menyimpan memo penyelesaian: ' . $e->getMessage());
}
}
/**
* Menampilkan detail memo penyelesaian
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
Log::info('MemoController: Mengakses detail memo penyelesaian ID: ' . $id);
$permohonan = Permohonan::with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->findOrFail($id);
return view('lpj::memo.show', compact('permohonan'));
}
/**
* Mengambil data untuk datatables pada halaman memo penyelesaian
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
Log::info('MemoController: Mengambil data untuk datatables');
if (is_null($this->user) || !$this->user->can('debitur.view')) {
Log::warning('MemoController: User tidak memiliki permission untuk melihat data');
// abort(403, 'Sorry! You are not allowed to view users.');
}
// Mengambil data dari database dengan kondisi yang sama seperti LaporanController
$query = Permohonan::query()
->whereIn('status', ['proses-laporan', 'done', 'paparan', 'proses-paparan', 'memo-penyelesaian'])
->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 = $query->orderBy('nomor_registrasi', 'desc');
// Apply search filter jika ada
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('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Apply sorting jika ada
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Mendapatkan total count records
$totalRecords = $query->count();
$size = $request->get('size', 10);
if ($size == 0) {
$size = 10;
}
// Apply pagination jika ada
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size;
$query->skip($offset)->take($size);
}
// Mendapatkan filtered count records
$filteredRecords = $query->count();
// Mendapatkan data untuk halaman saat ini
$data = $query->with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->get();
// Menghitung page count
$pageCount = ceil($totalRecords / $size);
// Menghitung current page number
$currentPage = max(1, $request->get('page', 1));
Log::info('MemoController: Berhasil mengambil data datatables - Total: ' . $totalRecords . ', Filtered: ' . $filteredRecords);
// Return response data sebagai JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
/**
* Mengambil total biaya PJ berdasarkan permohonan yang dipilih
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function getTotalBiayaPJ(Request $request)
{
Log::info('MemoController: Mengambil total biaya PJ');
try {
$permohonanIds = $request->get('permohonan_ids', []);
// Pastikan $permohonanIds selalu berupa array
if (is_string($permohonanIds)) {
$permohonanIds = explode(',', $permohonanIds);
}
// Filter array untuk menghilangkan nilai kosong
$permohonanIds = array_filter($permohonanIds, function($id) {
return !empty(trim($id));
});
$totalBiayaPJ = 0;
if (!empty($permohonanIds) && count($permohonanIds) > 0) {
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = \Modules\Lpj\Models\Noc::whereIn('permohonan_id', $permohonanIds)
->sum('nominal_bayar');
}
Log::info('MemoController: Total Biaya PJ berhasil dihitung: ' . $totalBiayaPJ);
return response()->json([
'success' => true,
'total_biaya_pj' => $totalBiayaPJ,
'total_biaya_pj_formatted' => 'Rp ' . number_format($totalBiayaPJ, 0, ',', '.')
]);
} catch (Exception $e) {
Log::error('MemoController: Error saat menghitung total biaya PJ - ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan saat menghitung total biaya PJ'
], 500);
}
}
/**
* Menampilkan preview memo penyelesaian sebelum menyimpan
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function preview(Request $request)
{
Log::info('MemoController: Mengakses halaman preview memo penyelesaian');
// Validasi input
$request->validate([
'permohonan_ids' => 'required|array',
'permohonan_ids.*' => 'exists:permohonan,id',
'memo_number' => 'required|string|max:255',
'payment_date' => 'required|date',
'memo_date' => 'required|date'
]);
$permohonanIds = $request->permohonan_ids;
$memoNumber = $request->memo_number;
$paymentDate = $request->payment_date;
$memoDate = $request->memo_date;
try {
// Ambil data permohonan yang dipilih
$permohonanList = Permohonan::with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->whereIn('id', $permohonanIds)->get();
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = Noc::whereIn('permohonan_id', $permohonanIds)
->sum('nominal_bayar');
// Data untuk template memo
$memoData = [
'memo_number' => $memoNumber,
'memo_date' => $memoDate,
'payment_date' => $paymentDate,
'total_biaya_pj' => $totalBiayaPJ,
'permohonan_list' => $permohonanList,
'debitur_count' => $permohonanList->count(),
'jaminan_info' => $this->getJaminanInfo($permohonanList)
];
Log::info('MemoController: Data preview memo berhasil disiapkan');
return view('lpj::memo.preview', compact('memoData', 'permohonanList', 'totalBiayaPJ'));
} catch (Exception $e) {
Log::error('MemoController: Error saat menyiapkan preview memo - ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Terjadi kesalahan saat menyiapkan preview memo: ' . $e->getMessage());
}
}
/**
* Generate PDF memo penyelesaian dan simpan ke database
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function generatePdf(Request $request)
{
Log::info('MemoController: Memulai generate PDF memo penyelesaian');
DB::beginTransaction();
try {
// Validasi input
$request->validate([
'permohonan_ids' => 'required|array',
'permohonan_ids.*' => 'exists:permohonan,id',
'memo_number' => 'required|string|max:255',
'payment_date' => 'required|date',
'memo_date' => 'required|date'
]);
$permohonanIds = $request->permohonan_ids;
$memoNumber = $request->memo_number;
$paymentDate = $request->payment_date;
$memoDate = $request->memo_date;
// Update status permohonan yang dipilih
foreach ($permohonanIds as $permohonanId) {
$permohonan = Permohonan::find($permohonanId);
if ($permohonan) {
$permohonan->status = 'memo-penyelesaian';
$permohonan->memo_penyelesaian_number = $memoNumber;
$permohonan->memo_penyelesaian_date = $memoDate;
$permohonan->memo_penyelesaian_payment_date = $paymentDate;
$permohonan->memo_penyelesaian_created_at = now();
$permohonan->save();
Log::info('MemoController: Berhasil update permohonan ID: ' . $permohonanId);
}
}
DB::commit();
Log::info('MemoController: Berhasil generate PDF dan menyimpan memo penyelesaian untuk ' . count($permohonanIds) . ' permohonan');
return redirect()->route('memo.index')
->with('success', 'Memo penyelesaian berhasil dibuat dan disimpan untuk ' . count($permohonanIds) . ' permohonan');
} catch (Exception $e) {
DB::rollback();
Log::error('MemoController: Error saat generate PDF memo penyelesaian - ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Terjadi kesalahan saat generate PDF memo penyelesaian: ' . $e->getMessage());
}
}
/**
* Helper function untuk mendapatkan informasi jaminan
*
* @param $permohonanList
* @return string
*/
private function getJaminanInfo($permohonanList)
{
$jaminanTypes = [];
foreach ($permohonanList as $permohonan) {
if ($permohonan->tujuanPenilaian) {
$jaminanTypes[] = $permohonan->tujuanPenilaian->name;
}
}
$uniqueJaminan = array_unique($jaminanTypes);
return implode(' & ', $uniqueJaminan);
}
}