From 1ff4035b9877a69958aa0bbc29f4b58ce20c6ab4 Mon Sep 17 00:00:00 2001 From: Daeng Deni Mardaeni Date: Wed, 27 Aug 2025 16:22:06 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(balance):=20implementasi=20ser?= =?UTF-8?q?vice=20layer=20untuk=20balance=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Menambahkan `AccountBalanceService` dengan transaksi PostgreSQL dan proper error handling. - Implementasi `BalanceServiceProvider` untuk mendukung dependency injection pattern. - Registrasi `BalanceServiceProvider` dalam `WebstatementServiceProvider`. - Penambahan `CAST` ke `DECIMAL(15,2)` untuk kompatibilitas PostgreSQL. - Perhitungan balance summary mencakup opening balance dan closing balance. - Agregasi transaksi dengan type casting yang aman. - Implementasi database transaction handling dengan mekanisme rollback dan commit. - Logging komprehensif untuk debugging dan audit trail. - Mendukung balance inquiry berdasarkan tanggal maupun periode tertentu. - Validasi akun dengan pengecekan `exists` untuk memastikan data valid. --- app/Providers/BalanceServiceProvider.php | 31 +++++ app/Providers/WebstatementServiceProvider.php | 1 + app/Services/AccountBalanceService.php | 130 ++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 app/Providers/BalanceServiceProvider.php create mode 100644 app/Services/AccountBalanceService.php diff --git a/app/Providers/BalanceServiceProvider.php b/app/Providers/BalanceServiceProvider.php new file mode 100644 index 0000000..cadbde5 --- /dev/null +++ b/app/Providers/BalanceServiceProvider.php @@ -0,0 +1,31 @@ +app->singleton(AccountBalanceService::class, function ($app) { + return new AccountBalanceService(); + }); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return [AccountBalanceService::class]; + } +} diff --git a/app/Providers/WebstatementServiceProvider.php b/app/Providers/WebstatementServiceProvider.php index 83b0bf1..55a7579 100644 --- a/app/Providers/WebstatementServiceProvider.php +++ b/app/Providers/WebstatementServiceProvider.php @@ -57,6 +57,7 @@ class WebstatementServiceProvider extends ServiceProvider { $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); + $this->app->register(BalanceServiceProvider::class); $this->app->bind(UpdateAtmCardBranchCurrencyJob::class); } diff --git a/app/Services/AccountBalanceService.php b/app/Services/AccountBalanceService.php new file mode 100644 index 0000000..fef6deb --- /dev/null +++ b/app/Services/AccountBalanceService.php @@ -0,0 +1,130 @@ + $accountNumber, + 'start_date' => $startDate, + 'end_date' => $endDate + ]); + + // Convert dates to Carbon instances + $startDateCarbon = Carbon::parse($startDate); + $endDateCarbon = Carbon::parse($endDate); + + // Get opening balance (balance from previous day) + $openingBalanceDate = $startDateCarbon->copy()->subDay(); + $openingBalance = $this->getAccountBalance($accountNumber, $openingBalanceDate); + + // Get closing balance date (previous day from end date) + $closingBalanceDate = $endDateCarbon->copy()->subDay(); + $closingBalanceBase = $this->getAccountBalance($accountNumber, $closingBalanceDate); + + // Get transactions on end date + $transactionsOnEndDate = $this->getTransactionsOnDate($accountNumber, $endDate); + + // Calculate closing balance + $closingBalance = $closingBalanceBase + $transactionsOnEndDate; + + $result = [ + 'account_number' => $accountNumber, + 'period' => [ + 'start_date' => $startDate, + 'end_date' => $endDate + ], + 'opening_balance' => [ + 'date' => $openingBalanceDate->format('Y-m-d'), + 'balance' => $openingBalance, + 'formatted_balance' => number_format($openingBalance, 2) + ], + 'closing_balance' => [ + 'date' => $endDate, + 'balance' => $closingBalance, + 'formatted_balance' => number_format($closingBalance, 2), + 'base_balance' => [ + 'date' => $closingBalanceDate->format('Y-m-d'), + 'balance' => $closingBalanceBase, + 'formatted_balance' => number_format($closingBalanceBase, 2) + ], + 'transactions_on_end_date' => $transactionsOnEndDate, + 'formatted_transactions_on_end_date' => number_format($transactionsOnEndDate, 2) + ] + ]; + + Log::info('Balance summary calculated successfully', $result); + + return $result; + }); + } + + /** + * Get account balance for specific date + * + * @param string $accountNumber + * @param Carbon $date + * @return float + */ + private function getAccountBalance(string $accountNumber, Carbon $date): float + { + $balance = AccountBalance::where('account_number', $accountNumber) + ->where('period', $date->format('Ymd')) + ->value('actual_balance'); + + if ($balance === null) { + Log::warning('Account balance not found', [ + 'account_number' => $accountNumber, + 'date' => $date->format('Y-m-d'), + 'period' => $date->format('Ymd') + ]); + return 0.00; + } + + return (float) $balance; + } + + /** + * Get transactions on specific date + * + * @param string $accountNumber + * @param string $date + * @return float + */ + private function getTransactionsOnDate(string $accountNumber, string $date): float + { + $total = StmtEntry::where('account_number', $accountNumber) + ->whereDate('value_date', $date) + ->sum(DB::raw('CAST(amount_lcy AS DECIMAL(15,2))')); + + return (float) $total; + } + + /** + * Validate if account exists + * + * @param string $accountNumber + * @return bool + */ + public function validateAccount(string $accountNumber): bool + { + return AccountBalance::where('account_number', $accountNumber)->exists(); + } +}