mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-07 05:31:24 +00:00
Refactor Custom Invoice/Estimate PDF Templates (#277)
* Add utility class for managing templates * Register custom pdf template views location * Update the make:template command to make use of PdfTemplateUtils * Update PDF invoice/estimate template controllers * Register pdf_templates filesystem disk * Remove unused leftovers * Reformat with pint
This commit is contained in:
committed by
GitHub
parent
12d9d6c801
commit
d862ee05e9
@@ -2,8 +2,11 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CreateTemplateCommand extends Command
|
||||
{
|
||||
@@ -37,25 +40,53 @@ class CreateTemplateCommand extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
$templateName = $this->argument('name');
|
||||
$type = $this->option('type');
|
||||
$templateType = $this->option('type');
|
||||
|
||||
if (! $type) {
|
||||
$type = $this->choice('Create a template for?', ['invoice', 'estimate']);
|
||||
if (! $templateType) {
|
||||
$templateType = $this->choice('Create a template for?', ['invoice', 'estimate']);
|
||||
}
|
||||
|
||||
if (Storage::disk('views')->exists("/app/pdf/{$type}/{$templateName}.blade.php")) {
|
||||
if (PdfTemplateUtils::customTemplateFileExists($templateType, sprintf('%s.blade.php', $templateName))) {
|
||||
$this->info('Template with given name already exists.');
|
||||
|
||||
return 0;
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
Storage::disk('views')->copy("/app/pdf/{$type}/{$type}1.blade.php", "/app/pdf/{$type}/{$templateName}.blade.php");
|
||||
copy(resource_path("static/img/PDF/{$type}1.png"), resource_path("static/img/PDF/{$templateName}.png"));
|
||||
if (! PdfTemplateUtils::toCustomTemplateMarkupFile(
|
||||
Str::replace(
|
||||
sprintf('app.pdf.%s', $templateType),
|
||||
sprintf('pdf_templates::%s', $templateType),
|
||||
Storage::disk('views')->get("/app/pdf/{$templateType}/{$templateType}1.blade.php"),
|
||||
),
|
||||
$templateType,
|
||||
$templateName
|
||||
)) {
|
||||
$this->error(sprintf('Unable to create %s template.', ucfirst($templateType)));
|
||||
|
||||
$path = resource_path("views/app/pdf/{$type}/{$templateName}.blade.php");
|
||||
$type = ucfirst($type);
|
||||
$this->info("{$type} Template created successfully at ".$path);
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
PdfTemplateUtils::toCustomTemplateImageFile(
|
||||
File::get(resource_path("static/img/PDF/{$templateType}1.png")),
|
||||
$templateType,
|
||||
$templateName,
|
||||
);
|
||||
|
||||
if (! PdfTemplateUtils::customTemplateFileExists($templateType, 'partials/table.blade.php')) {
|
||||
PdfTemplateUtils::toCustomTemplateFile(
|
||||
Storage::disk('views')->get("/app/pdf/{$templateType}/partials/table.blade.php"),
|
||||
$templateType,
|
||||
'partials/table.blade.php'
|
||||
);
|
||||
}
|
||||
|
||||
$this->info(
|
||||
sprintf('%s Template created successfully at %s',
|
||||
ucfirst($templateType),
|
||||
PdfTemplateUtils::getCustomTemplateFilePath($templateType, sprintf('%s.blade.php', $templateName))
|
||||
)
|
||||
);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace App\Http\Controllers\V1\Admin\Estimate;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Estimate;
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EstimateTemplatesController extends Controller
|
||||
@@ -11,13 +13,13 @@ class EstimateTemplatesController extends Controller
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$this->authorize('viewAny', Estimate::class);
|
||||
|
||||
$estimateTemplates = Estimate::estimateTemplates();
|
||||
$estimateTemplates = PdfTemplateUtils::getFormattedTemplates('estimate');
|
||||
|
||||
return response()->json([
|
||||
'estimateTemplates' => $estimateTemplates,
|
||||
|
||||
@@ -4,6 +4,9 @@ namespace App\Http\Controllers\V1\Admin\Invoice;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Invoice;
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class InvoiceTemplatesController extends Controller
|
||||
@@ -11,13 +14,16 @@ class InvoiceTemplatesController extends Controller
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$this->authorize('viewAny', Invoice::class);
|
||||
|
||||
$invoiceTemplates = Invoice::invoiceTemplates();
|
||||
$invoiceTemplates = PdfTemplateUtils::getFormattedTemplates('invoice');
|
||||
|
||||
return response()->json([
|
||||
'invoiceTemplates' => $invoiceTemplates,
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use App;
|
||||
use App\Mail\SendEstimateMail;
|
||||
use App\Services\SerialNumberFormatter;
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use App\Traits\GeneratesPdfTrait;
|
||||
use App\Traits\HasCustomFieldsTrait;
|
||||
use Barryvdh\DomPDF\Facade\Pdf as PDF;
|
||||
@@ -14,8 +15,6 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
@@ -424,11 +423,14 @@ class Estimate extends Model implements HasMedia
|
||||
'taxes' => $taxes,
|
||||
]);
|
||||
|
||||
$template = PdfTemplateUtils::findFormattedTemplate('estimate', $estimateTemplate, '');
|
||||
$templatePath = $template['custom'] ? sprintf('pdf_templates::estimate.%s', $estimateTemplate) : sprintf('app.pdf.estimate.%s', $estimateTemplate);
|
||||
|
||||
if (request()->has('preview')) {
|
||||
return view('app.pdf.estimate.'.$estimateTemplate);
|
||||
return view($templatePath);
|
||||
}
|
||||
|
||||
return PDF::loadView('app.pdf.estimate.'.$estimateTemplate);
|
||||
return PDF::loadView($templatePath);
|
||||
}
|
||||
|
||||
public function getCompanyAddress()
|
||||
@@ -499,27 +501,13 @@ class Estimate extends Model implements HasMedia
|
||||
];
|
||||
}
|
||||
|
||||
public static function estimateTemplates()
|
||||
{
|
||||
$templates = Storage::disk('views')->files('/app/pdf/estimate');
|
||||
$estimateTemplates = [];
|
||||
|
||||
foreach ($templates as $key => $template) {
|
||||
$templateName = Str::before(basename($template), '.blade.php');
|
||||
$estimateTemplates[$key]['name'] = $templateName;
|
||||
$estimateTemplates[$key]['path'] = Vite::asset('resources/static/img/PDF/'.$templateName.'.png');
|
||||
}
|
||||
|
||||
return $estimateTemplates;
|
||||
}
|
||||
|
||||
public function getInvoiceTemplateName()
|
||||
{
|
||||
$templateName = Str::replace('estimate', 'invoice', $this->template_name);
|
||||
|
||||
$name = [];
|
||||
|
||||
foreach (Invoice::invoiceTemplates() as $template) {
|
||||
foreach (PdfTemplateUtils::getFormattedTemplates('invoice') as $template) {
|
||||
$name[] = $template['name'];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Models;
|
||||
use App;
|
||||
use App\Mail\SendInvoiceMail;
|
||||
use App\Services\SerialNumberFormatter;
|
||||
use App\Space\ImageUtils;
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use App\Traits\GeneratesPdfTrait;
|
||||
use App\Traits\HasCustomFieldsTrait;
|
||||
use Barryvdh\DomPDF\Facade\Pdf as PDF;
|
||||
@@ -15,8 +15,6 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Nwidart\Modules\Facades\Module;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
@@ -582,11 +580,14 @@ class Invoice extends Model implements HasMedia
|
||||
'taxes' => $taxes,
|
||||
]);
|
||||
|
||||
$template = PdfTemplateUtils::findFormattedTemplate('invoice', $invoiceTemplate, '');
|
||||
$templatePath = $template['custom'] ? sprintf('pdf_templates::invoice.%s', $invoiceTemplate) : sprintf('app.pdf.invoice.%s', $invoiceTemplate);
|
||||
|
||||
if (request()->has('preview')) {
|
||||
return view('app.pdf.invoice.'.$invoiceTemplate);
|
||||
return view($templatePath);
|
||||
}
|
||||
|
||||
return PDF::loadView('app.pdf.invoice.'.$invoiceTemplate);
|
||||
return PDF::loadView($templatePath);
|
||||
}
|
||||
|
||||
public function getEmailAttachmentSetting()
|
||||
@@ -657,20 +658,6 @@ class Invoice extends Model implements HasMedia
|
||||
];
|
||||
}
|
||||
|
||||
public static function invoiceTemplates()
|
||||
{
|
||||
$templates = Storage::disk('views')->files('/app/pdf/invoice');
|
||||
$invoiceTemplates = [];
|
||||
|
||||
foreach ($templates as $key => $template) {
|
||||
$templateName = Str::before(basename($template), '.blade.php');
|
||||
$invoiceTemplates[$key]['name'] = $templateName;
|
||||
$invoiceTemplates[$key]['path'] = ImageUtils::toBase64Src(resource_path('static/img/PDF/'.$templateName.'.png'));
|
||||
}
|
||||
|
||||
return $invoiceTemplates;
|
||||
}
|
||||
|
||||
public function addInvoicePayment($amount)
|
||||
{
|
||||
$this->due_amount += $amount;
|
||||
|
||||
@@ -25,6 +25,7 @@ use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Silber\Bouncer\Database\Models as BouncerModels;
|
||||
use Silber\Bouncer\Database\Role;
|
||||
use View;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -58,6 +59,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
Gate::policy(Role::class, RolePolicy::class);
|
||||
|
||||
View::addNamespace('pdf_templates', storage_path('app/templates/pdf'));
|
||||
|
||||
$this->bootAuth();
|
||||
$this->bootBroadcast();
|
||||
|
||||
|
||||
140
app/Space/PdfTemplateUtils.php
Normal file
140
app/Space/PdfTemplateUtils.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace App\Space;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class PdfTemplateUtils
|
||||
{
|
||||
/**
|
||||
* Find the formatted template
|
||||
*
|
||||
* @param string $imageFormat
|
||||
* @return array|null
|
||||
*/
|
||||
public static function findFormattedTemplate($templateType, $templateName, $imageFormat = 'base64')
|
||||
{
|
||||
foreach (array_reverse(self::getFormattedTemplates($templateType, $imageFormat)) as $formattedTemplate) {
|
||||
if ($formattedTemplate['name'] === $templateName) {
|
||||
return $formattedTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the available formatted template paths
|
||||
*
|
||||
* @param string $imageFormat
|
||||
* @return array|array[]
|
||||
*/
|
||||
public static function getFormattedTemplates($templateType, $imageFormat = 'base64')
|
||||
{
|
||||
|
||||
$files_native = array_map(function ($file) {
|
||||
return [
|
||||
'path' => $file,
|
||||
'custom' => false,
|
||||
];
|
||||
}, Storage::disk('views')->files(sprintf('/app/pdf/%s', $templateType)));
|
||||
|
||||
$files_custom = array_map(function ($file) {
|
||||
return [
|
||||
'path' => $file,
|
||||
'custom' => true,
|
||||
];
|
||||
}, Storage::disk('pdf_templates')->files(sprintf('/%s', $templateType)));
|
||||
|
||||
$files = array_merge($files_native, $files_custom);
|
||||
$files = array_filter($files, function ($file) {
|
||||
return Str::endsWith($file['path'], '.blade.php');
|
||||
});
|
||||
|
||||
return array_map(function ($file) use ($templateType, $imageFormat) {
|
||||
$templateName = Str::before(basename($file['path']), '.blade.php');
|
||||
|
||||
if ($file['custom']) {
|
||||
$imagePath = self::getCustomTemplateFilePath($templateType, sprintf('%s.png', $templateName));
|
||||
$isCustomTemplate = true;
|
||||
} else {
|
||||
$imagePath = resource_path('static/img/PDF/'.$templateName.'.png');
|
||||
$isCustomTemplate = false;
|
||||
}
|
||||
|
||||
if (empty($imageFormat)) {
|
||||
$imageValue = '';
|
||||
} elseif ($imageFormat == 'path') {
|
||||
$imageValue = $imagePath;
|
||||
} else {
|
||||
$imageValue = File::exists($imagePath) ? ImageUtils::toBase64Src($imagePath) : '';
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $templateName,
|
||||
'path' => $imageValue,
|
||||
'custom' => $isCustomTemplate,
|
||||
];
|
||||
}, $files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns custom template path
|
||||
*
|
||||
* @param string $fileName
|
||||
*/
|
||||
public static function getCustomTemplateFilePath($templateType, $fileName = ''): string
|
||||
{
|
||||
$path = ! empty($fileName) ? sprintf('/%s/%s', $templateType, $fileName) : sprintf('/%s/', $templateType);
|
||||
|
||||
return Storage::disk('pdf_templates')->path($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if custom template exists.
|
||||
*
|
||||
* @param $templateName
|
||||
* @return string
|
||||
*/
|
||||
public static function customTemplateFileExists($templateType, $fileName)
|
||||
{
|
||||
return Storage::disk('pdf_templates')->exists(sprintf('/%s/%s', $templateType, $fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save template markup file
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function toCustomTemplateMarkupFile($contents, $templateType, $templateName)
|
||||
{
|
||||
return self::toCustomTemplateFile($contents, $templateType, $templateName.'.blade.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save template image file
|
||||
*
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function toCustomTemplateImageFile($contents, $templateType, $templateName, $imageType = 'png')
|
||||
{
|
||||
return self::toCustomTemplateFile($contents, $templateType, $templateName.'.'.$imageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save file contents into a template file of specific template type.
|
||||
*
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function toCustomTemplateFile($contents, $templateType, $fileName)
|
||||
{
|
||||
return Storage::disk('pdf_templates')->put(
|
||||
sprintf('/%s/%s', $templateType, $fileName),
|
||||
$contents
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,11 @@ return [
|
||||
'driver' => 'local',
|
||||
'root' => resource_path('views'),
|
||||
],
|
||||
|
||||
'pdf_templates' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/templates/pdf'),
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
1
storage/app/.gitignore
vendored
1
storage/app/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*
|
||||
!public/
|
||||
!templates/
|
||||
!.gitignore
|
||||
|
||||
3
storage/app/templates/.gitignore
vendored
Normal file
3
storage/app/templates/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!pdf/
|
||||
!.gitignore
|
||||
2
storage/app/templates/pdf/.gitignore
vendored
Normal file
2
storage/app/templates/pdf/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
Reference in New Issue
Block a user