Merge pull request #3177 from CihanSenturk/added-bulk-action-download

Added bulk action download
This commit is contained in:
Cüneyt Şentürk 2024-07-11 10:52:26 +01:00 committed by GitHub
commit ca9ff90caa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 520 additions and 23 deletions

View File

@ -2,6 +2,8 @@
namespace App\Abstracts;
use App\Jobs\Common\CreateMediableForDownload;
use App\Jobs\Common\CreateZipForDownload;
use App\Jobs\Common\DeleteContact;
use App\Jobs\Common\UpdateContact;
use App\Jobs\Banking\DeleteTransaction;
@ -11,6 +13,8 @@ use App\Traits\Translations;
use App\Utilities\Export;
use App\Utilities\Import;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Bus;
use Throwable;
abstract class BulkAction
{
@ -39,6 +43,11 @@ abstract class BulkAction
'message' => 'bulk_actions.message.export',
'type' => 'download'
],
'download' => [
'name' => 'general.download',
'message' => 'bulk_actions.message.download',
'type' => 'download',
],
];
public $icons = [
@ -47,6 +56,7 @@ abstract class BulkAction
'delete' => 'delete',
'duplicate' => 'file_copy',
'export' => 'file_download',
'download' => 'folder_zip',
'reconcile' => 'published_with_changes',
'unreconcile' => 'layers_clear',
'received' => 'call_received',
@ -276,4 +286,43 @@ abstract class BulkAction
{
return Export::toExcel($class, $translation, $extension);
}
/**
* Download the pdf file or catch errors
*
* @param $class
* @param $file_name
* @param $translation
*
* @return mixed
*/
public function downloadPdf($selected, $class, $file_name, $translation)
{
try {
if (should_queue()) {
$batch[] = new CreateZipForDownload($selected, $class, $file_name);
$batch[] = new CreateMediableForDownload(user(), $file_name, $translation);
Bus::chain($batch)->onQueue('default')->dispatch();
$message = trans('messages.success.download_queued', ['type' => $translation]);
flash($message)->success();
return back();
} else {
$this->dispatch(new CreateZipForDownload($selected, $class, $file_name));
$folder_path = 'app/temp/' . company_id() . '/bulk_actions/';
return response()->download(get_storage_path($folder_path . $file_name . '.zip'))->deleteFileAfterSend(true);
}
} catch (Throwable $e) {
report($e);
flash($e->getMessage())->error()->important();
return back();
}
}
}

View File

@ -3,14 +3,14 @@
namespace App\BulkActions\Sales;
use App\Abstracts\BulkAction;
use App\Events\Document\DocumentCancelled;
use App\Events\Document\DocumentCreated;
use App\Events\Document\DocumentMarkedSent;
use App\Events\Document\PaymentReceived;
use App\Exports\Sales\Invoices\Invoices as Export;
use App\Jobs\Document\UpdateDocument;
use App\Jobs\Document\DeleteDocument;
use App\Models\Document\Document;
use App\Jobs\Document\DeleteDocument;
use App\Jobs\Common\CreateZipForDownload;
use App\Jobs\Document\UpdateDocument;
use App\Events\Document\DocumentCreated;
use App\Events\Document\DocumentCancelled;
use App\Events\Document\DocumentMarkedSent;
use App\Exports\Sales\Invoices\Invoices as Export;
class Invoices extends BulkAction
{
@ -56,6 +56,12 @@ class Invoices extends BulkAction
'message' => 'bulk_actions.message.export',
'type' => 'download',
],
'download' => [
'icon' => 'folder_zip',
'name' => 'general.download',
'message' => 'bulk_actions.message.download',
'type' => 'download',
],
];
public function edit($request)
@ -150,5 +156,17 @@ class Invoices extends BulkAction
$selected = $this->getSelectedInput($request);
return $this->exportExcel(new Export($selected), trans_choice('general.invoices', 2));
}
}
public function download($request)
{
$selected = $this->getSelectedRecords($request);
$file_name = Document::INVOICE_TYPE . '-'. date('Y-m-d-H-i-s');
$class = '\App\Jobs\Document\DownloadDocument';
return $this->downloadPdf($selected, $class, $file_name, trans_choice('general.invoices', 2));
}
}

View File

@ -10,6 +10,7 @@ use App\Imports\Sales\Invoices\Invoices as Import;
use App\Jobs\Document\CreateDocument;
use App\Jobs\Document\DeleteDocument;
use App\Jobs\Document\DuplicateDocument;
use App\Jobs\Document\DownloadDocument;
use App\Jobs\Document\SendDocument;
use App\Jobs\Document\UpdateDocument;
use App\Models\Document\Document;
@ -327,19 +328,6 @@ class Invoices extends Controller
{
event(new \App\Events\Document\DocumentPrinting($invoice));
$currency_style = true;
$view = view($invoice->template_path, compact('invoice', 'currency_style'))->render();
$html = mb_convert_encoding($view, 'HTML-ENTITIES', 'UTF-8');
$pdf = app('dompdf.wrapper');
$pdf->loadHTML($html);
//$pdf->setPaper('A4', 'portrait');
$file_name = $this->getDocumentFileName($invoice);
return $pdf->download($file_name);
return $this->dispatch(new DownloadDocument($invoice, null, null, false, 'download'));
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace App\Jobs\Common;
use App\Abstracts\JobShouldQueue;
use App\Models\Common\Media as MediaModel;
use App\Notifications\Common\DownloadCompleted;
use App\Notifications\Common\DownloadFailed;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use MediaUploader;
class CreateMediableForDownload extends JobShouldQueue
{
protected $user;
protected $file_name;
protected $translation;
/**
* Create a new job instance.
*
* @param $user
* @param $file_name
* @param $translation
*/
public function __construct($user, $file_name, $translation)
{
$this->user = $user;
$this->file_name = $file_name;
$this->translation = $translation;
$this->onQueue('jobs');
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
try {
$media = $this->getQueuedMedia();
$this->user->attachMedia($media, 'download');
$download_url = route('uploads.download', ['id' => $media->id, 'company_id' => company_id()]);
$this->user->notify(new DownloadCompleted($this->translation, $this->file_name, $download_url));
} catch (\Throwable $exception) {
report($exception);
$this->user->notify(new DownloadFailed($exception->getMessage()));
throw $exception;
}
}
public function getQueuedMedia()
{
return config('excel.temporary_files.remote_disk') !== null
? $this->getRemoteQueuedMedia()
: $this->getLocalQueuedMedia();
}
public function getLocalQueuedMedia()
{
$folder_path = 'app/temp/' . company_id() . '/bulk_actions/';
$source = storage_path($folder_path . $this->file_name . '.zip');
$destination = $this->getMediaFolder('bulk_actions');
$media = MediaUploader::makePrivate()
->beforeSave(function(MediaModel $media) {
$media->company_id = company_id();
})
->fromSource($source)
->toDirectory($destination)
->upload();
File::delete($source);
return $media;
}
public function getRemoteQueuedMedia()
{
$disk = config('excel.temporary_files.remote_disk');
$prefix = config('excel.temporary_files.remote_prefix');
$content = Storage::disk($disk)->get($this->file_name);
$file_name = str_replace([$prefix, '.xlsx', '.xls'], '', $this->file_name);
$destination = $this->getMediaFolder('bulk_actions');
$media = MediaUploader::makePrivate()
->beforeSave(function(MediaModel $media) {
$media->company_id = company_id();
})
->fromString($content)
->useFilename($file_name)
->toDirectory($destination)
->upload();
Storage::disk($disk)->delete($this->file_name);
return $media;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Jobs\Common;
use App\Abstracts\JobShouldQueue;
use Illuminate\Support\Facades\File;
use ZipArchive;
class CreateZipForDownload extends JobShouldQueue
{
public $selected;
public $class;
public $file_name;
/**
* Create a new job instance.
*
* @param $selected
* @param $class
* @param $file_name
*/
public function __construct($selected, $class, $file_name)
{
$this->selected = $selected;
$this->class = $class;
$this->file_name = $file_name;
$this->onQueue('jobs');
}
public function handle()
{
$zip_archive = new ZipArchive();
$folder_path = 'app/temp/' . company_id() . '/bulk_actions/';
File::ensureDirectoryExists(get_storage_path($folder_path));
$zip_path = get_storage_path($folder_path . $this->file_name . '.zip');
$zip_archive->open($zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$total = count($this->selected);
$current = 0;
foreach ($this->selected as $selected) {
$current++;
if ($current === $total) {
$this->dispatch(new $this->class($selected, $folder_path, $zip_archive, true));
} else {
$this->dispatch(new $this->class($selected, $folder_path, $zip_archive));
}
}
return $zip_path;
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace App\Jobs\Document;
use App\Abstracts\Job;
use App\Events\Document\DocumentPrinting;
use App\Traits\Documents;
class DownloadDocument extends Job
{
use Documents;
public $document;
public $folder_path;
public $zip_archive;
public $close_zip;
public $method;
public function __construct($document, $folder_path = null, $zip_archive = null, $close_zip = false, $method = 'save')
{
$this->document = $document;
$this->folder_path = $folder_path;
$this->zip_archive = $zip_archive;
$this->close_zip = $close_zip;
$this->method = $method;
}
public function handle()
{
event(new DocumentPrinting($this->document));
$data = [
$this->document->type => $this->document,
'currency_style' => true
];
$view = view($this->document->template_path, $data)->render();
$html = mb_convert_encoding($view, 'HTML-ENTITIES', 'UTF-8');
$pdf = app('dompdf.wrapper');
$pdf->loadHTML($html);
$file_name = $this->getDocumentFileName($this->document);
switch ($this->method) {
case 'download':
return $pdf->download($file_name);
break;
default:
if (empty($this->zip_archive)) {
return;
}
$pdf_path = get_storage_path($this->folder_path . $file_name);
// Save the PDF file into temp folder
$pdf->save($pdf_path);
$this->zip_archive->addFile($pdf_path, $file_name);
if ($this->close_zip) {
$this->zip_archive->close();
}
return;
break;
}
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace App\Notifications\Common;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\HtmlString;
class DownloadCompleted extends Notification implements ShouldQueue
{
use Queueable;
protected $translation;
protected $file_name;
protected $download_url;
/**
* Create a notification instance.
*
* @param string $download_url
*/
public function __construct($translation, $file_name, $download_url)
{
$this->translation = $translation;
$this->file_name = $file_name;
$this->download_url = $download_url;
$this->onQueue('notifications');
}
/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return ['mail', 'database'];
}
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject(trans('notifications.download.completed.title'))
->line(new HtmlString('<br><br>'))
->line(trans('notifications.download.completed.description'))
->action(trans('general.download'), $this->download_url);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'title' => trans('notifications.menu.download_completed.title'),
'description' => trans('notifications.menu.download_completed.description', [
'type' => $this->translation,
'url' => $this->download_url,
]),
'translation' => $this->translation,
'file_name' => $this->file_name,
'download_url' => $this->download_url,
];
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace App\Notifications\Common;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\HtmlString;
class DownloadFailed extends Notification implements ShouldQueue
{
use Queueable;
/**
* The error exception.
*
* @var string
*/
public $message;
/**
* Create a notification instance.
*
* @param string $message
*/
public function __construct($message)
{
$this->message = $message;
$this->onQueue('notifications');
}
/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return ['mail', 'database'];
}
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject(trans('notifications.download.failed.title'))
->line(new HtmlString('<br><br>'))
->line(trans('notifications.download.failed.description'))
->line(new HtmlString('<br><br>'))
->line($this->message)
->line(new HtmlString('<br><br>'));
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'title' => trans('notifications.menu.download_failed.title'),
'description' => trans('notifications.menu.download_failed.description'),
'message' => $this->message,
];
}
}

View File

@ -10,6 +10,7 @@ return [
'duplicate' => 'Are you sure you want to <b>duplicate</b> selected record?',
'delete' => 'Are you sure you want to <b>delete</b> selected record?|Are you sure you want to <b>delete</b> selected records?',
'export' => 'Are you sure you want to <b>export</b> selected record?|Are you sure you want to <b>export</b> selected records?',
'download' => 'Are you sure you want to <b>download</b> selected record?|Are you sure you want to <b>download</b> selected records?',
'enable' => 'Are you sure you want to <b>enable</b> selected record?|Are you sure you want to <b>enable</b> selected records?',
'disable' => 'Are you sure you want to <b>disable</b> selected record?|Are you sure you want to <b>disable</b> selected records?',
'paid' => 'Are you sure you want to mark selected invoice as <b>paid</b>?|Are you sure you want to mark selected invoices as <b>paid</b>?',

View File

@ -12,6 +12,7 @@ return [
'import_queued' => ':type import has been scheduled! You will receive an email when it is finished.',
'exported' => ':type exported!',
'export_queued' => ':type export of the current page has been scheduled! You will receive an email when it is ready to download.',
'download_queued' => ':type download of the current page has been scheduled! You will receive an email when it is ready to download.',
'enabled' => ':type enabled!',
'disabled' => ':type disabled!',
'connected' => ':type connected!',

View File

@ -28,6 +28,24 @@ return [
],
'download' => [
'completed' => [
'title' => 'Download is ready',
'description' => 'The file is ready to download from the following link:',
],
'failed' => [
'title' => 'Download failed',
'description' => 'Not able to create the file due to the following issue:',
],
],
'import' => [
'completed' => [
@ -76,6 +94,20 @@ return [
'menu' => [
'download_completed' => [
'title' => 'Download is ready',
'description' => 'Your <strong>:type</strong> file is ready to <a href=":url" target="_blank"><strong>download</strong></a>.',
],
'download_failed' => [
'title' => 'Download failed',
'description' => 'Not able to create the file due to several issues. Check out your email for the details.',
],
'export_completed' => [
'title' => 'Export is ready',

View File

@ -19,6 +19,9 @@
@stack('body_scripts')
@livewireScripts
//should queue control added for bulk action download
@if (! should_queue())
@livewireScripts
@endif
@stack('scripts_end')