feat(webstatement): tambahkan fitur retry dengan handling timeout pada laporan transaksi ATM
- Memperbarui logika retry pada `AtmTransactionReportController`: - Memperbolehkan retry jika status laporan adalah `failed`, `pending`, atau laporan dengan status `processing` yang telah melebihi batas waktu (1 jam). - Menambahkan atribut baru seperti `processing_hours` dan `is_processing_timeout` pada data untuk menampilkan informasi durasi proses dan flag timeout. - Mengubah status laporan menjadi `failed` jika melebihi batas waktu sebelum dilakukan retry. - Memperbarui error message untuk mencatat alasan timeout. - Menambahkan metode baru `canRetry` pada controller: - Mengembalikan boolean jika laporan dapat di-retry berdasarkan status dan kondisi laporan. - Memperbarui tampilan untuk bagian daftar laporan (`atm-reports/index.blade.php`): - Menambahkan tombol retry dengan warna yang disesuaikan (kuning/oranye untuk `failed`/`pending`, merah untuk timeout). - Memperbarui tampilan status laporan menjadi lebih informatif, termasuk keterangan durasi proses jika timeout. - Memperbarui tampilan detail laporan (`atm-reports/show.blade.php`): - Menambahkan tombol retry dengan label tambahan "Retry (Timeout)" jika melebihi batas waktu proses. - Menampilkan informasi tambahan seperti waktu proses jika laporan dalam status `processing`. - Menyediakan fungsi JavaScript baru `retryReport` untuk handling retry via AJAX: - Menyertakan konfirmasi sebelum retry. - Memperbarui tombol retry agar lebih reaktif terhadap perubahan status laporan. Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
This commit is contained in:
@@ -210,11 +210,17 @@ class AtmTransactionReportController extends Controller
|
|||||||
|
|
||||||
// Get the data for the current page
|
// Get the data for the current page
|
||||||
$data = $query->get()->map(function ($item) {
|
$data = $query->get()->map(function ($item) {
|
||||||
|
$processingHours = $item->status === 'processing' ? $item->updated_at->diffInHours(now()) : 0;
|
||||||
|
$isProcessingTimeout = $item->status === 'processing' && $processingHours >= 1;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => $item->id,
|
'id' => $item->id,
|
||||||
'period' => $item->period,
|
'period' => $item->period,
|
||||||
'report_date' => Carbon::createFromFormat('Ymd', $item->period)->format('Y-m-d'),
|
'report_date' => Carbon::createFromFormat('Ymd', $item->period)->format('Y-m-d'),
|
||||||
'status' => $item->status,
|
'status' => $item->status,
|
||||||
|
'status_display' => $item->status . ($isProcessingTimeout ? ' (Timeout)' : ''),
|
||||||
|
'processing_hours' => $processingHours,
|
||||||
|
'is_processing_timeout' => $isProcessingTimeout,
|
||||||
'authorization_status' => $item->authorization_status,
|
'authorization_status' => $item->authorization_status,
|
||||||
'is_downloaded' => $item->is_downloaded,
|
'is_downloaded' => $item->is_downloaded,
|
||||||
'created_at' => dateFormat($item->created_at, 1, 1),
|
'created_at' => dateFormat($item->created_at, 1, 1),
|
||||||
@@ -222,6 +228,7 @@ class AtmTransactionReportController extends Controller
|
|||||||
'authorized_by' => $item->authorizer ? $item->authorizer->name : null,
|
'authorized_by' => $item->authorizer ? $item->authorizer->name : null,
|
||||||
'authorized_at' => $item->authorized_at ? $item->authorized_at->format('Y-m-d H:i:s') : null,
|
'authorized_at' => $item->authorized_at ? $item->authorized_at->format('Y-m-d H:i:s') : null,
|
||||||
'file_path' => $item->file_path,
|
'file_path' => $item->file_path,
|
||||||
|
'can_retry' => in_array($item->status, ['failed', 'pending']) || $isProcessingTimeout || ($item->status === 'completed' && !$item->file_path),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -298,13 +305,26 @@ class AtmTransactionReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function retry(AtmTransactionReportLog $atmReport)
|
public function retry(AtmTransactionReportLog $atmReport)
|
||||||
{
|
{
|
||||||
// Check if retry is allowed (only for failed or pending status)
|
// Check if retry is allowed (failed, pending, or processing for more than 1 hour)
|
||||||
if (!in_array($atmReport->status, ['failed', 'pending'])) {
|
$allowedStatuses = ['failed', 'pending'];
|
||||||
return back()->with('error', 'Report can only be retried if status is failed or pending.');
|
$isProcessingTooLong = $atmReport->status === 'processing' &&
|
||||||
|
$atmReport->updated_at->diffInHours(now()) >= 1;
|
||||||
|
|
||||||
|
if (!in_array($atmReport->status, $allowedStatuses) && !$isProcessingTooLong) {
|
||||||
|
return back()->with('error', 'Report can only be retried if status is failed, pending, or processing for more than 1 hour.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Reset the report status and clear error message
|
// If it was processing for too long, mark it as failed first
|
||||||
|
if ($isProcessingTooLong) {
|
||||||
|
$atmReport->update([
|
||||||
|
'status' => 'failed',
|
||||||
|
'error_message' => 'Processing timeout - exceeded 1 hour limit',
|
||||||
|
'updated_by' => Auth::id()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the report status and clear previous data
|
||||||
$atmReport->update([
|
$atmReport->update([
|
||||||
'status' => 'processing',
|
'status' => 'processing',
|
||||||
'error_message' => null,
|
'error_message' => null,
|
||||||
@@ -329,4 +349,18 @@ class AtmTransactionReportController extends Controller
|
|||||||
return back()->with('error', 'Failed to retry report generation: ' . $e->getMessage());
|
return back()->with('error', 'Failed to retry report generation: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if report can be retried
|
||||||
|
*/
|
||||||
|
public function canRetry(AtmTransactionReportLog $atmReport)
|
||||||
|
{
|
||||||
|
$allowedStatuses = ['failed', 'pending'];
|
||||||
|
$isProcessingTooLong = $atmReport->status === 'processing' &&
|
||||||
|
$atmReport->updated_at->diffInHours(now()) >= 1;
|
||||||
|
|
||||||
|
return in_array($atmReport->status, $allowedStatuses) ||
|
||||||
|
$isProcessingTooLong ||
|
||||||
|
($atmReport->status === 'completed' && !$atmReport->file_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
type: 'DELETE'
|
type: 'DELETE'
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
swal.fire('Deleted!', 'ATM Transaction report has been deleted.', 'success').then(
|
swal.fire('Deleted!', 'ATM Transaction report has been deleted.', 'success').then(
|
||||||
() => {
|
() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
@@ -143,6 +143,40 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the missing retryReport function
|
||||||
|
function retryReport(id) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Are you sure?',
|
||||||
|
text: 'This will reset the current job and start a new one.',
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#3085d6',
|
||||||
|
cancelButtonColor: '#d33',
|
||||||
|
confirmButtonText: 'Yes, retry it!'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
$.ajaxSetup({
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax(`atm-reports/${id}/retry`, {
|
||||||
|
type: 'POST'
|
||||||
|
}).then((response) => {
|
||||||
|
Swal.fire('Success!', 'Report retry initiated successfully.', 'success').then(
|
||||||
|
() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
Swal.fire('Error!', 'Failed to retry report: ' + (error.responseJSON?.message ||
|
||||||
|
'Unknown error'), 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -183,18 +217,24 @@
|
|||||||
title: 'Status',
|
title: 'Status',
|
||||||
render: (item, data) => {
|
render: (item, data) => {
|
||||||
let statusClass = 'badge badge-light-primary';
|
let statusClass = 'badge badge-light-primary';
|
||||||
|
let statusText = data.status;
|
||||||
|
|
||||||
if (data.status === 'completed') {
|
if (data.status === 'completed') {
|
||||||
statusClass = 'badge badge-light-success';
|
statusClass = 'badge badge-light-success';
|
||||||
} else if (data.status === 'failed') {
|
} else if (data.status === 'failed') {
|
||||||
statusClass = 'badge badge-light-danger';
|
statusClass = 'badge badge-light-danger';
|
||||||
} else if (data.status === 'processing') {
|
} else if (data.status === 'processing') {
|
||||||
statusClass = 'badge badge-light-warning';
|
if (data.is_processing_timeout) {
|
||||||
|
statusClass = 'badge badge-light-danger';
|
||||||
|
statusText += ` (${data.processing_hours}h)`;
|
||||||
|
} else {
|
||||||
|
statusClass = 'badge badge-light-warning';
|
||||||
|
}
|
||||||
} else if (data.status === 'pending') {
|
} else if (data.status === 'pending') {
|
||||||
statusClass = 'badge badge-light-info';
|
statusClass = 'badge badge-light-info';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<span class="${statusClass}">${data.status}</span>`;
|
return `<span class="${statusClass}">${statusText}</span>`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authorization_status: {
|
authorization_status: {
|
||||||
@@ -234,6 +274,17 @@
|
|||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry button
|
||||||
|
if (data.can_retry) {
|
||||||
|
let retryClass = 'btn-warning';
|
||||||
|
if (data.is_processing_timeout) {
|
||||||
|
retryClass = 'btn-danger';
|
||||||
|
}
|
||||||
|
buttons += `<button class="btn btn-sm btn-icon btn-clear ${retryClass}" onclick="retryReport(${data.id})">
|
||||||
|
<i class="ki-outline ki-arrows-circle"></i>
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
// Only show delete button if status is pending or failed
|
// Only show delete button if status is pending or failed
|
||||||
if (data.status === 'pending' || data.status === 'failed') {
|
if (data.status === 'pending' || data.status === 'failed') {
|
||||||
buttons += `<a onclick="deleteData(${data.id})" class="delete btn btn-sm btn-icon btn-clear btn-danger">
|
buttons += `<a onclick="deleteData(${data.id})" class="delete btn btn-sm btn-icon btn-clear btn-danger">
|
||||||
@@ -241,31 +292,6 @@
|
|||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Di bagian JavaScript untuk action buttons
|
|
||||||
if (data.status === 'failed' || data.status === 'pending' || (data.status === 'completed' && !data.file_path)) {
|
|
||||||
buttons += `<button class="btn btn-sm btn-icon btn-clear btn-warning" onclick="retryReport(${data.id})" title="Retry Job">
|
|
||||||
<i class="ki-duotone ki-arrows-circle fs-5"></i>
|
|
||||||
</button>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tambahkan function untuk retry
|
|
||||||
function retryReport(id) {
|
|
||||||
if (confirm('Are you sure you want to retry generating this report?')) {
|
|
||||||
$.ajax({
|
|
||||||
url: `atm-reports/${id}/retry`,
|
|
||||||
type: 'POST',
|
|
||||||
data: {
|
|
||||||
_token: $('meta[name="csrf-token"]').attr('content')
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
location.reload();
|
|
||||||
},
|
|
||||||
error: function(xhr) {
|
|
||||||
alert('Error: ' + xhr.responseJSON.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buttons += `</div>`;
|
buttons += `</div>`;
|
||||||
return buttons;
|
return buttons;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,11 +15,22 @@
|
|||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (in_array($atmReport->status, ['failed', 'pending']) || ($atmReport->status === 'completed' && !$atmReport->file_path))
|
@php
|
||||||
|
$canRetry = in_array($atmReport->status, ['failed', 'pending']) ||
|
||||||
|
($atmReport->status === 'processing' && $atmReport->updated_at->diffInHours(now()) >= 1) ||
|
||||||
|
($atmReport->status === 'completed' && !$atmReport->file_path);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if ($canRetry)
|
||||||
<form action="{{ route('atm-reports.retry', $atmReport->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to retry generating this report?')">
|
<form action="{{ route('atm-reports.retry', $atmReport->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to retry generating this report?')">
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="btn btn-sm btn-warning me-2">
|
<button type="submit" class="btn btn-sm btn-warning me-2">
|
||||||
<i class="ki-duotone ki-arrows-circle fs-2"></i>Retry Job
|
<i class="ki-duotone ki-arrows-circle fs-2"></i>
|
||||||
|
@if($atmReport->status === 'processing' && $atmReport->updated_at->diffInHours(now()) >= 1)
|
||||||
|
Retry (Timeout)
|
||||||
|
@else
|
||||||
|
Retry Job
|
||||||
|
@endif
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
@@ -72,7 +83,20 @@
|
|||||||
@if ($atmReport->status === 'pending')
|
@if ($atmReport->status === 'pending')
|
||||||
<span class="badge badge-info">Pending</span>
|
<span class="badge badge-info">Pending</span>
|
||||||
@elseif($atmReport->status === 'processing')
|
@elseif($atmReport->status === 'processing')
|
||||||
<span class="badge badge-warning">Processing</span>
|
@php
|
||||||
|
$processingHours = $atmReport->updated_at->diffInHours(now());
|
||||||
|
@endphp
|
||||||
|
<span class="badge {{ $processingHours >= 1 ? 'badge-danger' : 'badge-warning' }}">
|
||||||
|
Processing
|
||||||
|
@if($processingHours >= 1)
|
||||||
|
({{ $processingHours }}h - Timeout)
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
@if($processingHours >= 1)
|
||||||
|
<div class="mt-1 text-danger small">
|
||||||
|
Processing for more than 1 hour. You can retry this job.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@elseif($atmReport->status === 'completed')
|
@elseif($atmReport->status === 'completed')
|
||||||
<span class="badge badge-success">Completed</span>
|
<span class="badge badge-success">Completed</span>
|
||||||
@elseif($atmReport->status === 'failed')
|
@elseif($atmReport->status === 'failed')
|
||||||
|
|||||||
Reference in New Issue
Block a user