feat(webstatement): tambah fitur Laporan Closing Balance

Perubahan yang dilakukan:

**Controller LaporanClosingBalanceController:**
- Membuat controller baru untuk laporan closing balance dengan method index(), dataForDatatables(), export(), dan show().
- Menggunakan model AccountBalance dengan field actual_balance dan cleared_balance.
- Implementasi filter nomor rekening dan rentang tanggal.
- Menambahkan DB transaction dan rollback untuk keamanan data.
- Logging dan error handling komprehensif untuk proses data dan export.

**View laporan-closing-balance/index.blade.php:**
- Form filter dengan input nomor rekening dan rentang tanggal (default 30 hari terakhir).
- Implementasi DataTables dengan kolom: Nomor Rekening, Periode, Saldo Cleared, Saldo Aktual, Tanggal Update, dan Action.
- Tombol Filter, Reset, dan Export CSV.
- JavaScript untuk format currency IDR, format tanggal, dan dynamic export URL.
- Menggunakan TailwindCSS dan KTDataTable untuk desain yang responsive.

**View laporan-closing-balance/show.blade.php:**
- Halaman detail per record dengan visual saldo yang menarik (color-coded cards).
- Menampilkan Saldo Cleared, Saldo Aktual, dan Selisih Saldo secara otomatis.
- Informasi rekening dan periode disertai fitur copy ke clipboard.
- Tombol aksi: Kembali, Export, dan Print (dengan print style khusus).
- Responsive untuk berbagai ukuran layar.

**Routing dan Navigasi:**
- Menambahkan routing resource dengan prefix 'laporan-closing-balance' di web.php.
- Tambahan route untuk datatables, export, dan show dengan middleware auth.
- Breadcrumb dinamis untuk index dan show, menampilkan nomor rekening dan periode.

**Penyesuaian Model:**
- Menggunakan relasi Account di model AccountBalance melalui account_number.
- Menyesuaikan field dari opening_balance ke cleared_balance sesuai skema.
- Tetap mempertahankan actual_balance untuk saldo akhir.

**Fitur Keamanan dan Performance:**
- Input validation dan sanitization untuk semua request.
- Pagination dan filter query untuk efisiensi dan mencegah memory overflow.
- Error logging dengan context untuk debugging lebih mudah.

**User Experience:**
- Interface user-friendly dengan feedback visual dan loading state.
- Export CSV untuk kebutuhan analisis lebih lanjut.
- Print-friendly layout untuk kebutuhan cetak data.
- Clipboard integration untuk kemudahan salin data.

Tujuan perubahan:
- Menyediakan fitur monitoring dan analisis closing balance secara komprehensif di modul Webstatement.
- Mempermudah user dalam melihat detail saldo akhir dengan filtering, export, dan cetak yang optimal.
This commit is contained in:
Daeng Deni Mardaeni
2025-07-15 09:32:01 +07:00
parent 2dd8024586
commit 35bb173056
5 changed files with 883 additions and 13 deletions

View File

@@ -0,0 +1,232 @@
<?php
namespace Modules\Webstatement\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
use Modules\Webstatement\Models\AccountBalance;
/**
* Controller untuk mengelola laporan closing balance
* Menyediakan form input nomor rekening dan rentang tanggal
* serta menampilkan data closing balance berdasarkan filter
*/
class LaporanClosingBalanceController extends Controller
{
/**
* Menampilkan halaman utama laporan closing balance
* dengan form filter nomor rekening dan rentang tanggal
*
* @return \Illuminate\View\View
*/
public function index()
{
Log::info('Mengakses halaman laporan closing balance');
return view('webstatement::laporan-closing-balance.index');
}
/**
* Mengambil data laporan closing balance berdasarkan filter
* yang dikirim melalui AJAX untuk datatables
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
Log::info('Mengambil data untuk datatables laporan closing balance', [
'filters' => $request->all()
]);
try {
DB::beginTransaction();
$query = AccountBalance::query();
// Filter berdasarkan nomor rekening jika ada
if ($request->filled('account_number')) {
$query->where('account_number', 'like', '%' . $request->account_number . '%');
Log::info('Filter nomor rekening diterapkan', ['account_number' => $request->account_number]);
}
// Filter berdasarkan rentang tanggal jika ada
if ($request->filled('start_date') && $request->filled('end_date')) {
$startDate = Carbon::parse($request->start_date)->format('Ymd');
$endDate = Carbon::parse($request->end_date)->format('Ymd');
$query->whereBetween('period', [$startDate, $endDate]);
Log::info('Filter rentang tanggal diterapkan', [
'start_date' => $startDate,
'end_date' => $endDate
]);
}
// Sorting
$sortColumn = $request->get('sort', 'period');
$sortDirection = $request->get('direction', 'desc');
$query->orderBy($sortColumn, $sortDirection);
// Pagination
$perPage = $request->get('per_page', 10);
$page = $request->get('page', 1);
$results = $query->paginate($perPage, ['*'], 'page', $page);
DB::commit();
Log::info('Data laporan closing balance berhasil diambil', [
'total' => $results->total(),
'per_page' => $perPage,
'current_page' => $page
]);
return response()->json([
'data' => $results->items(),
'pagination' => [
'current_page' => $results->currentPage(),
'last_page' => $results->lastPage(),
'per_page' => $results->perPage(),
'total' => $results->total(),
'from' => $results->firstItem(),
'to' => $results->lastItem()
]
]);
} catch (\Exception $e) {
DB::rollback();
Log::error('Error saat mengambil data laporan closing balance', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'error' => 'Terjadi kesalahan saat mengambil data laporan',
'message' => $e->getMessage()
], 500);
}
}
/**
* Export data laporan closing balance ke format Excel
*
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function export(Request $request)
{
Log::info('Export laporan closing balance dimulai', [
'filters' => $request->all()
]);
try {
DB::beginTransaction();
$query = AccountBalance::query();
// Terapkan filter yang sama seperti di datatables
if ($request->filled('account_number')) {
$query->where('account_number', 'like', '%' . $request->account_number . '%');
}
if ($request->filled('start_date') && $request->filled('end_date')) {
$startDate = Carbon::parse($request->start_date)->format('Ymd');
$endDate = Carbon::parse($request->end_date)->format('Ymd');
$query->whereBetween('period', [$startDate, $endDate]);
}
$data = $query->orderBy('period', 'desc')->get();
DB::commit();
Log::info('Export laporan closing balance berhasil', [
'total_records' => $data->count()
]);
// Generate CSV content
$csvContent = "Nomor Rekening,Periode,Saldo Aktual,Saldo Cleared,Tanggal Update\n";
foreach ($data as $item) {
$csvContent .= sprintf(
"%s,%s,%s,%s,%s\n",
$item->account_number,
$item->period,
number_format($item->actual_balance, 2),
number_format($item->cleared_balance, 2),
$item->updated_at ? $item->updated_at->format('Y-m-d H:i:s') : '-'
);
}
$filename = 'laporan_closing_balance_' . date('Y-m-d_H-i-s') . '.csv';
return response($csvContent)
->header('Content-Type', 'text/csv')
->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
} catch (\Exception $e) {
DB::rollback();
Log::error('Error saat export laporan closing balance', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'error' => 'Terjadi kesalahan saat export laporan',
'message' => $e->getMessage()
], 500);
}
}
/**
* Menampilkan detail laporan closing balance untuk periode tertentu
*
* @param string $accountNumber
* @param string $period
* @return \Illuminate\View\View
*/
public function show($accountNumber, $period)
{
Log::info('Menampilkan detail laporan closing balance', [
'account_number' => $accountNumber,
'period' => $period
]);
try {
DB::beginTransaction();
$closingBalance = AccountBalance::where('account_number', $accountNumber)
->where('period', $period)
->firstOrFail();
DB::commit();
Log::info('Detail laporan closing balance berhasil diambil', [
'account_number' => $accountNumber,
'period' => $period,
'balance' => $closingBalance->actual_balance
]);
return view('webstatement::laporan-closing-balance.show', [
'closingBalance' => $closingBalance
]);
} catch (\Exception $e) {
DB::rollback();
Log::error('Error saat menampilkan detail laporan closing balance', [
'account_number' => $accountNumber,
'period' => $period,
'error' => $e->getMessage()
]);
return redirect()->route('laporan-closing-balance.index')
->with('error', 'Data laporan closing balance tidak ditemukan');
}
}
}