diff --git a/app/Abstracts/BulkAction.php b/app/Abstracts/BulkAction.php index 6ad86c0c8..b7c5fb10c 100644 --- a/app/Abstracts/BulkAction.php +++ b/app/Abstracts/BulkAction.php @@ -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(); + } + } } diff --git a/app/BulkActions/Sales/Invoices.php b/app/BulkActions/Sales/Invoices.php index 00d202788..3eff38a3c 100644 --- a/app/BulkActions/Sales/Invoices.php +++ b/app/BulkActions/Sales/Invoices.php @@ -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)); + } } + \ No newline at end of file diff --git a/app/Http/Controllers/Sales/Invoices.php b/app/Http/Controllers/Sales/Invoices.php index d51729ae9..673495390 100644 --- a/app/Http/Controllers/Sales/Invoices.php +++ b/app/Http/Controllers/Sales/Invoices.php @@ -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')); } } diff --git a/app/Jobs/Common/CreateMediableForDownload.php b/app/Jobs/Common/CreateMediableForDownload.php new file mode 100644 index 000000000..03413d00d --- /dev/null +++ b/app/Jobs/Common/CreateMediableForDownload.php @@ -0,0 +1,113 @@ +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; + } +} diff --git a/app/Jobs/Common/CreateZipForDownload.php b/app/Jobs/Common/CreateZipForDownload.php new file mode 100644 index 000000000..6a3102ab7 --- /dev/null +++ b/app/Jobs/Common/CreateZipForDownload.php @@ -0,0 +1,60 @@ +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; + } +} diff --git a/app/Jobs/Document/DownloadDocument.php b/app/Jobs/Document/DownloadDocument.php new file mode 100644 index 000000000..9c70f7130 --- /dev/null +++ b/app/Jobs/Document/DownloadDocument.php @@ -0,0 +1,76 @@ +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; + } + } +} \ No newline at end of file diff --git a/app/Notifications/Common/DownloadCompleted.php b/app/Notifications/Common/DownloadCompleted.php new file mode 100644 index 000000000..9ff34c764 --- /dev/null +++ b/app/Notifications/Common/DownloadCompleted.php @@ -0,0 +1,80 @@ +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('

')) + ->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, + ]; + } +} diff --git a/app/Notifications/Common/DownloadFailed.php b/app/Notifications/Common/DownloadFailed.php new file mode 100644 index 000000000..5ac32e3d4 --- /dev/null +++ b/app/Notifications/Common/DownloadFailed.php @@ -0,0 +1,76 @@ +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('

')) + ->line(trans('notifications.download.failed.description')) + ->line(new HtmlString('

')) + ->line($this->message) + ->line(new HtmlString('

')); + } + + /** + * 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, + ]; + } +} diff --git a/resources/lang/en-GB/bulk_actions.php b/resources/lang/en-GB/bulk_actions.php index f9921dce4..a095a60dc 100644 --- a/resources/lang/en-GB/bulk_actions.php +++ b/resources/lang/en-GB/bulk_actions.php @@ -10,6 +10,7 @@ return [ 'duplicate' => 'Are you sure you want to duplicate selected record?', 'delete' => 'Are you sure you want to delete selected record?|Are you sure you want to delete selected records?', 'export' => 'Are you sure you want to export selected record?|Are you sure you want to export selected records?', + 'download' => 'Are you sure you want to download selected record?|Are you sure you want to download selected records?', 'enable' => 'Are you sure you want to enable selected record?|Are you sure you want to enable selected records?', 'disable' => 'Are you sure you want to disable selected record?|Are you sure you want to disable selected records?', 'paid' => 'Are you sure you want to mark selected invoice as paid?|Are you sure you want to mark selected invoices as paid?', diff --git a/resources/lang/en-GB/messages.php b/resources/lang/en-GB/messages.php index 2b8daa842..0e655640b 100644 --- a/resources/lang/en-GB/messages.php +++ b/resources/lang/en-GB/messages.php @@ -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!', diff --git a/resources/lang/en-GB/notifications.php b/resources/lang/en-GB/notifications.php index 1a300ca3f..443246015 100644 --- a/resources/lang/en-GB/notifications.php +++ b/resources/lang/en-GB/notifications.php @@ -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 :type file is ready to download.', + + ], + + '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', diff --git a/resources/views/components/layouts/print/scripts.blade.php b/resources/views/components/layouts/print/scripts.blade.php index aefeb326e..c8ca0a617 100644 --- a/resources/views/components/layouts/print/scripts.blade.php +++ b/resources/views/components/layouts/print/scripts.blade.php @@ -19,6 +19,9 @@ @stack('body_scripts') -@livewireScripts +//should queue control added for bulk action download +@if (! should_queue()) + @livewireScripts +@endif @stack('scripts_end')