Compare commits

...

2 Commits

Author SHA1 Message Date
Daeng Deni Mardaeni
700c8bbbf6 feat(webstatement): tambahkan fitur konversi file HTML ke PDF
- Menambahkan command baru `webstatement:convert-html-to-pdf` untuk melakukan konversi file HTML menjadi PDF secara otomatis:
  - Dapat menerima parameter `directory` untuk menentukan direktori sumber file HTML.
  - Menampilkan pesan sukses atau error selama proses berjalan.
  - Menggunakan konsep asinkron melalui job untuk meningkatkan efisiensi.

- Membuat job baru `ConvertHtmlToPdfJob` untuk menangani proses konversi file:
  - Memproses folder yang berisi file HTML berdasarkan struktur direktori tertentu.
  - Mengambil semua file HTML dalam suatu folder, kemudian mengonversinya menjadi file PDF.
  - Menggunakan library `Barryvdh\DomPDF\Facade\Pdf` untuk konversi format HTML ke PDF.
  - Melakukan logging untuk setiap proses berhasil atau ketika terjadi error.
  - Memastikan suksesnya konversi ke direktori yang sama dengan file HTML.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
2025-06-02 18:45:07 +07:00
Daeng Deni Mardaeni
8a728d6c6e feat(webstatement): tambahkan fitur penggabungan dan proteksi file PDF
- Menambahkan command baru `webstatement:combine-pdf` melalui `CombinePdf` untuk menjalankan proses penggabungan file PDF.
  - Proses ini mencakup penggabungan file PDF dari folder r14 dan r23 berdasarkan periode tertentu.
  - File PDF yang dihasilkan juga dilindungi dengan password berbasis nomor rekening.
- Membuat controller `CombinePdfController` dengan fungsi utama `combinePdfs` untuk mengontrol alur penggabungan file PDF:
  - Mendapatkan daftar akun yang relevan.
  - Mengecek file dari folder r14 dan r23 untuk setiap akun.
  - Melakukan logging saat file tidak ditemukan atau jika terdapat error dalam proses.
  - Mendaftarkan job `CombinePdfJob` untuk memproses file secara async.
- Menambahkan job baru `CombinePdfJob`:
  - Menggunakan library `PDFMerger` untuk menggabungkan file.
  - Terapkan proteksi password menggunakan library `PDFPasswordProtect`.
  - Memastikan direktori output dibuat jika belum ada.
  - Melakukan logging saat proses berhasil maupun saat terjadi error.
- Memperbarui `WebstatementServiceProvider`:
  - Mendaftarkan command baru ke dalam provider.
  - Menambahkan penjadwalan otomatis untuk menjalankan perintah `webstatement:combine-pdf` setiap hari pada pukul 09:30.
  - Logging hasil eksekusi ke file log `logs/combine-pdf.log`.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
2025-06-02 10:31:51 +07:00
6 changed files with 398 additions and 7 deletions

View File

@@ -0,0 +1,47 @@
<?php
namespace Modules\Webstatement\Console;
use Exception;
use Illuminate\Console\Command;
use Modules\Webstatement\Http\Controllers\CombinePdfController;
class CombinePdf extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'webstatement:combine-pdf';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Process combine pdf';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->info('Starting combine pdf process...');
$period = request()->set(['period' => $this->argument('period')]);
try {
$controller = app(CombinePdfController::class);
$response = $controller->combinePdfs($period);
$responseData = json_decode($response->getContent(), true);
$this->info($responseData['message'] ?? 'Process completed');
return Command::SUCCESS;
} catch (Exception $e) {
$this->error('Error processing combine pdf: ' . $e->getMessage());
return Command::FAILURE;
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\Webstatement\Console;
use Illuminate\Console\Command;
use Modules\Webstatement\Jobs\ConvertHtmlToPdfJob;
use Exception;
class ConvertHtmlToPdf extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'webstatement:convert-html-to-pdf {directory}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Convert HTML files to PDF in the specified directory';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
try {
$directory = $this->argument('directory');
$this->info('Starting HTML to PDF conversion process...');
// Dispatch the job
ConvertHtmlToPdfJob::dispatch($directory);
$this->info('HTML to PDF conversion job has been queued.');
return 0;
} catch (Exception $e) {
$this->error('Error processing HTML to PDF conversion: ' . $e->getMessage());
return 1;
}
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Modules\Webstatement\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Modules\Webstatement\Jobs\CombinePdfJob;
use Modules\Webstatement\Models\Account;
class CombinePdfController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('webstatement::index');
}
/**
* Combine PDF files from r14 and r23 folders for all accounts
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function combinePdfs(Request $request)
{
// Get period from request or use current period
$period = $request->input('period', date('Ym'));
// Get all accounts
$accounts = Account::all();
$processedCount = 0;
$skippedCount = 0;
$errorCount = 0;
foreach ($accounts as $account) {
$branchCode = $account->branch_code;
$accountNumber = $account->account_number;
// Define file paths
$r14Path = storage_path("app/r14/{$period}/{$branchCode}/{$accountNumber}_{$period}.pdf");
$r23Path = storage_path("app/r23/{$period}/{$branchCode}/{$accountNumber}_{$period}.pdf");
$outputDir = storage_path("app/combine/{$period}/{$branchCode}");
$outputFilename = "{$accountNumber}_{$period}.pdf";
// Check if files exist
$r14Exists = File::exists($r14Path);
$r23Exists = File::exists($r23Path);
// Skip if neither file exists
if (!$r14Exists && !$r23Exists) {
Log::warning("No PDF files found for account {$accountNumber}");
$skippedCount++;
continue;
}
// If both files exist, combine them
if ($r14Exists && $r23Exists) {
Log::info("Combining PDFs for account {$accountNumber}");
$pdfFiles = [$r14Path, $r23Path];
}
// If only one file exists, just apply password protection
else {
Log::info("Applying password protection to single PDF for account {$accountNumber}");
$pdfFile = $r14Exists ? $r14Path : $r23Path;
$pdfFiles = [$pdfFile];
}
try {
// Use the account number as password
$password = $accountNumber;
// Dispatch job to combine PDFs or apply password protection
CombinePdfJob::dispatch($pdfFiles, $outputDir, $outputFilename, $password);
$processedCount++;
} catch (\Exception $e) {
Log::error("Error processing PDF for account {$accountNumber}: {$e->getMessage()}");
$errorCount++;
}
}
return response()->json([
'message' => 'PDF combination process has been queued',
'processed' => $processedCount,
'skipped' => $skippedCount,
'errors' => $errorCount,
'period' => $period
]);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace Modules\Webstatement\Jobs;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Owenoj\PDFPasswordProtect\Facade\PDFPasswordProtect;
use Webklex\PDFMerger\Facades\PDFMergerFacade as PDFMerger;
class CombinePdfJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $pdfFiles;
protected $outputPath;
protected $outputFilename;
protected $password;
/**
* Create a new job instance.
*
* @param array $pdfFiles Array of PDF file paths to combine
* @param string $outputPath Directory where the combined PDF will be saved
* @param string $outputFilename Filename for the combined PDF
* @param string $password Password to protect the PDF
*/
public function __construct(array $pdfFiles, string $outputPath, string $outputFilename, string $password)
{
$this->pdfFiles = $pdfFiles;
$this->outputPath = $outputPath;
$this->outputFilename = $outputFilename;
$this->password = $password;
}
/**
* Execute the job.
*/
public function handle(): void
{
try {
// Initialize the PDF merger
$merger = PDFMerger::init();
// Add each PDF file to the merger
foreach ($this->pdfFiles as $pdfFile) {
if (file_exists($pdfFile)) {
$merger->addPDF($pdfFile, 'all');
} else {
Log::warning("PDF file not found: {$pdfFile}");
}
}
// Make sure the output directory exists
if (!file_exists($this->outputPath)) {
mkdir($this->outputPath, 0755, true);
}
// Merge the PDFs
$merger->merge();
// Save the merged PDF
$fullPath = $this->outputPath . '/' . $this->outputFilename;
$merger->save($fullPath);
// Apply password protection if password is provided
if (!empty($this->password)) {
$tempPath = $this->outputPath . '/temp_' . $this->outputFilename;
// Rename the original file to a temporary name
rename($fullPath, $tempPath);
// Apply password protection and save to the original filename
PDFPasswordProtect::encrypt($tempPath, $fullPath, $this->password);
// Remove the temporary file
if (file_exists($tempPath)) {
unlink($tempPath);
}
Log::info("PDF password protection applied successfully.");
}
Log::info("PDFs combined successfully. Output file: {$fullPath}");
} catch (Exception $e) {
Log::error("Error combining PDFs: " . $e->getMessage());
throw $e;
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Modules\Webstatement\Jobs;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\File;
use Barryvdh\DomPDF\Facade\Pdf;
class ConvertHtmlToPdfJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $baseDirectory;
/**
* Create a new job instance.
*
* @param string $baseDirectory Base directory path to scan
*/
public function __construct(string $baseDirectory)
{
$this->baseDirectory = $baseDirectory;
}
/**
* Execute the job.
*/
public function handle(): void
{
try {
Log::info("Starting HTML to PDF conversion in directory: {$this->baseDirectory}");
// Check if directory exists
if (!File::isDirectory($this->baseDirectory)) {
Log::error("Directory not found: {$this->baseDirectory}");
return;
}
// Get all subdirectories (ID folders)
$idDirectories = File::directories($this->baseDirectory);
foreach ($idDirectories as $idDirectory) {
$this->processDirectory($idDirectory);
}
Log::info("HTML to PDF conversion completed successfully.");
} catch (Exception $e) {
Log::error("Error converting HTML to PDF: " . $e->getMessage());
}
}
/**
* Process a single ID directory
*
* @param string $directory Directory path to process
*/
protected function processDirectory(string $directory): void
{
try {
$htmlFiles = File::glob($directory . '/*.html');
foreach ($htmlFiles as $htmlFile) {
$this->convertHtmlToPdf($htmlFile);
}
} catch (Exception $e) {
Log::error("Error processing directory {$directory}: " . $e->getMessage());
}
}
/**
* Convert a single HTML file to PDF
*
* @param string $htmlFilePath Path to HTML file
*/
protected function convertHtmlToPdf(string $htmlFilePath): void
{
try {
$filename = pathinfo($htmlFilePath, PATHINFO_FILENAME);
$directory = pathinfo($htmlFilePath, PATHINFO_DIRNAME);
$pdfFilePath = $directory . '/' . $filename . '.pdf';
// Read HTML content
$htmlContent = File::get($htmlFilePath);
// Convert HTML to PDF
$pdf = PDF::loadHTML($htmlContent);
// Save PDF file
$pdf->save($pdfFilePath);
Log::info("Converted {$htmlFilePath} to {$pdfFilePath}");
} catch (Exception $e) {
Log::error("Error converting {$htmlFilePath} to PDF: " . $e->getMessage());
}
}
}

View File

@@ -2,15 +2,16 @@
namespace Modules\Webstatement\Providers;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Modules\Webstatement\Console\ExportDailyStatements;
use Modules\Webstatement\Console\GenerateBiayakartuCommand;
use Modules\Webstatement\Console\GenerateBiayaKartuCsvCommand;
use Modules\Webstatement\Console\ProcessDailyMigration;
use Modules\Webstatement\Jobs\UpdateAtmCardBranchCurrencyJob;
use Nwidart\Modules\Traits\PathNamespace;
use Illuminate\Console\Scheduling\Schedule;
use Modules\Webstatement\Console\CombinePdf;
use Modules\Webstatement\Console\ExportDailyStatements;
use Modules\Webstatement\Console\ProcessDailyMigration;
use Modules\Webstatement\Console\GenerateBiayakartuCommand;
use Modules\Webstatement\Jobs\UpdateAtmCardBranchCurrencyJob;
use Modules\Webstatement\Console\GenerateBiayaKartuCsvCommand;
class WebstatementServiceProvider extends ServiceProvider
{
@@ -57,6 +58,7 @@ class WebstatementServiceProvider extends ServiceProvider
GenerateBiayaKartuCsvCommand::class,
ProcessDailyMigration::class,
ExportDailyStatements::class,
CombinePdf::class
]);
}
@@ -98,7 +100,11 @@ class WebstatementServiceProvider extends ServiceProvider
->withoutOverlapping()
->appendOutputTo(storage_path('logs/statement-export.log'));
// Combine PDf
$schedule->command('webstatement:combine-pdf')
->dailyAt('09:30')
->withoutOverlapping()
->appendOutputTo(storage_path('logs/combine-pdf.log'));
}
/**