feat(webstatement): tambah validasi cabang rekening dan update logika penyimpanan statement

### Perubahan Utama
- Tambah validasi untuk memverifikasi bahwa nomor rekening sesuai dengan cabang pengguna.
- Cegah transaksi untuk rekening yang terdaftar di cabang khusus (`ID0019999`).
- Perbaikan sistem untuk menangani kasus rekening yang tidak ditemukan di database.

### Detail Perubahan
1. **Validasi Cabang Rekening**:
   - Tambah pengecekan untuk memastikan rekening yang dimasukkan adalah milik cabang pengguna (non-multi-branch).
   - Blokir transaksi jika rekening terdaftar pada cabang khusus (`ID0019999`) dengan menampilkan pesan error yang relevan.
   - Tambahkan pesan error jika nomor rekening tidak ditemukan dalam sistem.

2. **Update Logika Penyimpanan**:
   - Tambahkan validasi untuk mengisi kolom `branch_code` secara otomatis berdasarkan informasi rekening terkait.
   - Otomatis atur nilai awal `authorization_status` menjadi `approved`.

3. **Penghapusan Atribut Tidak Digunakan**:
   - Hapus form `branch_code` dari view terkait (`index.blade.php`) karena sekarang diisi secara otomatis berdasarkan data rekening.

4. **Perbaikan View dan Logika Terkait Status Otorisasi**:
   - Hapus logic dan elemen UI terkait `authorization_status` di halaman statement (`index.blade.php` dan `show.blade.php`).
   - Simplifikasi tampilan untuk hanya menampilkan informasi yang tersedia dan relevan.

5. **Optimasi Query Data Cabang**:
   - Update query untuk memfilter cabang berdasarkan kondisi `customer_company` dan mengecualikan kode cabang khusus.

6. **Penyesuaian Struktur Request**:
   - Hapus validasi terkait `branch_code` di `PrintStatementRequest` karena tidak lagi relevan.

7. **Log Aktivitas dan Kesalahan**:
   - Tambahkan log untuk mencatat aktivitas seperti validasi rekening dan penyimpanan batch data.
   - Penanganan lebih baik untuk logging jika terjadi error saat validasi nomor rekening atau penyimpanan statement.

### Manfaat Perubahan
- Meningkatkan akurasi data cabang dan validasi rekening sebelum penyimpanan.
- Menyederhanakan antarmuka pengguna dengan menghapus field input redundant.
- Memastikan proses menjadi lebih transparan dengan penanganan error yang lebih baik.

Langkah ini diterapkan untuk meningkatkan keamanan dan keandalan sistem dalam memverifikasi dan memproses pemintaan statement.
This commit is contained in:
daengdeni
2025-06-20 13:59:58 +07:00
parent 6035c61cc4
commit 8fb16028d9
5 changed files with 98 additions and 110 deletions

View File

@@ -14,6 +14,7 @@
use Modules\Basicdata\Models\Branch;
use Modules\Webstatement\Http\Requests\PrintStatementRequest;
use Modules\Webstatement\Mail\StatementEmail;
use Modules\Webstatement\Models\Account;
use Modules\Webstatement\Models\PrintStatementLog;
use ZipArchive;
@@ -24,7 +25,10 @@
*/
public function index(Request $request)
{
$branches = Branch::orderBy('name')->get();
$branches = Branch::whereNotNull('customer_company')
->where('code', '!=', 'ID0019999')
->orderBy('name')
->get();
return view('webstatement::statements.index', compact('branches'));
}
@@ -35,32 +39,66 @@
*/
public function store(PrintStatementRequest $request)
{
// Add account verification before storing
$accountNumber = $request->input('account_number'); // Assuming this is the field name for account number
// First, check if the account exists and get branch information
$account = Account::where('account_number', $accountNumber)->first();
if ($account) {
$branch_code = $account->branch_code;
$userBranchId = session('branch_id'); // Assuming branch ID is stored in session
$multiBranch = session('MULTI_BRANCH');
if (!$multiBranch) {
// Check if account branch matches user's branch
if ($account->branch_id !== $userBranchId) {
return redirect()->route('statements.index')
->with('error', 'Nomor rekening tidak sesuai dengan cabang Anda. Transaksi tidak dapat dilanjutkan.');
}
}
// Check if account belongs to restricted branch ID0019999
if ($account->branch_id === 'ID0019999') {
return redirect()->route('statements.index')
->with('error', 'Nomor rekening terdaftar pada cabang khusus. Silakan hubungi bagian HC untuk informasi lebih lanjut.');
}
// If all checks pass, proceed with storing data
// Your existing store logic here
} else {
// Account not found
return redirect()->route('statements.index')
->with('error', 'Nomor rekening tidak ditemukan dalam sistem.');
}
DB::beginTransaction();
try {
$validated = $request->validated();
// Add user tracking data dan field baru untuk single account request
$validated['user_id'] = Auth::id();
$validated['created_by'] = Auth::id();
$validated['ip_address'] = $request->ip();
$validated['user_agent'] = $request->userAgent();
$validated['request_type'] = 'single_account'; // Default untuk request manual
$validated['status'] = 'pending'; // Status awal
$validated['total_accounts'] = 1; // Untuk single account
$validated['user_id'] = Auth::id();
$validated['created_by'] = Auth::id();
$validated['ip_address'] = $request->ip();
$validated['user_agent'] = $request->userAgent();
$validated['request_type'] = 'single_account'; // Default untuk request manual
$validated['status'] = 'pending'; // Status awal
$validated['authorization_status'] = 'approved'; // Status otorisasi awal
$validated['total_accounts'] = 1; // Untuk single account
$validated['processed_accounts'] = 0;
$validated['success_count'] = 0;
$validated['failed_count'] = 0;
$validated['success_count'] = 0;
$validated['failed_count'] = 0;
$validated['branch_code'] = $branch_code; // Awal tidak tersedia
// Create the statement log
$statement = PrintStatementLog::create($validated);
// Log aktivitas
Log::info('Statement request created', [
'statement_id' => $statement->id,
'user_id' => Auth::id(),
'statement_id' => $statement->id,
'user_id' => Auth::id(),
'account_number' => $statement->account_number,
'request_type' => $statement->request_type
'request_type' => $statement->request_type
]);
// Process statement availability check
@@ -69,18 +107,18 @@
DB::commit();
return redirect()->route('statements.index')
->with('success', 'Statement request has been created successfully.');
->with('success', 'Statement request has been created successfully.');
} catch (Exception $e) {
DB::rollBack();
Log::error('Failed to create statement request', [
'error' => $e->getMessage(),
'error' => $e->getMessage(),
'user_id' => Auth::id()
]);
return redirect()->back()
->withInput()
->with('error', 'Failed to create statement request: ' . $e->getMessage());
->withInput()
->with('error', 'Failed to create statement request: ' . $e->getMessage());
}
}
@@ -102,19 +140,19 @@
DB::beginTransaction();
try {
$disk = Storage::disk('sftpStatement');
$disk = Storage::disk('sftpStatement');
$filePath = "{$statement->period_from}/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
if ($statement->is_period_range && $statement->period_to) {
$periodFrom = Carbon::createFromFormat('Ym', $statement->period_from);
$periodTo = Carbon::createFromFormat('Ym', $statement->period_to);
$periodTo = Carbon::createFromFormat('Ym', $statement->period_to);
$missingPeriods = [];
$missingPeriods = [];
$availablePeriods = [];
for ($period = clone $periodFrom; $period->lte($periodTo); $period->addMonth()) {
$periodFormatted = $period->format('Ym');
$periodPath = $periodFormatted . "/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
$periodPath = $periodFormatted . "/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
if ($disk->exists($periodPath)) {
$availablePeriods[] = $periodFormatted;
@@ -127,55 +165,55 @@
$notes = "Missing periods: " . implode(', ', $missingPeriods);
$statement->update([
'is_available' => false,
'remarks' => $notes,
'updated_by' => Auth::id(),
'status' => 'failed'
'remarks' => $notes,
'updated_by' => Auth::id(),
'status' => 'failed'
]);
Log::warning('Statement not available - missing periods', [
'statement_id' => $statement->id,
'statement_id' => $statement->id,
'missing_periods' => $missingPeriods
]);
} else {
$statement->update([
'is_available' => true,
'updated_by' => Auth::id(),
'status' => 'completed',
'is_available' => true,
'updated_by' => Auth::id(),
'status' => 'completed',
'processed_accounts' => 1,
'success_count' => 1
'success_count' => 1
]);
Log::info('Statement available - all periods found', [
'statement_id' => $statement->id,
'statement_id' => $statement->id,
'available_periods' => $availablePeriods
]);
}
} else if ($disk->exists($filePath)) {
$statement->update([
'is_available' => true,
'updated_by' => Auth::id(),
'status' => 'completed',
'is_available' => true,
'updated_by' => Auth::id(),
'status' => 'completed',
'processed_accounts' => 1,
'success_count' => 1
'success_count' => 1
]);
Log::info('Statement available', [
'statement_id' => $statement->id,
'file_path' => $filePath
'file_path' => $filePath
]);
} else {
$statement->update([
'is_available' => false,
'updated_by' => Auth::id(),
'status' => 'failed',
'is_available' => false,
'updated_by' => Auth::id(),
'status' => 'failed',
'processed_accounts' => 1,
'failed_count' => 1,
'error_message' => 'Statement file not found'
'failed_count' => 1,
'error_message' => 'Statement file not found'
]);
Log::warning('Statement not available', [
'statement_id' => $statement->id,
'file_path' => $filePath
'file_path' => $filePath
]);
}
@@ -185,14 +223,14 @@
Log::error('Error checking statement availability', [
'statement_id' => $statement->id,
'error' => $e->getMessage()
'error' => $e->getMessage()
]);
$statement->update([
'is_available' => false,
'status' => 'failed',
'is_available' => false,
'status' => 'failed',
'error_message' => $e->getMessage(),
'updated_by' => Auth::id()
'updated_by' => Auth::id()
]);
}
}
@@ -223,19 +261,19 @@
$statement->update([
'is_downloaded' => true,
'downloaded_at' => now(),
'updated_by' => Auth::id()
'updated_by' => Auth::id()
]);
Log::info('Statement downloaded', [
'statement_id' => $statement->id,
'user_id' => Auth::id(),
'statement_id' => $statement->id,
'user_id' => Auth::id(),
'account_number' => $statement->account_number
]);
DB::commit();
// Generate or fetch the statement file
$disk = Storage::disk('sftpStatement');
$disk = Storage::disk('sftpStatement');
$filePath = "{$statement->period_from}/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
if ($statement->is_period_range && $statement->period_to) {
@@ -249,7 +287,7 @@
Log::error('Failed to download statement', [
'statement_id' => $statement->id,
'error' => $e->getMessage()
'error' => $e->getMessage()
]);
return back()->with('error', 'Failed to download statement: ' . $e->getMessage());
@@ -337,13 +375,13 @@
// Map frontend column names to database column names if needed
$columnMap = [
'branch' => 'branch_code',
'account' => 'account_number',
'period' => 'period_from',
'auth_status' => 'authorization_status',
'branch' => 'branch_code',
'account' => 'account_number',
'period' => 'period_from',
'auth_status' => 'authorization_status',
'request_type' => 'request_type',
'status' => 'status',
'remarks' => 'remarks',
'status' => 'status',
'remarks' => 'remarks',
];
$dbColumn = $columnMap[$column] ?? $column;
@@ -541,8 +579,8 @@
Log::info('Statement email sent successfully', [
'statement_id' => $statement->id,
'email' => $statement->email,
'user_id' => Auth::id()
'email' => $statement->email,
'user_id' => Auth::id()
]);
DB::commit();

View File

@@ -21,7 +21,6 @@ class PrintStatementRequest extends FormRequest
public function rules(): array
{
$rules = [
'branch_code' => ['required', 'string', 'exists:branches,code'],
'account_number' => ['required', 'string'],
'is_period_range' => ['sometimes', 'boolean'],
'email' => ['nullable', 'email'],
@@ -77,8 +76,6 @@ class PrintStatementRequest extends FormRequest
public function messages(): array
{
return [
'branch_code.required' => 'Branch code is required',
'branch_code.exists' => 'Selected branch does not exist',
'account_number.required' => 'Account number is required',
'period_from.required' => 'Period is required',
'period_from.regex' => 'Period must be in YYYYMM format',

View File

@@ -30,7 +30,8 @@
"attributes": [],
"permission": "",
"roles": [
"administrator"
"administrator",
"customer_service"
]
},
{

View File

@@ -18,21 +18,6 @@
@endif
<div class="grid grid-cols-1 gap-5">
<div class="form-group">
<label class="form-label required" for="branch_code">Branch</label>
<select class="select tomselect @error('branch_code') is-invalid @enderror" id="branch_code" name="branch_code" required>
<option value="">Select Branch</option>
@foreach($branches as $branch)
<option value="{{ $branch->code }}" {{ (old('branch_code', $statement->branch_code ?? '') == $branch->code) ? 'selected' : '' }}>
{{ $branch->name }}
</option>
@endforeach
</select>
@error('branch_code')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label class="form-label required" for="account_number">Account Number</label>
<input type="text" class="input form-control @error('account_number') is-invalid @enderror" id="account_number" name="account_number" value="{{ old('account_number', $statement->account_number ?? '') }}" required>
@@ -122,10 +107,6 @@
<span class="sort"> <span class="sort-label"> Period </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="authorization_status">
<span class="sort"> <span class="sort-label"> Status </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="is_available">
<span class="sort"> <span class="sort-label"> Available </span>
<span class="sort-icon"> </span> </span>
@@ -243,22 +224,6 @@
return fromPeriod + toPeriod;
},
},
authorization_status: {
title: 'Status',
render: (item, data) => {
let statusClass = 'badge badge-light-primary';
if (data.authorization_status === 'approved') {
statusClass = 'badge badge-light-success';
} else if (data.authorization_status === 'rejected') {
statusClass = 'badge badge-light-danger';
} else if (data.authorization_status === 'pending') {
statusClass = 'badge badge-light-warning';
}
return `<span class="${statusClass}">${data.authorization_status}</span>`;
},
},
is_available: {
title: 'Available',
render: (item, data) => {

View File

@@ -73,19 +73,6 @@
</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Status</div>
<div>
@if($statement->authorization_status === 'pending')
<span class="badge badge-warning">Pending Authorization</span>
@elseif($statement->authorization_status === 'approved')
<span class="badge badge-success">Approved</span>
@elseif($statement->authorization_status === 'rejected')
<span class="badge badge-danger">Rejected</span>
@endif
</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Availability</div>
<div>