Compare commits
6 Commits
2c8f49af20
...
new
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4616137e0c | ||
|
|
19c962307e | ||
|
|
a79b1bd99e | ||
|
|
fd5b8e1dad | ||
|
|
8fb16028d9 | ||
|
|
6035c61cc4 |
@@ -14,6 +14,7 @@
|
|||||||
use Modules\Basicdata\Models\Branch;
|
use Modules\Basicdata\Models\Branch;
|
||||||
use Modules\Webstatement\Http\Requests\PrintStatementRequest;
|
use Modules\Webstatement\Http\Requests\PrintStatementRequest;
|
||||||
use Modules\Webstatement\Mail\StatementEmail;
|
use Modules\Webstatement\Mail\StatementEmail;
|
||||||
|
use Modules\Webstatement\Models\Account;
|
||||||
use Modules\Webstatement\Models\PrintStatementLog;
|
use Modules\Webstatement\Models\PrintStatementLog;
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
|
|
||||||
@@ -24,9 +25,15 @@
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request)
|
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'));
|
$branch = Branch::find(Auth::user()->branch_id);
|
||||||
|
$multiBranch = session('MULTI_BRANCH') ?? false;
|
||||||
|
|
||||||
|
return view('webstatement::statements.index', compact('branches', 'branch', 'multiBranch'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,6 +42,38 @@
|
|||||||
*/
|
*/
|
||||||
public function store(PrintStatementRequest $request)
|
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();
|
DB::beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -47,10 +86,12 @@
|
|||||||
$validated['user_agent'] = $request->userAgent();
|
$validated['user_agent'] = $request->userAgent();
|
||||||
$validated['request_type'] = 'single_account'; // Default untuk request manual
|
$validated['request_type'] = 'single_account'; // Default untuk request manual
|
||||||
$validated['status'] = 'pending'; // Status awal
|
$validated['status'] = 'pending'; // Status awal
|
||||||
|
$validated['authorization_status'] = 'approved'; // Status otorisasi awal
|
||||||
$validated['total_accounts'] = 1; // Untuk single account
|
$validated['total_accounts'] = 1; // Untuk single account
|
||||||
$validated['processed_accounts'] = 0;
|
$validated['processed_accounts'] = 0;
|
||||||
$validated['success_count'] = 0;
|
$validated['success_count'] = 0;
|
||||||
$validated['failed_count'] = 0;
|
$validated['failed_count'] = 0;
|
||||||
|
$validated['branch_code'] = $branch_code; // Awal tidak tersedia
|
||||||
|
|
||||||
// Create the statement log
|
// Create the statement log
|
||||||
$statement = PrintStatementLog::create($validated);
|
$statement = PrintStatementLog::create($validated);
|
||||||
@@ -66,6 +107,10 @@
|
|||||||
// Process statement availability check
|
// Process statement availability check
|
||||||
$this->checkStatementAvailability($statement);
|
$this->checkStatementAvailability($statement);
|
||||||
|
|
||||||
|
$statement = PrintStatementLog::find($statement->id);
|
||||||
|
if($statement->email){
|
||||||
|
$this->sendEmail($statement->id);
|
||||||
|
}
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
return redirect()->route('statements.index')
|
return redirect()->route('statements.index')
|
||||||
@@ -103,7 +148,14 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$disk = Storage::disk('sftpStatement');
|
$disk = Storage::disk('sftpStatement');
|
||||||
$filePath = "{$statement->period_from}/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
|
$filePath = "{$statement->period_from}/{$statement->branch_code}/{$statement->account_number}_{$statement->period_from}.pdf";
|
||||||
|
|
||||||
|
// Log untuk debugging
|
||||||
|
Log::info('Checking SFTP file path', [
|
||||||
|
'file_path' => $filePath,
|
||||||
|
'sftp_root' => config('filesystems.disks.sftpStatement.root'),
|
||||||
|
'full_path' => config('filesystems.disks.sftpStatement.root') . '/' . $filePath
|
||||||
|
]);
|
||||||
|
|
||||||
if ($statement->is_period_range && $statement->period_to) {
|
if ($statement->is_period_range && $statement->period_to) {
|
||||||
$periodFrom = Carbon::createFromFormat('Ym', $statement->period_from);
|
$periodFrom = Carbon::createFromFormat('Ym', $statement->period_from);
|
||||||
@@ -114,7 +166,7 @@
|
|||||||
|
|
||||||
for ($period = clone $periodFrom; $period->lte($periodTo); $period->addMonth()) {
|
for ($period = clone $periodFrom; $period->lte($periodTo); $period->addMonth()) {
|
||||||
$periodFormatted = $period->format('Ym');
|
$periodFormatted = $period->format('Ym');
|
||||||
$periodPath = $periodFormatted . "/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
|
$periodPath = $periodFormatted . "/{$statement->branch_code}/{$statement->account_number}_{$periodFormatted}.pdf";
|
||||||
|
|
||||||
if ($disk->exists($periodPath)) {
|
if ($disk->exists($periodPath)) {
|
||||||
$availablePeriods[] = $periodFormatted;
|
$availablePeriods[] = $periodFormatted;
|
||||||
@@ -236,20 +288,148 @@
|
|||||||
|
|
||||||
// Generate or fetch the statement file
|
// 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";
|
$filePath = "{$statement->period_from}/{$statement->branch_code}/{$statement->account_number}_{$statement->period_from}.pdf";
|
||||||
|
|
||||||
if ($statement->is_period_range && $statement->period_to) {
|
if ($statement->is_period_range && $statement->period_to) {
|
||||||
// Handle period range download (existing logic)
|
// Log: Memulai proses download period range
|
||||||
// ... existing zip creation logic ...
|
Log::info('Starting period range download', [
|
||||||
|
'statement_id' => $statement->id,
|
||||||
|
'period_from' => $statement->period_from,
|
||||||
|
'period_to' => $statement->period_to
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle period range download dengan membuat zip file
|
||||||
|
* yang berisi semua statement dalam rentang periode
|
||||||
|
*/
|
||||||
|
$periodFrom = Carbon::createFromFormat('Ym', $statement->period_from);
|
||||||
|
$periodTo = Carbon::createFromFormat('Ym', $statement->period_to);
|
||||||
|
|
||||||
|
// Loop through each month in the range
|
||||||
|
$missingPeriods = [];
|
||||||
|
$availablePeriods = [];
|
||||||
|
|
||||||
|
for ($period = clone $periodFrom; $period->lte($periodTo); $period->addMonth()) {
|
||||||
|
$periodFormatted = $period->format('Ym');
|
||||||
|
$periodPath = $periodFormatted . "/{$statement->branch_code}/{$statement->account_number}_{$periodFormatted}.pdf";
|
||||||
|
|
||||||
|
if ($disk->exists($periodPath)) {
|
||||||
|
$availablePeriods[] = $periodFormatted;
|
||||||
|
Log::info('Period available for download', [
|
||||||
|
'period' => $periodFormatted,
|
||||||
|
'path' => $periodPath
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$missingPeriods[] = $periodFormatted;
|
||||||
|
Log::warning('Period not available for download', [
|
||||||
|
'period' => $periodFormatted,
|
||||||
|
'path' => $periodPath
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any period is available, create a zip and download it
|
||||||
|
if (count($availablePeriods) > 0) {
|
||||||
|
/**
|
||||||
|
* Membuat zip file temporary untuk download
|
||||||
|
* dengan semua statement yang tersedia dalam periode
|
||||||
|
*/
|
||||||
|
$zipFileName = "{$statement->account_number}_{$statement->period_from}_to_{$statement->period_to}.zip";
|
||||||
|
$zipFilePath = storage_path("app/temp/{$zipFileName}");
|
||||||
|
|
||||||
|
// Ensure the temp directory exists
|
||||||
|
if (!file_exists(storage_path('app/temp'))) {
|
||||||
|
mkdir(storage_path('app/temp'), 0755, true);
|
||||||
|
Log::info('Created temp directory for zip files');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new zip archive
|
||||||
|
$zip = new ZipArchive();
|
||||||
|
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
|
||||||
|
Log::info('Zip archive created successfully', ['zip_path' => $zipFilePath]);
|
||||||
|
|
||||||
|
// Add each available statement to the zip
|
||||||
|
foreach ($availablePeriods as $period) {
|
||||||
|
$periodFilePath = "{$period}/{$statement->branch_code}/{$statement->account_number}_{$period}.pdf";
|
||||||
|
$localFilePath = storage_path("app/temp/{$statement->account_number}_{$period}.pdf");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Download the file from SFTP to local storage temporarily
|
||||||
|
file_put_contents($localFilePath, $disk->get($periodFilePath));
|
||||||
|
|
||||||
|
// Add the file to the zip
|
||||||
|
$zip->addFile($localFilePath, "{$statement->account_number}_{$period}.pdf");
|
||||||
|
|
||||||
|
Log::info('Added file to zip', [
|
||||||
|
'period' => $period,
|
||||||
|
'local_path' => $localFilePath
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Failed to add file to zip', [
|
||||||
|
'period' => $period,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip->close();
|
||||||
|
Log::info('Zip archive closed successfully');
|
||||||
|
|
||||||
|
// Return the zip file for download
|
||||||
|
$response = response()->download($zipFilePath, $zipFileName)->deleteFileAfterSend(true);
|
||||||
|
|
||||||
|
// Clean up temporary PDF files
|
||||||
|
foreach ($availablePeriods as $period) {
|
||||||
|
$localFilePath = storage_path("app/temp/{$statement->account_number}_{$period}.pdf");
|
||||||
|
if (file_exists($localFilePath)) {
|
||||||
|
unlink($localFilePath);
|
||||||
|
Log::info('Cleaned up temporary file', ['file' => $localFilePath]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info('Period range download completed successfully', [
|
||||||
|
'statement_id' => $statement->id,
|
||||||
|
'available_periods' => count($availablePeriods),
|
||||||
|
'missing_periods' => count($missingPeriods)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
} else {
|
||||||
|
Log::error('Failed to create zip archive', ['zip_path' => $zipFilePath]);
|
||||||
|
return back()->with('error', 'Failed to create zip archive for download.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log::warning('No statements available for download in period range', [
|
||||||
|
'statement_id' => $statement->id,
|
||||||
|
'missing_periods' => $missingPeriods
|
||||||
|
]);
|
||||||
|
return back()->with('error', 'No statements available for download in the specified period range.');
|
||||||
|
}
|
||||||
} else if ($disk->exists($filePath)) {
|
} else if ($disk->exists($filePath)) {
|
||||||
|
/**
|
||||||
|
* Handle single period download
|
||||||
|
* Download file PDF tunggal untuk periode tertentu
|
||||||
|
*/
|
||||||
|
Log::info('Single period download', [
|
||||||
|
'statement_id' => $statement->id,
|
||||||
|
'file_path' => $filePath
|
||||||
|
]);
|
||||||
|
|
||||||
return $disk->download($filePath, "{$statement->account_number}_{$statement->period_from}.pdf");
|
return $disk->download($filePath, "{$statement->account_number}_{$statement->period_from}.pdf");
|
||||||
|
} else {
|
||||||
|
Log::warning('Statement file not found', [
|
||||||
|
'statement_id' => $statement->id,
|
||||||
|
'file_path' => $filePath
|
||||||
|
]);
|
||||||
|
return back()->with('error', 'Statement file not found.');
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|
||||||
Log::error('Failed to download statement', [
|
Log::error('Failed to download statement', [
|
||||||
'statement_id' => $statement->id,
|
'statement_id' => $statement->id,
|
||||||
'error' => $e->getMessage()
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return back()->with('error', 'Failed to download statement: ' . $e->getMessage());
|
return back()->with('error', 'Failed to download statement: ' . $e->getMessage());
|
||||||
@@ -295,6 +475,13 @@
|
|||||||
// Retrieve data from the database
|
// Retrieve data from the database
|
||||||
$query = PrintStatementLog::query();
|
$query = PrintStatementLog::query();
|
||||||
|
|
||||||
|
if (!auth()->user()->hasRole('administrator')) {
|
||||||
|
$query->where(function($q) {
|
||||||
|
$q->where('user_id', Auth::id())
|
||||||
|
->orWhere('branch_code', Auth::user()->branch->code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Apply search filter if provided
|
// Apply search filter if provided
|
||||||
if ($request->has('search') && !empty($request->get('search'))) {
|
if ($request->has('search') && !empty($request->get('search'))) {
|
||||||
$search = $request->get('search');
|
$search = $request->get('search');
|
||||||
@@ -426,6 +613,7 @@
|
|||||||
public function sendEmail($id)
|
public function sendEmail($id)
|
||||||
{
|
{
|
||||||
$statement = PrintStatementLog::findOrFail($id);
|
$statement = PrintStatementLog::findOrFail($id);
|
||||||
|
|
||||||
// Check if statement has email
|
// Check if statement has email
|
||||||
if (empty($statement->email)) {
|
if (empty($statement->email)) {
|
||||||
return redirect()->back()->with('error', 'No email address provided for this statement.');
|
return redirect()->back()->with('error', 'No email address provided for this statement.');
|
||||||
@@ -438,7 +626,7 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$disk = Storage::disk('sftpStatement');
|
$disk = Storage::disk('sftpStatement');
|
||||||
$filePath = "{$statement->period_from}/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
|
$filePath = "{$statement->period_from}/{$statement->branch_code}/{$statement->account_number}_{$statement->period_from}.pdf";
|
||||||
|
|
||||||
if ($statement->is_period_range && $statement->period_to) {
|
if ($statement->is_period_range && $statement->period_to) {
|
||||||
$periodFrom = Carbon::createFromFormat('Ym', $statement->period_from);
|
$periodFrom = Carbon::createFromFormat('Ym', $statement->period_from);
|
||||||
@@ -450,7 +638,7 @@
|
|||||||
|
|
||||||
for ($period = clone $periodFrom; $period->lte($periodTo); $period->addMonth()) {
|
for ($period = clone $periodFrom; $period->lte($periodTo); $period->addMonth()) {
|
||||||
$periodFormatted = $period->format('Ym');
|
$periodFormatted = $period->format('Ym');
|
||||||
$periodPath = $periodFormatted . "/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
|
$periodPath = $periodFormatted . "/{$statement->branch_code}/{$statement->account_number}_{$periodFormatted}.pdf";
|
||||||
|
|
||||||
if ($disk->exists($periodPath)) {
|
if ($disk->exists($periodPath)) {
|
||||||
$availablePeriods[] = $periodFormatted;
|
$availablePeriods[] = $periodFormatted;
|
||||||
@@ -475,7 +663,7 @@
|
|||||||
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
|
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
|
||||||
// Add each available statement to the zip
|
// Add each available statement to the zip
|
||||||
foreach ($availablePeriods as $period) {
|
foreach ($availablePeriods as $period) {
|
||||||
$filePath = "{$period}/Print/{$statement->branch_code}/{$statement->account_number}.pdf";
|
$filePath = "{$period}/{$statement->branch_code}/{$statement->account_number}_{$statement->period_from}.pdf";
|
||||||
$localFilePath = storage_path("app/temp/{$statement->account_number}_{$period}.pdf");
|
$localFilePath = storage_path("app/temp/{$statement->account_number}_{$period}.pdf");
|
||||||
|
|
||||||
// Download the file from SFTP to local storage temporarily
|
// Download the file from SFTP to local storage temporarily
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class PrintStatementRequest extends FormRequest
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'branch_code' => ['required', 'string', 'exists:branches,code'],
|
|
||||||
'account_number' => ['required', 'string'],
|
'account_number' => ['required', 'string'],
|
||||||
'is_period_range' => ['sometimes', 'boolean'],
|
'is_period_range' => ['sometimes', 'boolean'],
|
||||||
'email' => ['nullable', 'email'],
|
'email' => ['nullable', 'email'],
|
||||||
@@ -36,6 +35,7 @@ class PrintStatementRequest extends FormRequest
|
|||||||
function ($attribute, $value, $fail) {
|
function ($attribute, $value, $fail) {
|
||||||
$query = Statement::where('account_number', $this->input('account_number'))
|
$query = Statement::where('account_number', $this->input('account_number'))
|
||||||
->where('authorization_status', '!=', 'rejected')
|
->where('authorization_status', '!=', 'rejected')
|
||||||
|
->where('is_available', true)
|
||||||
->where('period_from', $value);
|
->where('period_from', $value);
|
||||||
|
|
||||||
// If this is an update request, exclude the current record
|
// If this is an update request, exclude the current record
|
||||||
@@ -77,8 +77,6 @@ class PrintStatementRequest extends FormRequest
|
|||||||
public function messages(): array
|
public function messages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'branch_code.required' => 'Branch code is required',
|
|
||||||
'branch_code.exists' => 'Selected branch does not exist',
|
|
||||||
'account_number.required' => 'Account number is required',
|
'account_number.required' => 'Account number is required',
|
||||||
'period_from.required' => 'Period is required',
|
'period_from.required' => 'Period is required',
|
||||||
'period_from.regex' => 'Period must be in YYYYMM format',
|
'period_from.regex' => 'Period must be in YYYYMM format',
|
||||||
@@ -106,14 +104,14 @@ class PrintStatementRequest extends FormRequest
|
|||||||
$this->merge([
|
$this->merge([
|
||||||
'period_to' => substr($this->period_to, 0, 4) . substr($this->period_to, 5, 2),
|
'period_to' => substr($this->period_to, 0, 4) . substr($this->period_to, 5, 2),
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
// Convert is_period_range to boolean if it exists
|
// Only set is_period_range to true if period_to is different from period_from
|
||||||
if ($this->has('period_to')) {
|
if ($this->period_to !== $this->period_from) {
|
||||||
$this->merge([
|
$this->merge([
|
||||||
'is_period_range' => true,
|
'is_period_range' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set default request_type if not provided
|
// Set default request_type if not provided
|
||||||
if (!$this->has('request_type')) {
|
if (!$this->has('request_type')) {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
namespace Modules\Webstatement\Jobs;
|
namespace Modules\Webstatement\Jobs;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
@@ -13,6 +10,7 @@ use Illuminate\Support\Facades\DB;
|
|||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Modules\Webstatement\Models\StmtEntry;
|
use Modules\Webstatement\Models\StmtEntry;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class ProcessStmtEntryDataJob implements ShouldQueue
|
class ProcessStmtEntryDataJob implements ShouldQueue
|
||||||
{
|
{
|
||||||
@@ -201,9 +199,10 @@ use Illuminate\Support\Facades\DB;
|
|||||||
if (!empty($this->entryBatch)) {
|
if (!empty($this->entryBatch)) {
|
||||||
$totalProcessed = 0;
|
$totalProcessed = 0;
|
||||||
|
|
||||||
// Process in smaller chunks for better memory management
|
// Process each entry data directly (tidak ada nested array)
|
||||||
foreach ($this->entryBatch as $entryChunk) {
|
foreach ($this->entryBatch as $entryData) {
|
||||||
foreach ($entryChunk as $entryData) {
|
// Validasi bahwa entryData adalah array dan memiliki stmt_entry_id
|
||||||
|
if (is_array($entryData) && isset($entryData['stmt_entry_id'])) {
|
||||||
// Gunakan updateOrCreate untuk menghindari duplicate key error
|
// Gunakan updateOrCreate untuk menghindari duplicate key error
|
||||||
StmtEntry::updateOrCreate(
|
StmtEntry::updateOrCreate(
|
||||||
[
|
[
|
||||||
@@ -213,6 +212,9 @@ use Illuminate\Support\Facades\DB;
|
|||||||
);
|
);
|
||||||
|
|
||||||
$totalProcessed++;
|
$totalProcessed++;
|
||||||
|
} else {
|
||||||
|
Log::warning('Invalid entry data structure', ['data' => $entryData]);
|
||||||
|
$this->errorCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
"attributes": [],
|
"attributes": [],
|
||||||
"permission": "",
|
"permission": "",
|
||||||
"roles": [
|
"roles": [
|
||||||
"administrator"
|
"administrator",
|
||||||
|
"customer_service"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,31 +11,47 @@
|
|||||||
<h3 class="card-title">Request Print Stetement</h3>
|
<h3 class="card-title">Request Print Stetement</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="{{ isset($statement) ? route('statements.update', $statement->id) : route('statements.store') }}" method="POST">
|
<form
|
||||||
|
action="{{ isset($statement) ? route('statements.update', $statement->id) : route('statements.store') }}"
|
||||||
|
method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@if(isset($statement))
|
@if (isset($statement))
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-5">
|
<div class="grid grid-cols-1 gap-5">
|
||||||
|
|
||||||
|
@if ($multiBranch)
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label required" for="branch_code">Branch</label>
|
<label class="form-label required" for="branch_id">Branch/Cabang</label>
|
||||||
<select class="select tomselect @error('branch_code') is-invalid @enderror" id="branch_code" name="branch_code" required>
|
<select class="input form-control tomselect @error('branch_id') is-invalid @enderror"
|
||||||
<option value="">Select Branch</option>
|
id="branch_id" name="branch_id" required>
|
||||||
@foreach($branches as $branch)
|
<option value="">Pilih Branch/Cabang</option>
|
||||||
<option value="{{ $branch->code }}" {{ (old('branch_code', $statement->branch_code ?? '') == $branch->code) ? 'selected' : '' }}>
|
@foreach ($branches as $branchOption)
|
||||||
{{ $branch->name }}
|
<option value="{{ $branchOption->code }}"
|
||||||
|
{{ old('branch_id', $statement->branch_id ?? ($branch->code ?? '')) == $branchOption->code ? 'selected' : '' }}>
|
||||||
|
{{ $branchOption->code }} - {{ $branchOption->name }}
|
||||||
</option>
|
</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
@error('branch_code')
|
@error('branch_id')
|
||||||
<div class="invalid-feedback">{{ $message }}</div>
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</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_id" value="{{ $branch->code ?? '' }}">
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label required" for="account_number">Account Number</label>
|
<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>
|
<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>
|
||||||
@error('account_number')
|
@error('account_number')
|
||||||
<div class="invalid-feedback">{{ $message }}</div>
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
@enderror
|
@enderror
|
||||||
@@ -43,7 +59,9 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="email">Email</label>
|
<label class="form-label" for="email">Email</label>
|
||||||
<input type="email" class="input form-control @error('email') is-invalid @enderror" id="email" name="email" value="{{ old('email', $statement->email ?? '') }}" placeholder="Optional email for send statement">
|
<input type="email" class="input form-control @error('email') is-invalid @enderror"
|
||||||
|
id="email" name="email" value="{{ old('email', $statement->email ?? '') }}"
|
||||||
|
placeholder="Optional email for send statement">
|
||||||
@error('email')
|
@error('email')
|
||||||
<div class="invalid-feedback">{{ $message }}</div>
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
@enderror
|
@enderror
|
||||||
@@ -53,24 +71,21 @@
|
|||||||
<label class="form-label required" for="start_date">Start Date</label>
|
<label class="form-label required" for="start_date">Start Date</label>
|
||||||
|
|
||||||
<input class="input @error('period_from') border-danger bg-danger-light @enderror"
|
<input class="input @error('period_from') border-danger bg-danger-light @enderror"
|
||||||
type="month"
|
type="month" name="period_from"
|
||||||
name="period_from"
|
|
||||||
value="{{ $statement->period_from ?? old('period_from') }}"
|
value="{{ $statement->period_from ?? old('period_from') }}"
|
||||||
max="{{ date('Y-m', strtotime('-1 month')) }}">
|
max="{{ date('Y-m', strtotime('-1 month')) }}">
|
||||||
@error('period_from')
|
@error('period_from')
|
||||||
<em class="alert text-danger text-sm">{{ $message }}</em>
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label required" for="end_date">End Date</label>
|
<label class="form-label required" for="end_date">End Date</label>
|
||||||
<input class="input @error('period_to') border-danger bg-danger-light @enderror"
|
<input class="input @error('period_to') border-danger bg-danger-light @enderror" type="month"
|
||||||
type="month"
|
name="period_to" value="{{ $statement->period_to ?? old('period_to') }}"
|
||||||
name="period_to"
|
|
||||||
value="{{ $statement->period_to ?? old('period_to') }}"
|
|
||||||
max="{{ date('Y-m', strtotime('-1 month')) }}">
|
max="{{ date('Y-m', strtotime('-1 month')) }}">
|
||||||
@error('period_to')
|
@error('period_to')
|
||||||
<em class="alert text-danger text-sm">{{ $message }}</em>
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,8 +100,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-6">
|
<div class="col-span-6">
|
||||||
<div class="card card-grid min-w-full" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="statement-table" data-api-url="{{ route('statements.datatables') }}">
|
<div class="min-w-full card card-grid" data-datatable="false" data-datatable-page-size="10"
|
||||||
<div class="card-header py-5 flex-wrap">
|
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">
|
<h3 class="card-title">
|
||||||
Daftar Statement Request
|
Daftar Statement Request
|
||||||
</h3>
|
</h3>
|
||||||
@@ -100,11 +116,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="scrollable-x-auto">
|
<div class="scrollable-x-auto">
|
||||||
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
|
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
|
||||||
|
data-datatable-table="true">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-14">
|
<th class="w-14">
|
||||||
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
|
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" />
|
||||||
</th>
|
</th>
|
||||||
<th class="min-w-[100px]" data-datatable-column="id">
|
<th class="min-w-[100px]" data-datatable-column="id">
|
||||||
<span class="sort"> <span class="sort-label"> ID </span>
|
<span class="sort"> <span class="sort-label"> ID </span>
|
||||||
@@ -122,10 +139,6 @@
|
|||||||
<span class="sort"> <span class="sort-label"> Period </span>
|
<span class="sort"> <span class="sort-label"> Period </span>
|
||||||
<span class="sort-icon"> </span> </span>
|
<span class="sort-icon"> </span> </span>
|
||||||
</th>
|
</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">
|
<th class="min-w-[150px]" data-datatable-column="is_available">
|
||||||
<span class="sort"> <span class="sort-label"> Available </span>
|
<span class="sort"> <span class="sort-label"> Available </span>
|
||||||
<span class="sort-icon"> </span> </span>
|
<span class="sort-icon"> </span> </span>
|
||||||
@@ -143,12 +156,14 @@
|
|||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
|
<div
|
||||||
<div class="flex items-center gap-2">
|
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
|
Show
|
||||||
<select class="select select-sm w-16" 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>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex gap-4 items-center">
|
||||||
<span data-datatable-info="true"> </span>
|
<span data-datatable-info="true"> </span>
|
||||||
<div class="pagination" data-datatable-pagination="true">
|
<div class="pagination" data-datatable-pagination="true">
|
||||||
</div>
|
</div>
|
||||||
@@ -162,6 +177,10 @@
|
|||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
/**
|
||||||
|
* Fungsi untuk menghapus data statement
|
||||||
|
* @param {number} data - ID statement yang akan dihapus
|
||||||
|
*/
|
||||||
function deleteData(data) {
|
function deleteData(data) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Are you sure?',
|
title: 'Are you sure?',
|
||||||
@@ -192,6 +211,56 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -243,31 +312,16 @@
|
|||||||
return fromPeriod + toPeriod;
|
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: {
|
is_available: {
|
||||||
title: 'Available',
|
title: 'Available',
|
||||||
render: (item, data) => {
|
render: (item, data) => {
|
||||||
let statusClass = data.is_available ? 'badge badge-light-success' : 'badge badge-light-danger';
|
let statusClass = data.is_available ? 'badge badge-light-success' :
|
||||||
|
'badge badge-light-danger';
|
||||||
let statusText = data.is_available ? 'Yes' : 'No';
|
let statusText = data.is_available ? 'Yes' : 'No';
|
||||||
return `<span class="${statusClass}">${statusText}</span>`;
|
return `<span class="${statusClass}">${statusText}</span>`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
remarks : {
|
remarks: {
|
||||||
title: 'Notes',
|
title: 'Notes',
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
@@ -315,7 +369,7 @@
|
|||||||
|
|
||||||
let dataTable = new KTDataTable(element, dataTableOptions);
|
let dataTable = new KTDataTable(element, dataTableOptions);
|
||||||
// Custom search functionality
|
// Custom search functionality
|
||||||
searchInput.addEventListener('input', function () {
|
searchInput.addEventListener('input', function() {
|
||||||
const searchValue = this.value.trim();
|
const searchValue = this.value.trim();
|
||||||
dataTable.search(searchValue, true);
|
dataTable.search(searchValue, true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,19 +73,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</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="d-flex flex-column">
|
||||||
<div class="text-gray-500 fw-semibold">Availability</div>
|
<div class="text-gray-500 fw-semibold">Availability</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user