- **Optimalisasi Pembuatan dan Pengunduhan PDF:** - Tambahkan validasi untuk mengecek keberadaan file sebelum pembuatan ulang PDF. - Tambahkan mekanisme pengunduhan langsung file PDF jika sudah tersedia di storage. - Mengelompokkan path penyimpanan dan nama file PDF secara dinamis berdasarkan periode dan nomor rekening. - Integrasi update status `is_available` pada tabel `PrintStatementLog` setelah file berhasil dibuat. - **Refaktor dan Perbaikan Logika Pembuatan PDF:** - Perbarui fungsi `generateStatementPdf` untuk mendukung parameter tambahan terkait penyimpanan file. - Hapus duplikasi logika terkait pembuatan path storage dan file PDF. - Tambahkan handling error untuk memastikan file PDF berhasil dibuat. - **Hapus Fitur Preview PDF:** - Menghapus fungsi `previewPdf()` dan rute terkait karena sudah tidak digunakan. - Membersihkan bagian UI yang merujuk pada fitur ini. - **Peningkatan UI dan Tampilan:** - Menghapus kolom `authorization_status` pada tabel karena tidak relevan. - Penyesuaian styling pada kolom `is_available` untuk menampilkan status ketersediaan file. - **Perubahan pada Rute:** - Membersihkan rute tidak terpakai terkait preview dan generate PDF langsung. - **Refaktor Umum:** - Menghapus kode redundan yang berhubungan dengan penyimpanan file sementara. - Penyesuaian struktur fungsi untuk meningkatkan keterbacaan dan efisiensi. Perubahan ini memastikan proses PDF statement lebih efisien dengan validasi ketat, pengelolaan file yang lebih baik, serta penyederhanaan sistem dengan menghapus fitur yang tidak relevan. Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
491 lines
30 KiB
PHP
491 lines
30 KiB
PHP
@extends('layouts.main')
|
|
|
|
@section('breadcrumbs')
|
|
{{ Breadcrumbs::render(request()->route()->getName()) }}
|
|
@endsection
|
|
|
|
@section('content')
|
|
<div class="grid grid-cols-8 gap-5">
|
|
<div class="col-span-2 card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Request Print Stetement</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<form
|
|
action="{{ isset($statement) ? route('statements.update', $statement->id) : route('statements.store') }}"
|
|
method="POST">
|
|
@csrf
|
|
@if (isset($statement))
|
|
@method('PUT')
|
|
@endif
|
|
|
|
<div class="grid grid-cols-1 gap-5">
|
|
@if ($multiBranch)
|
|
<div class="form-group">
|
|
<label class="form-label required" for="branch_code">Branch/Cabang</label>
|
|
<select
|
|
class="input form-control tomselect @error('branch_code') border-danger bg-danger-light @enderror"
|
|
id="branch_code" name="branch_code" required>
|
|
<option value="">Pilih Branch/Cabang</option>
|
|
@foreach ($branches as $branchOption)
|
|
<option value="{{ $branchOption->code }}"
|
|
{{ old('branch_code', $statement->branch_code ?? ($branch->code ?? '')) == $branchOption->code ? 'selected' : '' }}>
|
|
{{ $branchOption->code }} - {{ $branchOption->name }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
@error('branch_code')
|
|
<div class="text-sm alert text-danger">{{ $message }}</div>
|
|
@enderror
|
|
</div>
|
|
@else
|
|
<div class="form-group">
|
|
<label class="form-label" for="branch_display">Branch/Cabang</label>
|
|
<input type="text" class="input form-control" id="branch_display"
|
|
value="{{ $branch->code ?? '' }} - {{ $branch->name ?? '' }}" readonly>
|
|
<input type="hidden" name="branch_code" value="{{ $branch->code ?? '' }}">
|
|
@error('branch_code')
|
|
<div class="text-sm alert text-danger">{{ $message }}</div>
|
|
@enderror
|
|
</div>
|
|
@endif
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="stmt_sent_type">Statement Type</label>
|
|
<select
|
|
class="select tomselect @error('stmt_sent_type') border-danger bg-danger-light @enderror"
|
|
id="stmt_sent_type" name="stmt_sent_type[]" multiple>
|
|
<option value="ALL"
|
|
{{ in_array('ALL', old('stmt_sent_type', $statement->stmt_sent_type ?? [])) ? 'selected' : '' }}>
|
|
ALL
|
|
</option>
|
|
<option value="BY.EMAIL"
|
|
{{ in_array('BY.EMAIL', old('stmt_sent_type', $statement->stmt_sent_type ?? [])) ? 'selected' : '' }}>
|
|
BY EMAIL
|
|
</option>
|
|
<option value="BY.MAIL.TO.DOM.ADDR"
|
|
{{ in_array('BY.MAIL.TO.DOM.ADDR', old('stmt_sent_type', $statement->stmt_sent_type ?? [])) ? 'selected' : '' }}>
|
|
BY MAIL TO DOM ADDR
|
|
</option>
|
|
<option value="BY.MAIL.TO.KTP.ADDR"
|
|
{{ in_array('BY.MAIL.TO.KTP.ADDR', old('stmt_sent_type', $statement->stmt_sent_type ?? [])) ? 'selected' : '' }}>
|
|
BY MAIL TO KTP ADDR
|
|
</option>
|
|
<option value="NO.PRINT"
|
|
{{ in_array('NO.PRINT', old('stmt_sent_type', $statement->stmt_sent_type ?? [])) ? 'selected' : '' }}>
|
|
NO PRINT
|
|
</option>
|
|
<option value="PRINT"
|
|
{{ in_array('PRINT', old('stmt_sent_type', $statement->stmt_sent_type ?? [])) ? 'selected' : '' }}>
|
|
PRINT
|
|
</option>
|
|
</select>
|
|
@error('stmt_sent_type')
|
|
<div class="text-sm alert text-danger">{{ $message }}</div>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="account_number">Account Number</label>
|
|
<input type="text"
|
|
class="input form-control @error('account_number') border-danger bg-danger-light @enderror"
|
|
id="account_number" name="account_number"
|
|
value="{{ old('account_number', $statement->account_number ?? '') }}">
|
|
@error('account_number')
|
|
<div class="text-sm alert text-danger">{{ $message }}</div>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="email">Email</label>
|
|
<input type="email"
|
|
class="input form-control @error('email') border-danger bg-danger-light @enderror"
|
|
id="email" name="email" value="{{ old('email', $statement->email ?? '') }}"
|
|
placeholder="Optional email for send statement">
|
|
@error('email')
|
|
<div class="text-sm alert text-danger">{{ $message }}</div>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label required" for="start_date">Start Date</label>
|
|
|
|
<input class="input @error('period_from') border-danger bg-danger-light @enderror"
|
|
type="month" name="period_from"
|
|
value="{{ $statement->period_from ?? old('period_from') }}"
|
|
max="{{ date('Y-m', strtotime('-1 month')) }}">
|
|
@error('period_from')
|
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
|
@enderror
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label required" for="end_date">End Date</label>
|
|
<input class="input @error('period_to') border-danger bg-danger-light @enderror" type="month"
|
|
name="period_to" value="{{ $statement->period_to ?? old('period_to') }}"
|
|
max="{{ date('Y-m', strtotime('-1 month')) }}">
|
|
@error('period_to')
|
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-5 text-end">
|
|
<button type="reset" class="btn btn-light me-3">Reset</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="ki-outline ki-check fs-2"></i> Submit
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-span-6">
|
|
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="10"
|
|
data-datatable-state-save="false" id="statement-table" data-api-url="{{ route('statements.datatables') }}">
|
|
<div class="flex-wrap py-5 card-header">
|
|
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="10"
|
|
data-datatable-state-save="false" id="statement-table"
|
|
data-api-url="{{ route('statements.datatables') }}">
|
|
<div class="flex-wrap py-5 card-header">
|
|
<h3 class="card-title">
|
|
Daftar Statement Request
|
|
</h3>
|
|
<div class="flex flex-wrap gap-2 lg:gap-5">
|
|
<div class="flex">
|
|
<label class="input input-sm"> <i class="ki-filled ki-magnifier"> </i>
|
|
<input placeholder="Search Statement" id="search" type="text"
|
|
value="">
|
|
</label>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="scrollable-x-auto">
|
|
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
|
|
data-datatable-table="true">
|
|
<thead>
|
|
<tr>
|
|
<th class="w-14">
|
|
<input class="checkbox checkbox-sm" data-datatable-check="true"
|
|
type="checkbox" />
|
|
</th>
|
|
<th class="min-w-[100px]" data-datatable-column="id">
|
|
<span class="sort"> <span class="sort-label"> ID </span>
|
|
<span class="sort-icon"> </span> </span>
|
|
</th>
|
|
<th class="min-w-[150px]" data-datatable-column="branch_name">
|
|
<span class="sort"> <span class="sort-label"> Branch </span>
|
|
<span class="sort-icon"> </span> </span>
|
|
</th>
|
|
<th class="min-w-[150px]" data-datatable-column="account_number">
|
|
<span class="sort"> <span class="sort-label"> Account Number </span>
|
|
<span class="sort-icon"> </span> </span>
|
|
</th>
|
|
<th class="min-w-[150px]" data-datatable-column="period">
|
|
<span class="sort"> <span class="sort-label"> Period </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>
|
|
</th>
|
|
<th class="min-w-[150px]" data-datatable-column="is_generated">
|
|
<span class="sort"> <span class="sort-label"> Generated </span>
|
|
<span class="sort-icon"> </span> </span>
|
|
</th>
|
|
<th class="min-w-[150px]" data-datatable-column="remarks">
|
|
<span class="sort"> <span class="sort-label"> Notes </span>
|
|
<span class="sort-icon"> </span> </span>
|
|
</th>
|
|
<th class="min-w-[180px]" data-datatable-column="created_at">
|
|
<span class="sort"> <span class="sort-label"> Created At </span>
|
|
<span class="sort-icon"> </span> </span>
|
|
</th>
|
|
<th class="min-w-[50px] text-center" data-datatable-column="actions">
|
|
Action</th>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
<div
|
|
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
|
|
<div class="flex gap-2 items-center">
|
|
<div
|
|
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
|
|
<div class="flex gap-2 items-center">
|
|
Show
|
|
<select class="w-16 select select-sm" data-datatable-size="true"
|
|
name="perpage"> </select>
|
|
per page
|
|
<select class="w-16 select select-sm" data-datatable-size="true"
|
|
name="perpage"> </select>
|
|
per page
|
|
</div>
|
|
<div class="flex gap-4 items-center">
|
|
<div class="flex gap-4 items-center">
|
|
<span data-datatable-info="true"> </span>
|
|
<div class="pagination" data-datatable-pagination="true">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script type="text/javascript">
|
|
/**
|
|
* Fungsi untuk menghapus data statement
|
|
* @param {number} data - ID statement yang akan dihapus
|
|
*/
|
|
function deleteData(data) {
|
|
Swal.fire({
|
|
title: 'Are you sure?',
|
|
text: "You won't be able to revert this!",
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#3085d6',
|
|
cancelButtonColor: '#d33',
|
|
confirmButtonText: 'Yes, delete it!'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
$.ajaxSetup({
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
}
|
|
});
|
|
|
|
$.ajax(`statements/${data}`, {
|
|
type: 'DELETE'
|
|
}).then((response) => {
|
|
swal.fire('Deleted!', 'Statement request has been deleted.', 'success').then(() => {
|
|
window.location.reload();
|
|
});
|
|
}).catch((error) => {
|
|
console.error('Error:', error);
|
|
Swal.fire('Error!', 'An error occurred while deleting the record.', 'error');
|
|
});
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Konfirmasi email sebelum submit form
|
|
* Menampilkan SweetAlert jika email diisi untuk konfirmasi pengiriman
|
|
*/
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const form = document.querySelector('form');
|
|
const emailInput = document.getElementById('email');
|
|
|
|
// Log: Inisialisasi event listener untuk konfirmasi email
|
|
console.log('Email confirmation listener initialized');
|
|
|
|
form.addEventListener('submit', function(e) {
|
|
const emailValue = emailInput.value.trim();
|
|
|
|
// Jika email diisi, tampilkan konfirmasi
|
|
if (emailValue) {
|
|
e.preventDefault(); // Hentikan submit form sementara
|
|
|
|
// Log: Email terdeteksi, menampilkan konfirmasi
|
|
console.log('Email detected:', emailValue);
|
|
|
|
Swal.fire({
|
|
title: 'Konfirmasi Pengiriman Email',
|
|
text: `Apakah Anda yakin ingin mengirimkan statement ke email: ${emailValue}?`,
|
|
icon: 'question',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#3085d6',
|
|
cancelButtonColor: '#d33',
|
|
confirmButtonText: 'Ya, Kirim Email',
|
|
cancelButtonText: 'Batal',
|
|
reverseButtons: true
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
// Log: User konfirmasi pengiriman email
|
|
console.log('User confirmed email sending');
|
|
|
|
// Submit form setelah konfirmasi
|
|
form.submit();
|
|
} else {
|
|
// Log: User membatalkan pengiriman email
|
|
console.log('User cancelled email sending');
|
|
}
|
|
});
|
|
} else {
|
|
// Log: Tidak ada email, submit form normal
|
|
console.log('No email provided, submitting form normally');
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<script type="module">
|
|
const element = document.querySelector('#statement-table');
|
|
const searchInput = document.getElementById('search');
|
|
|
|
const apiUrl = element.getAttribute('data-api-url');
|
|
const dataTableOptions = {
|
|
apiEndpoint: apiUrl,
|
|
pageSize: 10,
|
|
columns: {
|
|
select: {
|
|
render: (item, data, context) => {
|
|
const checkbox = document.createElement('input');
|
|
checkbox.className = 'checkbox checkbox-sm';
|
|
checkbox.type = 'checkbox';
|
|
checkbox.value = data.id.toString();
|
|
checkbox.setAttribute('data-datatable-row-check', 'true');
|
|
return checkbox.outerHTML.trim();
|
|
},
|
|
},
|
|
id: {
|
|
title: 'ID',
|
|
},
|
|
branch_name: {
|
|
title: 'Branch',
|
|
},
|
|
account_number: {
|
|
title: 'Account Number',
|
|
},
|
|
period: {
|
|
title: 'Period',
|
|
render: (item, data) => {
|
|
const monthNames = [
|
|
'January', 'February', 'March', 'April', 'May', 'June',
|
|
'July', 'August', 'September', 'October', 'November', 'December'
|
|
];
|
|
|
|
const formatPeriod = (period) => {
|
|
if (!period) return '';
|
|
const year = period.substring(0, 4);
|
|
const month = parseInt(period.substring(4, 6));
|
|
return `${monthNames[month - 1]} ${year}`;
|
|
};
|
|
|
|
const fromPeriod = formatPeriod(data.period_from);
|
|
const toPeriod = data.period_to ? ` - ${formatPeriod(data.period_to)}` : '';
|
|
|
|
return fromPeriod + toPeriod;
|
|
},
|
|
},
|
|
is_available: {
|
|
title: 'Available',
|
|
render: (item, data) => {
|
|
let statusClass = data.is_available ? 'badge badge-light-success' :
|
|
|
|
'badge badge-light-danger';
|
|
let statusText = data.is_available ? 'Yes' : 'No';
|
|
return `<span class="${statusClass}">${statusText}</span>`;
|
|
},
|
|
},
|
|
is_generated: {
|
|
title: 'Generated',
|
|
render: (item, data) => {
|
|
let statusClass = data.is_generated ? 'badge badge-light-success' :
|
|
'badge badge-light-danger';
|
|
let statusText = data.is_generated ? 'Yes' : 'No';
|
|
return `<span class="${statusClass}">${statusText}</span>`;
|
|
},
|
|
},
|
|
remarks: {
|
|
title: 'Notes',
|
|
},
|
|
created_at: {
|
|
title: 'Created At',
|
|
render: (item, data) => {
|
|
return data.created_at ?? '';
|
|
},
|
|
},
|
|
actions: {
|
|
title: 'Actions',
|
|
render: (item, data) => {
|
|
let buttons = `<div class="flex flex-nowrap justify-center">
|
|
<a class="btn btn-sm btn-icon btn-clear btn-info" href="statements/${data.id}">
|
|
<i class="ki-outline ki-eye"></i>
|
|
</a>`;
|
|
|
|
// Show download button if statement is approved and available but not downloaded
|
|
//if (data.authorization_status === 'approved' && data.is_available && !data.is_downloaded) {
|
|
if (data.is_available) {
|
|
buttons += `<a class="btn btn-sm btn-icon btn-clear btn-success" href="statements/${data.id}/download">
|
|
<i class="ki-outline ki-cloud-download"></i>
|
|
</a>`;
|
|
}
|
|
|
|
// Show send email button if email is not empty and statement is available
|
|
if (data.is_available && data.email) {
|
|
buttons += `<a class="btn btn-sm btn-icon btn-clear btn-primary" href="statements/${data.id}/send-email" title="Send to Email">
|
|
<i class="ki-outline ki-paper-plane"></i>
|
|
</a>`;
|
|
}
|
|
|
|
// Only show delete button if status is pending
|
|
if (data.authorization_status === 'pending') {
|
|
buttons += `<a onclick="deleteData(${data.id})" class="delete btn btn-sm btn-icon btn-clear btn-danger">
|
|
<i class="ki-outline ki-trash"></i>
|
|
</a>`;
|
|
}
|
|
|
|
buttons += `</div>`;
|
|
return buttons;
|
|
},
|
|
}
|
|
},
|
|
};
|
|
|
|
let dataTable = new KTDataTable(element, dataTableOptions);
|
|
// Custom search functionality
|
|
searchInput.addEventListener('input', function() {
|
|
searchInput.addEventListener('input', function() {
|
|
const searchValue = this.value.trim();
|
|
dataTable.search(searchValue, true);
|
|
});
|
|
|
|
// Handle the "select all" checkbox
|
|
const selectAllCheckbox = document.querySelector('input[data-datatable-check="true"]');
|
|
if (selectAllCheckbox) {
|
|
selectAllCheckbox.addEventListener('change', function() {
|
|
const isChecked = this.checked;
|
|
const rowCheckboxes = document.querySelectorAll(
|
|
'input[data-datatable-row-check="true"]');
|
|
|
|
rowCheckboxes.forEach(checkbox => {
|
|
checkbox.checked = isChecked;
|
|
});
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Validate that end date is after start date
|
|
const startDateInput = document.getElementById('start_date');
|
|
const endDateInput = document.getElementById('end_date');
|
|
|
|
function validateDates() {
|
|
const startDate = new Date(startDateInput.value);
|
|
const endDate = new Date(endDateInput.value);
|
|
|
|
if (startDate > endDate) {
|
|
endDateInput.setCustomValidity('End date must be after start date');
|
|
} else {
|
|
endDateInput.setCustomValidity('');
|
|
}
|
|
}
|
|
|
|
startDateInput.addEventListener('change', validateDates);
|
|
endDateInput.addEventListener('change', validateDates);
|
|
|
|
// Set max date for date inputs to today
|
|
const today = new Date().toISOString().split('T')[0];
|
|
startDateInput.setAttribute('max', today);
|
|
endDateInput.setAttribute('max', today);
|
|
});
|
|
</script>
|
|
@endpush
|