diff --git a/app/Console/Commands/DesignUpdate.php b/app/Console/Commands/DesignUpdate.php
index 623364aea8..e582cc1822 100644
--- a/app/Console/Commands/DesignUpdate.php
+++ b/app/Console/Commands/DesignUpdate.php
@@ -69,9 +69,9 @@ class DesignUpdate extends Command
private function handleOnDb()
{
- foreach (Design::whereIsCustom(false)->get() as $design) {
- $invoice_design = new \App\Services\PdfMaker\Design(strtolower($design->name));
- $invoice_design->document();
+ foreach (Design::where('is_custom', false)->get() as $design) {
+
+ $invoice_design = new \App\Services\Pdf\DesignExtractor($design->name);
$design_object = new stdClass();
$design_object->includes = $invoice_design->getSectionHTML('style');
diff --git a/app/Http/Controllers/DesignController.php b/app/Http/Controllers/DesignController.php
index 9d509762e2..37b77acaca 100644
--- a/app/Http/Controllers/DesignController.php
+++ b/app/Http/Controllers/DesignController.php
@@ -554,9 +554,6 @@ class DesignController extends BaseController
$company = $user->getCompany();
- nlog("Design Change {$company->id}");
- nlog($request->all());
-
$design = Design::where('company_id', $company->id)
->orWhereNull('company_id')
->where('id', $design_id)
diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php
index 7e39fd4d34..fd722059e1 100644
--- a/app/Http/Controllers/PreviewController.php
+++ b/app/Http/Controllers/PreviewController.php
@@ -21,6 +21,7 @@ use App\Jobs\Util\PreviewPdf;
use App\Models\ClientContact;
use App\Services\Pdf\PdfMock;
use App\Utils\Traits\MakesHash;
+use App\Utils\VendorHtmlEngine;
use App\Services\Pdf\PdfService;
use App\Utils\PhantomJS\Phantom;
use App\Models\InvoiceInvitation;
@@ -32,6 +33,7 @@ use Illuminate\Support\Facades\App;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesInvoiceHtml;
use Turbo124\Beacon\Facades\LightLogs;
+use App\Models\PurchaseOrderInvitation;
use App\Utils\Traits\Pdf\PageNumbering;
use Illuminate\Support\Facades\Response;
use App\DataMapper\Analytics\LivePreview;
@@ -39,7 +41,6 @@ use App\Services\Template\TemplateService;
use App\Http\Requests\Preview\ShowPreviewRequest;
use App\Http\Requests\Preview\DesignPreviewRequest;
use App\Http\Requests\Preview\PreviewInvoiceRequest;
-use App\Utils\VendorHtmlEngine;
class PreviewController extends BaseController
{
@@ -145,99 +146,65 @@ class PreviewController extends BaseController
public function show(ShowPreviewRequest $request)
{
+
if ($request->input('design.is_template')) {
return $this->template();
}
- if (request()->has('entity') &&
- request()->has('entity_id') &&
- ! empty(request()->input('entity')) &&
- ! empty(request()->input('entity_id'))) {
+ if ($request->input('entity', false) &&
+ $request->input('entity_id', false) != '-1') {
- if ($request->input('entity') == 'purchase_order') {
- return app(\App\Http\Controllers\PreviewPurchaseOrderController::class)->show($request);
- }
-
- $design_object = json_decode(json_encode(request()->input('design')));
+ $design_object = json_decode(json_encode($request->input('design')));
if (! is_object($design_object)) {
return response()->json(['message' => ctrans('texts.invalid_design_object')], 400);
}
- $entity = Str::camel(request()->input('entity'));
+ $entity = Str::camel($request->input('entity'));
$class = "App\Models\\$entity";
- $entity_obj = $class::whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first();
+ $entity_obj = $class::whereId($this->decodePrimaryKey($request->input('entity_id')))->company()->first();
if (! $entity_obj) {
return $this->blankEntity();
}
- $entity_obj->load('client');
+ if($entity_obj->client){
+ $entity_obj->load('client');
+ $locale = $entity_obj->client->preferredLocale();
+ $settings = $entity_obj->client->getMergedSettings();
+ }
+ else {
+ $entity_obj->load('vendor');
+ $locale = $entity_obj->vendor->preferredLocale();
+ $settings = $entity_obj->vendor->getMergedSettings();
+ }
App::forgetInstance('translator');
$t = app('translator');
- App::setLocale($entity_obj->client->preferredLocale());
- $t->replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
+ App::setLocale($locale);
+ $t->replace(Ninja::transformTranslations($settings));
+ $invitation = $entity_obj->invitations()->first();
- if ($entity_obj->client) {
- $html = new HtmlEngine($entity_obj->invitations()->first());
- } else {
- $html = new VendorHtmlEngine($entity_obj->invitations()->first());
+ $ps = new PdfService($invitation, 'product', [
+ 'client' => $entity_obj->client ?? false,
+ 'vendor' => $entity_obj->vendor ?? false,
+ $request->input('entity')."s" => [$entity_obj],
+ ]);
+
+ $ps->boot()
+ ->designer
+ ->buildFromPartials($request->design['design']);
+
+ $ps->builder
+ ->build();
+
+ if ($request->query('html') == 'true') {
+ return $ps->getHtml();
}
- $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]);
-
- $state = [
- 'template' => $design->elements([
- 'client' => $entity_obj->client,
- 'entity' => $entity_obj,
- 'pdf_variables' => (array) $entity_obj->company->settings->pdf_variables,
- 'products' => request()->design['design']['product'],
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'process_markdown' => $entity_obj->client->company->markdown_enabled,
- 'options' => [
- 'client' => $entity_obj->client ?? [],
- 'vendor' => $entity_obj->vendor ?? [],
- request()->input('entity_type', 'invoice')."s" => [$entity_obj],
- ]
- ];
-
- $maker = new PdfMaker($state);
-
- $maker
- ->design($design)
- ->build();
-
- if (request()->query('html') == 'true') {
-
- return $maker->getCompiledHTML();
- }
-
- //if phantom js...... inject here..
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
-
- /** @var \App\Models\User $user */
- $user = auth()->user();
- $company = $user->company();
-
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
-
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
- $numbered_pdf = $this->pageNumbering($pdf, $company);
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
-
- }
-
- $pdf = (new PreviewPdf($maker->getCompiledHTML(true), $company))->handle();
+ $pdf = $ps->getPdf();
return response()->streamDownload(function () use ($pdf) {
echo $pdf;
@@ -338,200 +305,85 @@ class PreviewController extends BaseController
$t = app('translator');
$t->replace(Ninja::transformTranslations($company->settings));
- /** @var \App\Models\InvoiceInvitation $invitation */
- $invitation = InvoiceInvitation::where('company_id', $company->id)->orderBy('id', 'desc')->first();
+ $entity_string = 'invoice';
+
+ if(request()->input('entity') == 'purchase_order') {
+ $invitation = PurchaseOrderInvitation::where('company_id', $company->id)->orderBy('id', 'desc')->first();
+ $entity_string = 'purchase_order';
+ }
+ else{
+ /** @var \App\Models\InvoiceInvitation $invitation */
+ $invitation = InvoiceInvitation::where('company_id', $company->id)->orderBy('id', 'desc')->first();
+ }
/* If we don't have a valid invitation in the system - create a mock using transactions */
if (! $invitation) {
return $this->mockEntity();
}
- $design_object = json_decode(json_encode(request()->input('design')));
+ $design_object = json_decode(json_encode(request()->input('design')), true);
- if (! is_object($design_object)) {
+ if (! is_array($design_object)) {
return response()->json(['message' => 'Invalid custom design object'], 400);
}
- $html = new HtmlEngine($invitation);
+ $ps = new PdfService($invitation, 'product', [
+ 'client' => $invitation->client ?? false,
+ 'vendor' => $invitation->vendor ?? false,
+ "{$entity_string}s" => [$invitation->{$entity_string}],
+ ]);
- $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]);
+ $ps->boot()
+ ->designer
+ ->buildFromPartials($design_object['design']);
- $state = [
- 'template' => $design->elements([
- 'client' => $invitation->invoice->client,
- 'entity' => $invitation->invoice,
- 'pdf_variables' => (array) $invitation->invoice->company->settings->pdf_variables,
- 'products' => request()->design['design']['product'],
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'process_markdown' => $invitation->invoice->client->company->markdown_enabled,
- 'options' => [
- 'client' => $invitation->invoice->client,
- 'invoices' => [$invitation->invoice],
- ]
- ];
+ $ps->builder
+ ->build();
- $maker = new PdfMaker($state);
-
- $maker
- ->design($design)
- ->build();
if (request()->query('html') == 'true') {
- return $maker->getCompiledHTML();
+ return $ps->getHtml();
}
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
+ $pdf = $ps->getPdf();
- /** @var \App\Models\User $user */
- $user = auth()->user();
+ return response()->streamDownload(function () use ($pdf) {
+ echo $pdf;
+ }, 'preview.pdf', [
+ 'Content-Disposition' => 'inline',
+ 'Content-Type' => 'application/pdf',
+ 'Cache-Control:' => 'no-cache',
+ ]);
- /** @var \App\Models\Company $company */
- $company = $user->company();
-
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, $company);
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
- }
-
- $file_path = (new PreviewPdf($maker->getCompiledHTML(true), $company))->handle();
-
- $response = Response::make($file_path, 200);
- $response->header('Content-Type', 'application/pdf');
-
- return $response;
}
+
private function mockEntity()
{
- /** @var \App\Models\User $user */
+
+ $start = microtime(true);
$user = auth()->user();
/** @var \App\Models\Company $company */
$company = $user->company();
- try {
- DB::connection($company->db)->beginTransaction();
-
- /** @var \App\Models\Client $client */
- $client = Client::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $company->id,
- ]);
-
- /** @var \App\Models\ClientContact $contact */
- $contact = ClientContact::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $company->id,
- 'client_id' => $client->id,
- 'is_primary' => 1,
- 'send_email' => true,
- ]);
-
- $settings = $company->settings;
-
- /** @var \App\Models\Invoice $invoice */
- $invoice = Invoice::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $company->id,
- 'client_id' => $client->id,
- 'terms' => $company->settings->invoice_terms,
- 'footer' => $company->settings->invoice_footer,
- 'public_notes' => 'Sample Public Notes',
- ]);
-
- if ($settings->invoice_number_pattern) {
- $invoice->number = $this->getFormattedEntityNumber(
- $invoice,
- rand(1, 9999),
- $settings->counter_padding ?: 4,
- $settings->invoice_number_pattern,
- );
- $invoice->save();
- }
-
- $invitation = InvoiceInvitation::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $company->id,
- 'invoice_id' => $invoice->id,
- 'client_contact_id' => $contact->id,
- ]);
-
- $invoice->setRelation('invitations', $invitation);
- $invoice->setRelation('client', $client);
- $invoice->setRelation('company', $company);
- $invoice->load('client.company');
-
- $design_object = json_decode(json_encode(request()->input('design')));
-
- if (! is_object($design_object)) {
- return response()->json(['message' => 'Invalid custom design object'], 400);
- }
-
- $html = new HtmlEngine($invoice->invitations()->first());
-
- $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]);
-
- $state = [
- 'template' => $design->elements([
- 'client' => $invoice->client,
- 'entity' => $invoice,
- 'pdf_variables' => (array) $settings->pdf_variables,
- 'products' => request()->design['design']['product'],
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'process_markdown' => $invoice->client->company->markdown_enabled,
- 'options' => [
- 'client' => $invoice->client,
- 'invoices' => [$invoice],
- ]
- ];
-
- $maker = new PdfMaker($state);
-
- $maker
- ->design($design)
- ->build();
-
- DB::connection($company->db)->rollBack();
- } catch (\Exception $e) {
- DB::connection($company->db)->rollBack();
- return response()->json(['message' => $e->getMessage()], 400);
- }
+ $request = request()->input('design');
+ $request['entity_type'] = request()->input('entity', 'invoice');
+ $pdf = (new PdfMock($request, $company))->build();
+
if (request()->query('html') == 'true') {
- return $maker->getCompiledHTML();
+ return $pdf->getHtml();
}
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
+ $pdf = $pdf->getPdf();
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, $company);
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
- }
-
- $file_path = (new PreviewPdf($maker->getCompiledHTML(true), $company))->handle();
-
- $response = Response::make($file_path, 200);
+ $response = Response::make($pdf, 200);
$response->header('Content-Type', 'application/pdf');
+ $response->header('Server-Timing', (string) (microtime(true) - $start));
+
return $response;
+
}
+
}
diff --git a/app/Http/Controllers/PreviewPurchaseOrderController.php b/app/Http/Controllers/PreviewPurchaseOrderController.php
index 86f218292e..4c0323ba48 100644
--- a/app/Http/Controllers/PreviewPurchaseOrderController.php
+++ b/app/Http/Controllers/PreviewPurchaseOrderController.php
@@ -37,8 +37,6 @@ use Illuminate\Support\Facades\Response;
use App\DataMapper\Analytics\LivePreview;
use App\Repositories\PurchaseOrderRepository;
use App\Http\Requests\Preview\ShowPreviewRequest;
-use App\Services\PdfMaker\Design as PdfDesignModel;
-use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Http\Requests\Preview\PreviewPurchaseOrderRequest;
class PreviewPurchaseOrderController extends BaseController
@@ -87,18 +85,17 @@ class PreviewPurchaseOrderController extends BaseController
*/
public function show(ShowPreviewRequest $request)
{
- if (request()->has('entity') &&
- request()->has('entity_id') &&
- ! empty(request()->input('entity')) &&
- ! empty(request()->input('entity_id')) &&
- request()->has('body')) {
- $design_object = json_decode(json_encode(request()->input('design')));
+ if ($request->input('entity', false) &&
+ $request->input('entity_id', false) != '-1' &&
+ $request->has('body')) {
- if (! is_object($design_object)) {
+ $design_object = json_decode(json_encode($request->input('design')), true);
+
+ if (! is_array($design_object)) {
return response()->json(['message' => ctrans('texts.invalid_design_object')], 400);
}
- $entity_obj = PurchaseOrder::query()->whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first();
+ $entity_obj = PurchaseOrder::query()->whereId($this->decodePrimaryKey($request->input('entity_id')))->company()->first();
if (! $entity_obj) {
return $this->blankEntity();
@@ -109,63 +106,36 @@ class PreviewPurchaseOrderController extends BaseController
App::setLocale($entity_obj->company->locale());
$t->replace(Ninja::transformTranslations($entity_obj->company->settings));
- $html = new VendorHtmlEngine($entity_obj->invitations()->first());
+ $invitation = $entity_obj->invitations()->first();
- $design_namespace = 'App\Services\PdfMaker\Designs\\'.request()->design['name'];
+ $ps = new PdfService($invitation, 'product', [
+ 'client' => $entity_obj->client ?? false,
+ 'vendor' => $entity_obj->vendor ?? false,
+ $request->input('entity')."s" => [$entity_obj],
+ ]);
- $design_class = new $design_namespace();
+ $ps->boot()
+ ->designer
+ ->buildFromPartials($request->design['design']);
- $state = [
- 'template' => $design_class->elements([
- 'client' => null,
- 'vendor' => $entity_obj->vendor,
- 'entity' => $entity_obj,
- 'pdf_variables' => (array) $entity_obj->company->settings->pdf_variables,
- 'variables' => $html->generateLabelsAndValues(),
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'process_markdown' => $entity_obj->company->markdown_enabled,
- 'options' => [
- 'vendor' => $entity_obj->vendor ?? [],
- request()->input('entity')."s" => [$entity_obj],
- ]
- ];
+ $ps->builder
+ ->build();
- $design = new Design(request()->design['name']);
- $maker = new PdfMaker($state);
-
- $maker
- ->design($design)
- ->build();
-
- if (request()->query('html') == 'true') {
- return $maker->getCompiledHTML();
+ if ($request->query('html') == 'true') {
+ return $ps->getHtml();
}
- //if phantom js...... inject here..
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
+ $pdf = $ps->getPdf();
- /** @var \App\Models\User $user */
- $user = auth()->user();
+ return response()->streamDownload(function () use ($pdf) {
+ echo $pdf;
+ }, 'preview.pdf', [
+ 'Content-Disposition' => 'inline',
+ 'Content-Type' => 'application/pdf',
+ 'Cache-Control:' => 'no-cache',
+ ]);
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
- $numbered_pdf = $this->pageNumbering($pdf, $user->company());
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
- }
-
- //else
- $file_path = (new PreviewPdf($maker->getCompiledHTML(true), $user->company()))->handle();
-
- return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
}
return $this->blankEntity();
@@ -238,177 +208,70 @@ class PreviewPurchaseOrderController extends BaseController
return $this->mockEntity();
}
- $design_object = json_decode(json_encode(request()->input('design')));
+
+ $design_object = json_decode(json_encode(request()->input('design')), true);
- if (! is_object($design_object)) {
+ if (! is_array($design_object)) {
return response()->json(['message' => 'Invalid custom design object'], 400);
}
- $html = new VendorHtmlEngine($invitation);
+ $ps = new PdfService($invitation, 'product', [
+ 'client' => $invitation->client ?? false,
+ 'vendor' => $invitation->vendor ?? false,
+ "{$entity_string}s" => [$invitation->{$entity_string}],
+ ]);
- $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]);
+ $ps->boot()
+ ->designer
+ ->buildFromPartials($design_object['design']);
- $state = [
- 'template' => $design->elements([
- 'client' => null,
- 'vendor' => $invitation->purchase_order->vendor,
- 'entity' => $invitation->purchase_order,
- 'pdf_variables' => (array) $invitation->company->settings->pdf_variables,
- 'products' => request()->design['design']['product'],
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'process_markdown' => $invitation->company->markdown_enabled,
- 'options' => [
- 'vendor' => $invitation->purchase_order->vendor,
- 'purchase_orders' => [$invitation->purchase_order],
- ],
- ];
+ $ps->builder
+ ->build();
- $maker = new PdfMaker($state);
-
- $maker
- ->design($design)
- ->build();
-
if (request()->query('html') == 'true') {
- return $maker->getCompiledHTML();
+ return $ps->getHtml();
}
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
+ $pdf = $ps->getPdf();
- /** @var \App\Models\User $user */
- $user = auth()->user();
+ return response()->streamDownload(function () use ($pdf) {
+ echo $pdf;
+ }, 'preview.pdf', [
+ 'Content-Disposition' => 'inline',
+ 'Content-Type' => 'application/pdf',
+ 'Cache-Control:' => 'no-cache',
+ ]);
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, $user->company());
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
- }
-
- $file_path = (new PreviewPdf($maker->getCompiledHTML(true), $user->company()))->handle();
-
- $response = Response::make($file_path, 200);
- $response->header('Content-Type', 'application/pdf');
-
- return $response;
}
private function mockEntity()
{
- /** @var \App\Models\User $user */
+
+ nlog("mockEntity");
+
+ $start = microtime(true);
$user = auth()->user();
- DB::connection($user->company()->db)->beginTransaction();
+ /** @var \App\Models\Company $company */
+ $company = $user->company();
- /** @var \App\Models\Vendor $vendor */
- $vendor = Vendor::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $user->company()->id,
- ]);
+ $request = request()->input('design');
+ $request['entity_type'] = request()->input('entity', 'invoice');
- /** @var \App\Models\VendorContact $contact */
- $contact = VendorContact::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $user->company()->id,
- 'vendor_id' => $vendor->id,
- 'is_primary' => 1,
- 'send_email' => true,
- ]);
-
- /** @var \App\Models\PurchaseOrder $purchase_order */
- $purchase_order = PurchaseOrder::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $user->company()->id,
- 'vendor_id' => $vendor->id,
- 'terms' => 'Sample Terms',
- 'footer' => 'Sample Footer',
- 'public_notes' => 'Sample Public Notes',
- ]);
-
- /** @var \App\Models\PurchaseOrderInvitation $invitation */
- $invitation = PurchaseOrderInvitation::factory()->create([
- 'user_id' => $user->id,
- 'company_id' => $user->company()->id,
- 'purchase_order_id' => $purchase_order->id,
- 'vendor_contact_id' => $contact->id,
- ]);
-
- $purchase_order->setRelation('invitations', $invitation);
- $purchase_order->setRelation('vendor', $vendor);
- $purchase_order->setRelation('company', $user->company());
- $purchase_order->load('vendor.company');
-
- $design_object = json_decode(json_encode(request()->input('design')));
-
- if (! is_object($design_object)) {
- return response()->json(['message' => 'Invalid custom design object'], 400);
- }
-
- $html = new VendorHtmlEngine($purchase_order->invitations()->first());
-
- $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]);
-
- $state = [
- 'template' => $design->elements([
- 'client' => null,
- 'vendor' => $purchase_order->vendor,
- 'entity' => $purchase_order,
- 'pdf_variables' => (array) $purchase_order->company->settings->pdf_variables,
- 'products' => request()->design['design']['product'],
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'process_markdown' => $purchase_order->company->markdown_enabled,
- 'options' => [
- 'vendor' => $invitation->purchase_order->vendor,
- 'purchase_orders' => [$invitation->purchase_order],
- ],
- ];
-
- $maker = new PdfMaker($state);
-
- $maker
- ->design($design)
- ->build();
-
- /** @var \App\Models\User $user */
- $user = auth()->user();
-
- DB::connection($user->company()->db)->rollBack();
+ $pdf = (new PdfMock($request, $company))->build();
if (request()->query('html') == 'true') {
- return $maker->getCompiledHTML();
+ return $pdf->getHtml();
}
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
+ $pdf = $pdf->getPdf();
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, $user->company());
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
- }
-
- $file_path = (new PreviewPdf($maker->getCompiledHTML(true), $user->company()))->handle();
-
- $response = Response::make($file_path, 200);
+ $response = Response::make($pdf, 200);
$response->header('Content-Type', 'application/pdf');
+ $response->header('Server-Timing', (string) (microtime(true) - $start));
return $response;
+
}
}
diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php
index 2feaa7073b..28b6730510 100644
--- a/app/Http/Controllers/SetupController.php
+++ b/app/Http/Controllers/SetupController.php
@@ -78,7 +78,7 @@ class SetupController extends Controller
$db_database = $request->input('db_database');
$db_username = $request->input('db_username');
$db_password = $request->input('db_password');
- $mail_port = $request->input('mail_port');
+ $mail_port = $request->input('mail_port',0);
$encryption = $request->input('encryption');
$mail_host = $request->input('mail_host');
$mail_username = $request->input('mail_username');
diff --git a/app/Http/Requests/Preview/PreviewPurchaseOrderRequest.php b/app/Http/Requests/Preview/PreviewPurchaseOrderRequest.php
index 4d11f21758..ad1a31ec44 100644
--- a/app/Http/Requests/Preview/PreviewPurchaseOrderRequest.php
+++ b/app/Http/Requests/Preview/PreviewPurchaseOrderRequest.php
@@ -11,12 +11,13 @@
namespace App\Http\Requests\Preview;
-use App\Http\Requests\Request;
-use App\Models\PurchaseOrder;
-use App\Models\PurchaseOrderInvitation;
use App\Models\Vendor;
-use App\Utils\Traits\CleanLineItems;
+use App\Models\PurchaseOrder;
+use App\Http\Requests\Request;
use App\Utils\Traits\MakesHash;
+use Illuminate\Validation\Rule;
+use App\Utils\Traits\CleanLineItems;
+use App\Models\PurchaseOrderInvitation;
class PreviewPurchaseOrderRequest extends Request
{
@@ -40,9 +41,14 @@ class PreviewPurchaseOrderRequest extends Request
public function rules()
{
+
+ /** @var \App\Models\User $user */
+ $user = auth()->user();
+
$rules = [];
$rules['number'] = ['nullable'];
+ $rules['vendor_id'] = ['required', Rule::exists(Vendor::class, 'id')->where('is_deleted', 0)->where('company_id', $user->company()->id)];
return $rules;
}
diff --git a/app/Jobs/Cron/RecurringInvoicesCron.php b/app/Jobs/Cron/RecurringInvoicesCron.php
index c99e12d9a1..1b0fef8638 100644
--- a/app/Jobs/Cron/RecurringInvoicesCron.php
+++ b/app/Jobs/Cron/RecurringInvoicesCron.php
@@ -55,8 +55,8 @@ class RecurringInvoicesCron
->whereNull('deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
- $query->where('is_deleted', 0)
- ->where('deleted_at', null);
+ $query->where('is_deleted', false)
+ ->whereNull('deleted_at');
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0)
@@ -97,8 +97,9 @@ class RecurringInvoicesCron
->whereNotNull('next_send_date')
->where('next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
- $query->where('is_deleted', 0)
- ->where('deleted_at', null);
+ $query->where('is_deleted', false)
+ ->whereNull('deleted_at');
+
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0)
diff --git a/app/Jobs/Vendor/CreatePurchaseOrderPdf.php b/app/Jobs/Vendor/CreatePurchaseOrderPdf.php
deleted file mode 100644
index d40765b560..0000000000
--- a/app/Jobs/Vendor/CreatePurchaseOrderPdf.php
+++ /dev/null
@@ -1,218 +0,0 @@
-invitation = $invitation;
- $this->company = $invitation->company;
-
- $this->entity = $invitation->purchase_order;
- $this->entity_string = 'purchase_order';
-
- $this->contact = $invitation->contact;
-
- $this->vendor = $invitation->contact->vendor;
- $this->vendor->load('company');
-
- $this->disk = $disk ?? config('filesystems.default');
- }
-
- public function handle()
- {
- /** Testing this override to improve PDF generation performance */
- $ps = new PdfService($this->invitation, 'product', [
- 'client' => $this->entity->client ?? false,
- 'vendor' => $this->entity->vendor ?? false,
- "{$this->entity_string}s" => [$this->entity],
- ]);
-
- nlog("returning purchase order");
-
- return $ps->boot()->getPdf();
-
- }
-
- public function rawPdf()
- {
- MultiDB::setDb($this->company->db);
-
- /* Forget the singleton*/
- App::forgetInstance('translator');
-
- /* Init a new copy of the translator*/
- $t = app('translator');
- /* Set the locale*/
- App::setLocale($this->vendor->locale());
-
- /* Set customized translations _NOW_ */
- $t->replace(Ninja::transformTranslations($this->company->settings));
-
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->generate($this->invitation, true);
- }
-
- $entity_design_id = '';
-
- $this->path = $this->vendor->purchase_order_filepath($this->invitation);
- $entity_design_id = 'purchase_order_design_id';
-
- $this->file_path = $this->path.$this->entity->numberFormatter().'.pdf';
-
- $entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey('Wpmbk5ezJn');
-
- $design = Design::withTrashed()->find($entity_design_id);
-
- /* Catch all in case migration doesn't pass back a valid design */
- if (!$design) {
- /** @var \App\Models\Design $design */
- $design = Design::find(2);
- }
-
- $html = new VendorHtmlEngine($this->invitation);
-
- if ($design->is_custom) {
- $options = [
- 'custom_partials' => json_decode(json_encode($design->design), true)
- ];
- $template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
- } else {
- $template = new PdfMakerDesign(strtolower($design->name));
- }
-
- $variables = $html->generateLabelsAndValues();
-
- $state = [
- 'template' => $template->elements([
- 'client' => null,
- 'vendor' => $this->vendor,
- 'entity' => $this->entity,
- 'pdf_variables' => (array) $this->company->settings->pdf_variables,
- '$product' => $design->design->product,
- 'variables' => $variables,
- ]),
- 'variables' => $variables,
- 'options' => [
- 'all_pages_header' => $this->entity->company->getSetting('all_pages_header'),
- 'all_pages_footer' => $this->entity->company->getSetting('all_pages_footer'),
- 'client' => null,
- 'vendor' => $this->vendor,
- 'entity' => $this->entity,
- 'variables' => $variables,
- ],
- 'process_markdown' => $this->entity->company->markdown_enabled,
- ];
-
- $maker = new PdfMakerService($state);
-
- $maker
- ->design($template)
- ->build();
-
- $pdf = null;
-
- try {
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, $this->company);
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
- } else {
- $pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, $this->company);
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
- }
- } catch (\Exception $e) {
- nlog($e->getMessage());
- }
-
- if (config('ninja.log_pdf_html')) {
- nlog($maker->getCompiledHTML());
- }
-
- $maker = null;
- $state = null;
-
- return $pdf;
- }
-
- public function failed($e)
- {
- }
-}
diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php
index 2778cbebfd..7d3d26a71b 100644
--- a/app/Models/Vendor.php
+++ b/app/Models/Vendor.php
@@ -319,6 +319,11 @@ class Vendor extends BaseModel
return $this->language ? $this->language->locale : $this->company->locale();
}
+ public function preferredLocale(): string
+ {
+ return $this->locale();
+ }
+
public function language(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Language::class);
diff --git a/app/Repositories/ActivityRepository.php b/app/Repositories/ActivityRepository.php
index 79c0e43974..a4cab0c8ab 100644
--- a/app/Repositories/ActivityRepository.php
+++ b/app/Repositories/ActivityRepository.php
@@ -11,23 +11,21 @@
namespace App\Repositories;
-use App\Models\Activity;
+use App\Models\User;
+use App\Models\Quote;
use App\Models\Backup;
-use App\Models\CompanyToken;
use App\Models\Credit;
use App\Models\Design;
use App\Models\Invoice;
-use App\Models\PurchaseOrder;
-use App\Models\Quote;
-use App\Models\RecurringInvoice;
-use App\Models\User;
-use App\Services\PdfMaker\Design as PdfDesignModel;
-use App\Services\PdfMaker\Design as PdfMakerDesign;
-use App\Services\PdfMaker\PdfMaker as PdfMakerService;
+use App\Models\Activity;
use App\Utils\HtmlEngine;
+use App\Models\CompanyToken;
+use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
-use App\Utils\Traits\MakesInvoiceHtml;
use App\Utils\VendorHtmlEngine;
+use App\Models\RecurringInvoice;
+use App\Services\Pdf\PdfService;
+use App\Utils\Traits\MakesInvoiceHtml;
/**
* Class for activity repository.
@@ -111,7 +109,7 @@ class ActivityRepository extends BaseRepository
$backup->json_backup = '';
$backup->save();
- $backup->storeRemotely($this->generateVendorHtml($entity), $entity->vendor);
+ $backup->storeRemotely($this->generateHtml($entity), $entity->vendor);
return;
@@ -132,79 +130,44 @@ class ActivityRepository extends BaseRepository
return false;
}
- private function generateVendorHtml($entity)
- {
- $entity_design_id = $entity->design_id ? $entity->design_id : $this->decodePrimaryKey($entity->vendor->getSetting('purchase_order_design_id'));
-
- $design = Design::withTrashed()->find($entity_design_id);
-
- if (! $entity->invitations()->exists() || ! $design) {
- return '';
- }
-
- $entity->load('vendor.company', 'invitations');
-
- $html = new VendorHtmlEngine($entity->invitations->first()->load('purchase_order', 'contact'));
-
- if ($design->is_custom) {
- $options = [
- 'custom_partials' => json_decode(json_encode($design->design), true),
- ];
- $template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
- } else {
- $template = new PdfMakerDesign(strtolower($design->name));
- }
-
- $state = [
- 'template' => $template->elements([
- 'vendor' => $entity->vendor,
- 'entity' => $entity,
- 'pdf_variables' => (array) $entity->company->settings->pdf_variables,
- '$product' => $design->design->product,
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'options' => [
- 'all_pages_header' => $entity->vendor->getSetting('all_pages_header'),
- 'all_pages_footer' => $entity->vendor->getSetting('all_pages_footer'),
- 'vendor' => $entity->vendor,
- 'entity' => $entity,
- ],
- 'process_markdown' => $entity->vendor->company->markdown_enabled,
- ];
-
- $maker = new PdfMakerService($state);
-
- $html = $maker->design($template)
- ->build()
- ->getCompiledHTML(true);
-
- $maker = null;
- $state = null;
-
- return $html;
-
- }
-
private function generateHtml($entity)
{
$entity_design_id = '';
$entity_type = '';
+ $settings = $entity->client ? $entity->client->getMergedSettings() : $entity->vendor->getMergedSettings();
+
if ($entity instanceof Invoice) {
$entity_type = 'invoice';
$entity_design_id = 'invoice_design_id';
+ $entity->load('client.company', 'invitations');
+ $document_type = 'product';
} elseif ($entity instanceof RecurringInvoice) {
$entity_type = 'recurring_invoice';
$entity_design_id = 'invoice_design_id';
+
+ $entity->load('client.company', 'invitations');
+ $document_type = 'product';
} elseif ($entity instanceof Quote) {
$entity_type = 'quote';
$entity_design_id = 'quote_design_id';
+
+ $entity->load('client.company', 'invitations');
+ $document_type = 'product';
} elseif ($entity instanceof Credit) {
- $entity_type = 'credit';
+ $entity_type = 'product';
+
+ $entity->load('client.company', 'invitations');
$entity_design_id = 'credit_design_id';
+ $document_type = 'credit';
+ } elseif ($entity instanceof PurchaseOrder) {
+ $entity_type = 'purchase_order';
+ $entity_design_id = 'purchase_order_design_id';
+ $document_type = 'purchase_order';
+ $entity->load('vendor.company', 'invitations');
}
- $entity_design_id = $entity->design_id ? $entity->design_id : $this->decodePrimaryKey($entity->client->getSetting($entity_design_id));
+ $entity_design_id = $entity->design_id ? $entity->design_id : $this->decodePrimaryKey($settings->{$entity_design_id});
$design = Design::withTrashed()->find($entity_design_id);
@@ -212,45 +175,13 @@ class ActivityRepository extends BaseRepository
return '';
}
- $entity->load('client.company', 'invitations');
+ $ps = new PdfService($entity->invitations()->first(), $document_type, [
+ 'client' => $entity->client ?? false,
+ 'vendor' => $entity->vendor ?? false,
+ "{$entity_type}s" => [$entity],
+ ]);
- $html = new HtmlEngine($entity->invitations->first()->load($entity_type, 'contact'));
+ return $ps->boot()->getHtml();
- if ($design->is_custom) {
- $options = [
- 'custom_partials' => json_decode(json_encode($design->design), true),
- ];
- $template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
- } else {
- $template = new PdfMakerDesign(strtolower($design->name));
- }
-
- $state = [
- 'template' => $template->elements([
- 'client' => $entity->client,
- 'entity' => $entity,
- 'pdf_variables' => (array) $entity->company->settings->pdf_variables,
- '$product' => $design->design->product,
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'options' => [
- 'all_pages_header' => $entity->client->getSetting('all_pages_header'),
- 'all_pages_footer' => $entity->client->getSetting('all_pages_footer'),
- 'client' => $entity->client,
- 'entity' => $entity,
- ],
- 'process_markdown' => $entity->client->company->markdown_enabled,
- ];
-
- $maker = new PdfMakerService($state);
-
- $html = $maker->design($template)
- ->build()
- ->getCompiledHTML(true);
-
- $maker = null;
- $state = null;
-
- return $html;
}
}
diff --git a/app/Services/Client/Statement.php b/app/Services/Client/Statement.php
index 9fe51761bf..ff6ca627de 100644
--- a/app/Services/Client/Statement.php
+++ b/app/Services/Client/Statement.php
@@ -12,29 +12,27 @@
namespace App\Services\Client;
-use App\Factory\InvoiceFactory;
-use App\Factory\InvoiceInvitationFactory;
-use App\Factory\InvoiceItemFactory;
+use App\Utils\Number;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Design;
use App\Models\Invoice;
use App\Models\Payment;
-use App\Services\PdfMaker\Design as PdfMakerDesign;
-use App\Services\PdfMaker\PdfMaker;
-use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine;
-use App\Utils\Number;
+use Illuminate\Support\Carbon;
+use App\Factory\InvoiceFactory;
+use App\Utils\Traits\MakesHash;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\MakesDates;
-use App\Utils\Traits\MakesHash;
-use App\Utils\Traits\Pdf\PdfMaker as PdfMakerTrait;
+use App\Utils\HostedPDF\NinjaPdf;
+use App\Utils\Traits\Pdf\PdfMaker;
+use App\Factory\InvoiceItemFactory;
+use App\Factory\InvoiceInvitationFactory;
use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Support\Carbon;
class Statement
{
- use PdfMakerTrait;
+ use PdfMaker;
use MakesHash;
use MakesDates;
@@ -89,52 +87,33 @@ class Statement
return $pdf;
}
- if ($this->getDesign()->is_custom) {
- $this->options['custom_partials'] = \json_decode(\json_encode($this->getDesign()->design), true);
- $template = new PdfMakerDesign(\App\Services\PdfMaker\Design::CUSTOM, $this->options);
- } else {
- $template = new PdfMakerDesign(strtolower($this->getDesign()->name), $this->options);
- }
+ $variables['values']['$show_paid_stamp'] = 'none';
- $variables['values']['$show_paid_stamp'] = 'none'; //do not show paid stamp on statement
-
- $state = [
- 'template' => $template->elements([
- 'client' => $this->client,
- 'entity' => $this->entity,
- 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables,
- '$product' => $this->getDesign()->design->product,
- 'variables' => $variables,
- 'invoices' => $this->getInvoices()->cursor(),
- 'payments' => $this->getPayments()->cursor(),
- 'credits' => $this->getCredits()->cursor(),
- 'aging' => $this->getAging(),
- 'unapplied' => $this->getUnapplied()->cursor(),
- ], \App\Services\PdfMaker\Design::STATEMENT),
- 'variables' => $variables,
- 'options' => [
- ],
- 'process_markdown' => $this->entity->client->company->markdown_enabled,
+ $options = [
+ // 'client' => $this->entity->client,
+ // 'entity' => $this->entity,
+ // 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables,
+ // '$product' => $this->getDesign()->design->product,
+ // 'variables' => $variables,
+ 'invoices' => $this->getInvoices()->cursor(),
+ 'payments' => $this->getPayments()->cursor(),
+ 'credits' => $this->getCredits()->cursor(),
+ 'aging' => $this->getAging(),
+ 'unapplied' => $this->getUnapplied()->cursor()
];
- $maker = new PdfMaker($state);
+ $ps = new \App\Services\Pdf\PdfService($invitation, 'statement', array_merge($options, $this->options));
+ $pdf = $ps->boot();
+
+ $ps->config->pdf_variables = (array) $this->entity->company->settings->pdf_variables;
+ $ps->html_variables = $variables;
+ $ps->config->design = $this->getDesign();
- $maker
- ->design($template)
- ->build();
+ $ps->designer->buildFromPartials((array)$ps->config->design->design);
+ $ps->builder->build();
+ $pdf = $ps->getPdf();
- $pdf = null;
- $html = $maker->getCompiledHTML(true);
-
- // nlog($html);
-
- $pdf = $this->convertToPdf($html);
-
- $this->setVariables($variables);
-
- $maker = null;
- $state = null;
return $pdf;
@@ -219,7 +198,7 @@ class Statement
protected function setupEntity(): self
{
if ($this->getInvoices()->count() >= 1) {
- $this->entity = $this->getInvoices()->first();//@phpstan-ignore-line
+ $this->entity = $this->getInvoices()->first(); //@phpstan-ignore-line
}
else {
$this->entity = $this->client->invoices()->whereHas('invitations')->first();
@@ -235,7 +214,7 @@ class Statement
$this->entity = \App\Models\Invoice::factory()->make(); //@phpstan-ignore-line
$this->entity->client = \App\Models\Client::factory()->make(['settings' => $settings]); //@phpstan-ignore-line
$this->entity->client->setRelation('company', $this->client->company);
- $this->entity->invitation = \App\Models\InvoiceInvitation::factory()->make(); //@phpstan-ignore-line
+ $this->entity->setRelation('invitations', \App\Models\InvoiceInvitation::factory()->make()); //@phpstan-ignore-line
$this->entity->setRelation('company', $this->client->company);
$this->entity->setRelation('user', $this->client->user);
@@ -540,8 +519,8 @@ class Statement
{
$id = 1;
- if (! empty($this->client->getSetting('entity_design_id'))) {
- $id = (int) $this->client->getSetting('entity_design_id');
+ if (! empty($this->client->getSetting('statement_design_id'))) {
+ $id = (int) $this->client->getSetting('statement_design_id');
}
return Design::withTrashed()->find($id);
diff --git a/app/Services/Invoice/GenerateDeliveryNote.php b/app/Services/Invoice/GenerateDeliveryNote.php
index 0c1cf2dc38..ea7eef0d99 100644
--- a/app/Services/Invoice/GenerateDeliveryNote.php
+++ b/app/Services/Invoice/GenerateDeliveryNote.php
@@ -12,18 +12,14 @@
namespace App\Services\Invoice;
-use App\Models\ClientContact;
use App\Models\Design;
use App\Models\Invoice;
-use App\Services\PdfMaker\Design as PdfMakerDesign;
-use App\Services\PdfMaker\PdfMaker as PdfMakerService;
-use App\Services\Template\TemplateService;
-use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine;
-use App\Utils\PhantomJS\Phantom;
+use App\Models\ClientContact;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Pdf\PdfMaker;
use Illuminate\Support\Facades\Storage;
+use App\Services\Template\TemplateService;
class GenerateDeliveryNote
{
@@ -65,62 +61,11 @@ class GenerateDeliveryNote
$invitation = $this->invoice->invitations->first();
- // return (new \App\Services\Pdf\PdfService($invitation, 'delivery_note'))->boot()->getPdf();
-
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom())->generate($this->invoice->invitations->first());
- }
-
$design = Design::withTrashed()->find($design_id);
- $html = new HtmlEngine($invitation);
- if ($design->is_custom) {
- $options = ['custom_partials' => json_decode(json_encode($design->design), true)];
- $template = new PdfMakerDesign(PdfMakerDesign::CUSTOM, $options);
- } else {
- $template = new PdfMakerDesign(strtolower($design->name));
- }
+ $ps = new \App\Services\Pdf\PdfService($invitation, 'delivery_note');
- $variables = $html->generateLabelsAndValues();
- $variables['labels']['$entity_label'] = ctrans('texts.delivery_note');
- $variables['labels']['$invoice.date_label'] = ctrans('texts.date');
- $variables['labels']['$invoice.number_label'] = ctrans('texts.number');
-
- $state = [
- 'template' => $template->elements([
- 'client' => $this->invoice->client,
- 'entity' => $this->invoice,
- 'pdf_variables' => (array) $this->invoice->company->settings->pdf_variables,
- 'contact' => $this->contact,
- ], 'delivery_note'),
- 'variables' => $variables,
- 'options' => [
- 'client' => $this->invoice->client,
- 'entity' => $this->invoice,
- 'contact' => $this->contact,
- ],
- 'process_markdown' => $this->invoice->client->company->markdown_enabled,
- ];
-
- $maker = new PdfMakerService($state);
- $maker
- ->design($template)
- ->build();
-
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
- } else {
- $pdf = $this->makePdf(null, null, $maker->getCompiledHTML());
- }
-
- if (config('ninja.log_pdf_html')) {
- info($maker->getCompiledHTML());
- }
-
- $maker = null;
- $state = null;
-
- return $pdf;
+ return $ps->boot()->getPdf();
}
}
diff --git a/app/Services/Pdf/DesignExtractor.php b/app/Services/Pdf/DesignExtractor.php
new file mode 100644
index 0000000000..56fb713594
--- /dev/null
+++ b/app/Services/Pdf/DesignExtractor.php
@@ -0,0 +1,86 @@
+design = $design : $this->design = "{$design}.html";
+ }
+
+ $this->options = $options;
+ }
+
+ public function setHtml(string $html): self
+ {
+ $this->html = $html;
+
+ return $this;
+ }
+
+ private function getHtml(): string
+ {
+ nlog($this->design);
+ if($this->html) {
+ return $this->html;
+ }
+
+ $this->html = file_get_contents( config('ninja.designs.base_path') . $this->design );
+
+ return $this->html;
+ }
+
+ public function getSectionHTML(string $section, $id = true): ?string
+ {
+
+ $document = new \DOMDocument();
+
+ $document->validateOnParse = true;
+ @$document->loadHTML($this->getHtml());
+
+ if ($id) {
+ $element = $document->getElementById($section);
+ } else {
+ $elements = $document->getElementsByTagName($section);
+ $element = $elements[0];
+ }
+
+ if ($element) {
+
+ $_document = new \DOMDocument();
+ $_document->preserveWhiteSpace = false;
+ $_document->formatOutput = true;
+
+ $_document->appendChild(
+ $_document->importNode($element, true)
+ );
+
+ $html = $_document->saveHTML();
+
+ return str_replace('%24', '$', $html);
+ }
+
+ return '';
+
+ }
+}
diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php
index 48e37b1a08..5f06144efc 100644
--- a/app/Services/Pdf/PdfBuilder.php
+++ b/app/Services/Pdf/PdfBuilder.php
@@ -80,7 +80,15 @@ class PdfBuilder
return $this;
}
-
+
+ /**
+ * removeEmptyElements
+ *
+ * Removes any empty elements from the DomDocument, this improves the vertical spacing of the PDF
+ * This also decodes any encoded HTML elements.
+ *
+ * @return self
+ */
private function removeEmptyElements(): self
{
@@ -101,7 +109,7 @@ class PdfBuilder
}
-
+ // Decode any HTML based elements.
$xpath = new \DOMXPath($this->document);
$elements = $xpath->query('//*[@data-state="encoded-html"]');
@@ -117,35 +125,29 @@ class PdfBuilder
// Add UTF-8 wrapper and div container
$wrappedHtml = '
' . $html . '
';
- // Load the HTML, suppressing any parsing warnings
@$temp->loadHTML($wrappedHtml, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
-
- // Import the div's contents
$imported = $this->document->importNode($temp->getElementsByTagName('div')->item(0), true);
- // Clear existing content - more efficient
$element->textContent = '';
- // Get the first div's content
$divContent = $temp->getElementsByTagName('div')->item(0);
if ($divContent) {
- // Import all nodes from the temporary div
+
foreach ($divContent->childNodes as $child) {
$imported = $this->document->importNode($child, true);
$element->appendChild($imported);
}
} else {
- // Fallback - import the entire content if no div found
+
$imported = $this->document->importNode($temp->documentElement, true);
$element->appendChild($imported);
}
+ unset($temp); //releases memory immediately rather than at the end of the function
}
-
-
return $this;
}
@@ -182,8 +184,15 @@ class PdfBuilder
return $this;
}
-
- private function parseTwigElements()
+
+ /**
+ * parseTwigElements
+ *
+ * Parses any ninja tags in the template and processes them via TWIG.
+ *
+ * @return self
+ */
+ private function parseTwigElements(): self
{
$replacements = [];
@@ -217,7 +226,13 @@ class PdfBuilder
return $this;
}
-
+
+ /**
+ * setDocument
+ *
+ * @param mixed $document
+ * @return self
+ */
public function setDocument($document): self
{
$this->document = $document;
@@ -241,14 +256,30 @@ class PdfBuilder
return $this;
}
-
+
+ /**
+ * mergeSections
+ *
+ * Merges the sections into the sections array.
+ *
+ * @param array $section
+ * @return self
+ */
private function mergeSections(array $section): self
{
$this->sections = array_merge($this->sections, $section);
return $this;
}
-
+
+ /**
+ * setSections
+ *
+ * Sets the sections array.
+ *
+ * @param mixed $sections
+ * @return self
+ */
public function setSections($sections): self
{
$this->sections = $sections;
@@ -296,6 +327,23 @@ class PdfBuilder
$this->genericSectionBuilder();
$this->mergeSections([
+ 'client-details' => [
+ 'id' => 'client-details',
+ 'elements' => $this->clientDetails(),
+ ],
+ 'vendor-details' => [ //this block pads the grid for client / vendor / entity details
+ 'id' => 'vendor-details',
+ 'elements' => [
+ ['element' => 'tr', 'properties' => ['data-ref' => 'statement-labelx'], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => ""],
+ ['element' => 'th', 'properties' => [], 'content' => ''],
+ ]],
+ ],
+ ],
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->statementDetails(),
+ ],
'statement-invoice-table' => [
'id' => 'statement-invoice-table',
'elements' => $this->statementInvoiceTable(),
@@ -320,8 +368,8 @@ class PdfBuilder
'id' => 'statement-payment-table-totals',
'elements' => $this->statementPaymentTableTotals(),
],
- 'statement-credits-table' => [
- 'id' => 'statement-credits-table',
+ 'statement-credit-table' => [
+ 'id' => 'statement-credit-table',
'elements' => $this->statementCreditTable(),
],
'statement-credit-table-totals' => [
@@ -357,49 +405,49 @@ class PdfBuilder
}
/**
- * Parent method for building payments table within statement.
+ * Parent method for building credits table within for statements.
*
* @return array
*/
public function statementCreditTable(): array
{
- if (is_null($this->service->options['credits'])) {
- return [];
- }
-
- if (\array_key_exists('show_credits_table', $this->service->options) && $this->service->options['show_credits_table'] === false) {
+ if (is_null($this->service->options['credits']) || (\array_key_exists('show_credits_table', $this->service->options) && $this->service->options['show_credits_table'] === false)) {
return [];
}
$tbody = [];
foreach ($this->service->options['credits'] as $credit) {
+
$element = ['element' => 'tr', 'elements' => []];
- $element['elements'][] = ['element' => 'td', 'content' => $credit->number];
- $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($credit->date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($credit->amount) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($credit->balance) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $credit->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($credit->date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($credit->amount) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($credit->balance) ?: ' '];
$tbody[] = $element;
}
return [
- ['element' => 'thead', 'elements' => $this->buildTableHeader('statement_invoice')],
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('statement_credit')],
['element' => 'tbody', 'elements' => $tbody],
];
}
/**
- * Parent method for building invoice table totals
- * for statements.
- *
+ * Parent method for building credits table totals for statements.
+ *
* @return array
*/
public function statementCreditTableTotals(): array
{
$outstanding = $this->service->options['credits']->sum('balance');
+
+ if (\array_key_exists('show_credits_table', $this->service->options) && $this->service->options['show_credits_table'] === false) {
+ return [];
+ }
return [
['element' => 'div', 'content' => '$credit.balance_label: ' . $this->service->config->formatMoney($outstanding)],
@@ -414,17 +462,12 @@ class PdfBuilder
*/
public function statementPaymentTable(): array
{
- if (is_null($this->service->options['payments'])) {
- return [];
- }
-
- if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ if (is_null($this->service->options['payments']) || (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false)) {
return [];
}
$tbody = [];
- //24-03-2022 show payments per invoice
foreach ($this->service->options['invoices'] as $invoice) {
foreach ($invoice->payments as $payment) {
if ($payment->is_deleted) {
@@ -462,10 +505,10 @@ class PdfBuilder
}
$element = ['element' => 'tr', 'elements' => []];
- $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
- $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($refund_date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => ctrans('texts.refund')];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($payment->pivot->refunded) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($refund_date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => ctrans('texts.refund')];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($payment->pivot->refunded) ?: ' '];
$tbody[] = $element;
@@ -482,18 +525,14 @@ class PdfBuilder
}
/**
- * Generates the statement payments table
+ * Generates the payments table totals for statements.
*
* @return array
*
*/
public function statementPaymentTableTotals(): array
{
- if (is_null($this->service->options['payments']) || !$this->service->options['payments']->first()) {
- return [];
- }
-
- if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ if (is_null($this->service->options['payments']) || !$this->service->options['payments']->first() || (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false)) {
return [];
}
@@ -501,52 +540,46 @@ class PdfBuilder
return [
['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), $this->service->config->formatMoney($this->payment_amount_total))],
- ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
- ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
];
}
-
+
+ /**
+ * Generates the unapplied payments table totals for statements.
+ *
+ * @return array
+ */
public function statementUnappliedPaymentTableTotals(): array
{
- if (is_null($this->service->options['unapplied']) || !$this->service->options['unapplied']->first()) {
- return [];
- }
-
- if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ if (is_null($this->service->options['unapplied']) || !$this->service->options['unapplied']->first() || (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false)) {
return [];
}
$payment = $this->service->options['unapplied']->first();
return [
- ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance'), $this->service->config->formatMoney($this->unapplied_total))],
- ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
- ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
+ ['element' => 'div', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance_on_file'), $this->service->config->formatMoney($this->unapplied_total))],
];
}
/**
- * Generates the statement unapplied payments table
+ * Generates the unapplied payments table for statements.
*
* @return array
*
*/
public function statementUnappliedPaymentTable(): array
{
- if (is_null($this->service->options['unapplied']) || !$this->service->options['unapplied']->first()) {
- return [];
- }
-
- if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ if (is_null($this->service->options['unapplied']) || !$this->service->options['unapplied']->first() || (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false)) {
return [];
}
$tbody = [];
+
+ $this->unapplied_total = 0;
- //24-03-2022 show payments per invoice
foreach ($this->service->options['unapplied'] as $unapplied_payment) {
if ($unapplied_payment->is_deleted) {
continue;
@@ -554,10 +587,10 @@ class PdfBuilder
$element = ['element' => 'tr', 'elements' => []];
- $element['elements'][] = ['element' => 'td', 'content' => $unapplied_payment->number];
- $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($unapplied_payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($unapplied_payment->amount) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($unapplied_payment->amount - $unapplied_payment->applied) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $unapplied_payment->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($unapplied_payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($unapplied_payment->amount) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($unapplied_payment->amount - $unapplied_payment->applied) ?: ' '];
$tbody[] = $element;
@@ -572,7 +605,7 @@ class PdfBuilder
}
/**
- * Generates the statement aging table
+ * Generates the aging table for statements.
*
* @return array
*
@@ -649,16 +682,6 @@ class PdfBuilder
],
]);
- // if($this->service->config->entity->client)
- // {
- // $this->service->config->client = $this->service->config->entity->client;
- // nlog("inside");
- // $this->getClientDetails();
- // $this->service->config->client = null;
- // }
-
- // nlog($this->sections);
-
return $this;
}
@@ -682,9 +705,7 @@ class PdfBuilder
],
'footer-elements' => [
'id' => 'footer',
- 'elements' => [
- // $this->sharedFooterElements(),
- ],
+ 'elements' => [],
],
]);
@@ -701,14 +722,16 @@ class PdfBuilder
{
$tbody = [];
+ $date_format = $this->service->config->client->date_format();
+
foreach ($this->service->options['invoices'] as $invoice) {
$element = ['element' => 'tr', 'elements' => []];
- $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
- $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->due_date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->amount) ?: ' '];
- $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->balance) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->date, $date_format, $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->due_date, $date_format, $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->amount) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->balance) ?: ' '];
$tbody[] = $element;
}
@@ -718,6 +741,57 @@ class PdfBuilder
['element' => 'tbody', 'elements' => $tbody],
];
}
+
+ /**
+ * Filters the visible elements for a table row and also
+ * assigned the left and right radius classes to the first and last cells
+ *
+ * @param array $element
+ * @return array
+ */
+ private function parseVisibleElements(array $element): array
+ {
+
+ $visible_elements = array_filter($element['elements'], function ($el) {
+ if (isset($el['properties']['visi']) && $el['properties']['visi']) {
+ return true;
+ }
+ return false;
+ });
+
+ if (!empty($visible_elements)) {
+ $first_visible = array_key_first($visible_elements);
+ $last_visible = array_key_last($visible_elements);
+
+ // Add class to first visible cell
+ if (!isset($element['elements'][$first_visible]['properties']['class'])) { //@phpstan-ignore-line
+ $element['elements'][$first_visible]['properties']['class'] = 'left-radius';
+ } else {
+ $element['elements'][$first_visible]['properties']['class'] .= ' left-radius';
+ }
+
+ // Add class to last visible cell
+ if (!isset($element['elements'][$last_visible]['properties']['class'])) {
+ $element['elements'][$last_visible]['properties']['class'] = 'right-radius';
+ } else {
+ $element['elements'][$last_visible]['properties']['class'] .= ' right-radius';
+ }
+ }
+
+ // Then, filter the elements array
+ $element['elements'] = array_map(function ($el) {
+ if (isset($el['properties']['visi'])) {
+ if ($el['properties']['visi'] === false) {
+ $el['properties']['style'] = 'display: none;';
+ }
+ unset($el['properties']['visi']);
+ }
+ return $el;
+ }, $element['elements']);
+
+ return $element;
+
+ }
/**
@@ -740,6 +814,10 @@ class PdfBuilder
return [];
}
+ $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
+
+ $column_visibility = $this->getColumnVisibility($this->service->config->entity->line_items, $_type);
+
if ($type == PdfService::DELIVERY_NOTE) {
$product_customs = [false, false, false, false];
@@ -754,41 +832,36 @@ class PdfBuilder
foreach ($items as $row) {
$element = ['element' => 'tr', 'elements' => []];
- $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.product_key'], 'properties' => ['data-ref' => 'delivery_note_table.product_key-td']];
- $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td']];
- $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td']];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.product_key'], 'properties' => ['data-ref' => 'delivery_note_table.product_key-td','visi' => $this->visibilityCheck($column_visibility, 'product_key')]];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td','visi' => $this->visibilityCheck($column_visibility, 'notes')]];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td','visi' => $this->visibilityCheck($column_visibility, 'quantity')]];
for ($i = 0; $i < count($product_customs); $i++) {
if ($product_customs[$i]) {
- $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.delivery_note' . ($i + 1)], 'properties' => ['data-ref' => 'delivery_note_table.product' . ($i + 1) . '-td']];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.delivery_note' . ($i + 1)], 'properties' => ['data-ref' => 'delivery_note_table.product' . ($i + 1) . '-td','visi' => $this->visibilityCheck($column_visibility, 'product' . ($i + 1))]];
}
}
+ $element = $this->parseVisibleElements($element);
+
$elements[] = $element;
}
return $elements;
}
- $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
$table_type = "{$_type}_columns";
+ //Handle custom quote columns
if ($_type == 'product' && $this->service->config->entity instanceof Quote && !$this->service->config->settings?->sync_invoice_quote_columns) {
$table_type = "product_quote_columns";
}
- $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
-
- $column_visibility = $this->getColumnVisibility($this->service->config->entity->line_items, $_type);
-
foreach ($items as $row) {
$element = ['element' => 'tr', 'elements' => []];
-
- if (
- array_key_exists($type, $this->service->options) &&
- !empty($this->service->options[$type]) &&
- !is_null($this->service->options[$type])
- ) {
+ //checks if we have custom columns in the options array with key $product/$task - looks like unused functionality
+ if (isset($this->service->options[$type]) && !empty($this->service->options[$type])) {
+
$document = new DOMDocument();
$document->loadHTML($this->service->options[$type], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
@@ -835,50 +908,14 @@ class PdfBuilder
} elseif ($cell == '$task.tax_rate3') {
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax3-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
} elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
- $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'style' => $this->visibilityCheck($column_visibility, $cell)]];
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
} else {
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
}
}
}
- $visible_elements = array_filter($element['elements'], function ($el) {
- if (isset($el['properties']['visi']) && $el['properties']['visi']) {
- return true;
- }
- return false;
- });
-
- if (!empty($visible_elements)) {
- $first_visible = array_key_first($visible_elements);
- $last_visible = array_key_last($visible_elements);
-
- // Add class to first visible cell
- if (!isset($element['elements'][$first_visible]['properties']['class'])) { //@phpstan-ignore-line
- $element['elements'][$first_visible]['properties']['class'] = 'left-radius';
- } else {
- $element['elements'][$first_visible]['properties']['class'] .= ' left-radius';
- }
-
- // Add class to last visible cell
- if (!isset($element['elements'][$last_visible]['properties']['class'])) {
- $element['elements'][$last_visible]['properties']['class'] = 'right-radius';
- } else {
- $element['elements'][$last_visible]['properties']['class'] .= ' right-radius';
- }
- }
-
- // Then, filter the elements array
- $element['elements'] = array_map(function ($el) {
- if (isset($el['properties']['visi'])) {
- if ($el['properties']['visi'] === false) {
- $el['properties']['style'] = 'display: none;';
- }
- unset($el['properties']['visi']);
- }
- return $el;
- }, $element['elements']);
-
+ $element = $this->parseVisibleElements($element);
$elements[] = $element;
}
@@ -912,17 +949,16 @@ class PdfBuilder
}
if ($table_type == '$task' && $item->type_id != 2) {
- // if ($item->type_id != 4 && $item->type_id != 5) {
continue;
- // }
}
$helpers = new Helpers();
$_table_type = ltrim($table_type, '$'); // From $product -> product.
- $data[$key][$table_type.'.product_key'] = is_null(optional($item)->product_key) ? $item->item : $item->product_key;
- $data[$key][$table_type.'.item'] = is_null(optional($item)->item) ? $item->product_key : $item->item;
- $data[$key][$table_type.'.service'] = is_null(optional($item)->service) ? $item->product_key : $item->service;
+ //2025-01-28 not sure how we ever got ->item and ->service....
+ $data[$key][$table_type.'.product_key'] = $item->product_key ?? $item->item;
+ $data[$key][$table_type.'.item'] = $item->item ?? $item->product_key;
+ $data[$key][$table_type.'.service'] = $item->service ?? $item->product_key;
$currentDateTime = null;
if (isset($this->service->config->entity->next_send_date)) {
@@ -930,7 +966,7 @@ class PdfBuilder
}
$data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $this->service->config->currency_entity, $currentDateTime);
- $data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $this->service->config->currency_entity, $currentDateTime);
+ $data[$key][$table_type.'.description'] = &$data[$key][$table_type.'.notes'];
$data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}1", $item->custom_value1, $this->service->config->currency_entity) : '';
$data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}2", $item->custom_value2, $this->service->config->currency_entity) : '';
@@ -977,9 +1013,6 @@ class PdfBuilder
$data[$key][$table_type.'.discount'] = '';
}
- // Previously we used to check for tax_rate value,
- // but that's no longer necessary.
-
if (isset($item->tax_rate1)) {
$data[$key][$table_type.'.tax_rate1'] = $this->service->config->formatValueNoTrailingZeroes(floatval($item->tax_rate1)).'%';
$data[$key][$table_type.'.tax1'] = &$data[$key][$table_type.'.tax_rate1'];
@@ -998,12 +1031,18 @@ class PdfBuilder
$data[$key]['task_id'] = property_exists($item, 'task_id') ? $item->task_id : '';
}
- //nlog(microtime(true) - $start);
-
return $data;
}
-
+
+ /**
+ * Filters the visible columns for a table row.
+ *
+ * @param array $items
+ * @param string $type_id
+ *
+ * @return array
+ */
private function getColumnVisibility(array $items, string $type_id): array
{
@@ -1043,7 +1082,6 @@ class PdfBuilder
return $columns;
-
}
/**
@@ -1138,7 +1176,14 @@ class PdfBuilder
return $elements;
}
-
+
+ /**
+ * visibilityCheck
+ *
+ * @param array $column_visibility
+ * @param string $column
+ * @return bool
+ */
private function visibilityCheck(array $column_visibility, string $column): bool
{
if(!$this->service->config->settings->hide_empty_columns_on_pdf){
@@ -1218,36 +1263,6 @@ class PdfBuilder
}
}
- /**
- * Generates the javascript block for
- * hiding elements which need to be hidden
- *
- * @return array
- *
- */
- public function sharedFooterElements(): array
- {
- // We want to show headers for statements, no exceptions.
- $statements = "
- document.querySelectorAll('#statement-invoice-table > thead > tr > th, #statement-payment-table > thead > tr > th, #statement-aging-table > thead > tr > th').forEach(t => {
- t.hidden = false;
- });
- ";
-
- $javascript = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{if(""!==t.innerText){let e=t.getAttribute("data-ref").slice(0,-3);document.querySelector(`th[data-ref="${e}-th"]`).removeAttribute("hidden")}}),document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{let e=t.getAttribute("data-ref").slice(0,-3);(e=document.querySelector(`th[data-ref="${e}-th"]`)).hasAttribute("hidden")&&""==t.innerText&&t.setAttribute("hidden","true")})},!1);';
-
- // Previously we've been decoding the HTML on the backend and XML parsing isn't good options because it requires,
- // strict & valid HTML to even output/decode. Decoding is now done on the frontend with this piece of Javascript.
-
- $html_decode = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(`[data-state="encoded-html"]`).forEach(e=>e.innerHTML=e.innerText)},!1);';
-
- return ['element' => 'div', 'elements' => [
- ['element' => 'script', 'content' => $statements],
- ['element' => 'script', 'content' => $javascript],
- ['element' => 'script', 'content' => $html_decode],
- ]];
- }
-
/**
* Generates the totals table for
* the product type entities
@@ -1373,14 +1388,10 @@ class PdfBuilder
$_variable = $aliases[$variable];
}
- if (is_null($this->service->config->entity->{$_variable})) {
+ if (is_null($this->service->config->entity->{$_variable}) || empty($this->service->config->entity->{$_variable})) {
return true;
}
-
- if (empty($this->service->config->entity->{$_variable})) {
- return true;
- }
-
+
return false;
}
@@ -1402,11 +1413,11 @@ class PdfBuilder
$elements = [
['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
['element' => 'div', 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;'], 'elements' => [
- ['element' => 'span', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables)]
+ ['element' => 'div', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables)]
]],
['element' => 'div', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
- ['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight:bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
- ['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
+ ['element' => 'div', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight:bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
+ ['element' => 'div', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
]],
['element' => 'img', 'properties' => ['style' => 'max-width: 50%; height: auto;', 'src' => '$contact.signature', 'id' => 'contact-signature']],
['element' => 'div', 'properties' => ['style' => 'display: flex; align-items: flex-start; page-break-inside: auto;'], 'elements' => [
@@ -1465,8 +1476,8 @@ class PdfBuilder
foreach ($taxes as $i => $tax) {
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
- ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i . '-label']],
- ['element' => 'span', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i]],
+ ['element' => 'p', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i . '-label']],
+ ['element' => 'p', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i]],
]];
}
} elseif ($variable == '$line_taxes') {
@@ -1478,8 +1489,8 @@ class PdfBuilder
foreach ($taxes as $i => $tax) {
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
- ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i . '-label']],
- ['element' => 'span', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]],
+ ['element' => 'p', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i . '-label']],
+ ['element' => 'p', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]],
]];
}
} elseif (Str::startsWith($variable, '$custom_surcharge')) {
@@ -1488,28 +1499,28 @@ class PdfBuilder
$visible = intval(str_replace(['0','.'], '', ($this->service->config->entity->{$_variable} ?? ''))) != 0;
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
- ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
- ['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ['element' => 'p', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'p', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
]];
} elseif (Str::startsWith($variable, '$custom')) {
$field = explode('_', $variable);
$visible = is_object($this->service->company->custom_fields) && property_exists($this->service->company->custom_fields, $field[1]) && !empty($this->service->company->custom_fields->{$field[1]});
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
- ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
- ['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ['element' => 'p', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'p', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
]];
} else {
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
- ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
- ['element' => 'span', 'content' => $variable, 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1)]],
- ]];
+ ['element' => 'p', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'p', 'content' => $variable, 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ], 'properties' => ['class' => 'totals_table-' . substr($variable, 1)]];
}
}
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
- ['element' => 'span', 'content' => '',],
- ['element' => 'span', 'content' => ''],
+ ['element' => 'p', 'content' => '',],
+ ['element' => 'p', 'content' => ''],
]];
@@ -1590,6 +1601,10 @@ class PdfBuilder
*/
public function taskTable(): array
{
+
+ if($this->service->config->entity instanceof \App\Models\PurchaseOrder)
+ return [];
+
$task_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
return $item->type_id == 2;
});
@@ -1699,11 +1714,6 @@ class PdfBuilder
{
$variables = $this->service->config->pdf_variables['invoice_details'];
- // $_v = $this->service->html_variables;
-
- // $_v['labels']['$invoice.date_label'] = ctrans('text.date');
- // $this->service->html_variables = $_v;
-
$variables = array_filter($variables, function ($m) {
return !in_array($m, ['$invoice.balance_due', '$invoice.total']);
});
@@ -1746,9 +1756,11 @@ class PdfBuilder
/**
- * Generates the client delivery
- * details array
- *
+ * Generates the client delivery details array
+ *
+ * We also override some variables here to ensure they are
+ * appropriate for the delivery note.
+ *
* @return array
*
*/
@@ -1760,19 +1772,31 @@ class PdfBuilder
return $elements;
}
+ $this->service->html_variables['values']['$show_paid_stamp'] = 'none';
+ $this->service->html_variables['values']['$show_shipping_address_block'] = 'none';
+ $this->service->html_variables['values']['$show_shipping_address'] = 'none';
+ $this->service->html_variables['values']['$show_shipping_address_visibility'] = 'hidden';
+ $this->service->html_variables['labels']['$entity_issued_to_label'] = '';
+ $this->service->html_variables['labels']['$entity_number_label'] = ctrans('texts.delivery_note');
+ $this->service->html_variables['values']['$entity'] = ctrans('texts.delivery_note');
+ $this->service->html_variables['labels']['$entity_label'] = ctrans('texts.delivery_note');
+ $this->service->html_variables['labels']['$invoice.number_label'] = ctrans('texts.delivery_note');
+ $this->service->html_variables['labels']['$payment_due_label'] = '';
+ $this->service->html_variables['values']['$payment_due'] = '';
+ $this->service->html_variables['labels']['$amount_due_label'] = '';
+ $this->service->html_variables['values']['$balance_due'] = '';
+ $this->service->html_variables['values']['$amount_due'] = '';
+ $this->service->html_variables['labels']['$amount_due_label'] = '';
+
$elements = [
- ['element' => 'div', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
['element' => 'div', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
['element' => 'div', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
['element' => 'div', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
- ['element' => 'div', 'show_empty' => false, 'elements' => [
- ['element' => 'span', 'content' => "{$this->service->config->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
- ['element' => 'span', 'content' => "{$this->service->config->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
- ['element' => 'span', 'content' => "{$this->service->config->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
- ]],
+ ['element' => 'div', 'content' => "{$this->service->config->client->shipping_city} {$this->service->config->client->shipping_state} {$this->service->config->client->shipping_postal_code}", 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.city_state_postal']],
['element' => 'div', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
];
+
if (!is_null($this->service->config->contact)) {
$elements[] = ['element' => 'div', 'content' => $this->service->config->contact->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
}
@@ -1801,26 +1825,26 @@ class PdfBuilder
return $elements;
}
-
+
+ /**
+ * Generates the shipping details section
+ *
+ * @return array
+ */
public function shippingDetails(): array
{
$elements = [];
- if (!$this->service->config->client) {
+ if (!$this->service->config->client || $this->service->document_type == PdfService::DELIVERY_NOTE) {
return $elements;
}
$elements = [
['element' => 'div', 'content' => ctrans('texts.shipping_address'), 'properties' => ['data-ref' => 'shipping_address-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
- // ['element' => 'div', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.name']],
['element' => 'div', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address1']],
['element' => 'div', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address2']],
- ['element' => 'div', 'show_empty' => false, 'elements' => [
- ['element' => 'span', 'content' => "{$this->service->config->client->shipping_city} ", 'properties' => ['ref' => 'shipping_address-client.shipping_city']],
- ['element' => 'span', 'content' => "{$this->service->config->client->shipping_state} ", 'properties' => ['ref' => 'shipping_address-client.shipping_state']],
- ['element' => 'span', 'content' => "{$this->service->config->client->shipping_postal_code} ", 'properties' => ['ref' => 'shipping_address-client.shipping_postal_code']],
- ]],
- ['element' => 'div', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
+ ['element' => 'div', 'content' => "{$this->service->config->client->shipping_city} {$this->service->config->client->shipping_state} {$this->service->config->client->shipping_postal_code}", 'properties' => ['data-ref' => 'shipping_address-client.city_state_postal']],
+ ['element' => 'div', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_country']],
];
return $elements;
@@ -1834,7 +1858,7 @@ class PdfBuilder
*/
public function deliveryNoteTable(): array
{
- /* Static array of delivery note columns*/
+
$thead = [
['element' => 'th', 'content' => '$item_label', 'properties' => ['data-ref' => 'delivery_note-item_label']],
['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_label']],
@@ -1861,6 +1885,23 @@ class PdfBuilder
}
}
+ $first_visible = array_key_first($thead);
+ $last_visible = array_key_last($thead);
+
+ // Add class to first visible cell
+ if (!isset($thead[$first_visible]['properties']['class'])) { //@phpstan-ignore-line
+ $thead[$first_visible]['properties']['class'] = 'left-radius';
+ } else {
+ $thead[$first_visible]['properties']['class'] .= ' left-radius';
+ }
+
+ // Add class to last visible cell
+ if (!isset($thead[$last_visible]['properties']['class'])) {
+ $thead[$last_visible]['properties']['class'] = 'right-radius';
+ } else {
+ $thead[$last_visible]['properties']['class'] .= ' right-radius';
+ }
+
return [
['element' => 'thead', 'elements' => $thead],
['element' => 'tbody', 'elements' => $this->buildTableBody(PdfService::DELIVERY_NOTE)],
@@ -1899,7 +1940,7 @@ class PdfBuilder
$elements = [];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
}
return $elements;
@@ -1993,7 +2034,15 @@ class PdfBuilder
return $element;
}
-
+
+ /**
+ * isMarkdown
+ *
+ * Checks if the given content is most likely markdown
+ *
+ * @param string $content
+ * @return bool
+ */
private function isMarkdown(string $content): bool
{
$content = str_ireplace('
', "\n", $content);
@@ -2006,7 +2055,8 @@ class PdfBuilder
'/\*\*.*?\*\*/', // Bold
'/\*.*?\*/', // Italic
'/__.*?__/', // Bold
- '/_.*?_/', // Italic
+ // '/_.*?_/', // Italic
+ '/(?/m', // Blockquotes
'/^\s*```/m', // Code blocks
@@ -2025,30 +2075,25 @@ class PdfBuilder
public function createElementContent($element, $children): self
{
- foreach ($children as $child) {
-
+ foreach ($children as $child) {
+ if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ continue;
+ }
+
$contains_html = false;
$child['content'] = $child['content'] ?? '';
- if ($this->service->company->markdown_enabled && $this->isMarkdown($child['content']) && $child['element'] !== 'script') {
+ if ($this->service->company->markdown_enabled && $this->isMarkdown($child['content'])) {
$child['content'] = str_ireplace('
', "\r", $child['content']);
$child['content'] = $this->commonmark->convert($child['content']); //@phpstan-ignore-line
}
- if (isset($child['is_empty']) && $child['is_empty'] === true) {
- continue;
- }
-
$contains_html = str_contains($child['content'], '<') && str_contains($child['content'], '>');
-
if ($contains_html) {
-
- // If the element contains the HTML, we gonna display it as is. Backend is going to
- // encode it for us, preventing any errors on the processing stage.
- // Later, we decode this using Javascript so it looks like it's normal HTML being injected.
- // To get all elements that need frontend decoding, we use 'data-state' property.
+ // Encode any HTML elements now so that DOMDocument doesn't throw any errors,
+ // Later we can decode specific elements.
$_child = $this->document->createElement($child['element'], '');
$_child->setAttribute('data-state', 'encoded-html');
@@ -2056,9 +2101,6 @@ class PdfBuilder
} else {
- // .. in case string doesn't contain any HTML, we'll just return
- // raw $content
-
$_child = $this->document->createElement($child['element'], htmlspecialchars($child['content']));
}
@@ -2077,14 +2119,18 @@ class PdfBuilder
return $this;
}
-
+
+ /**
+ * updateVariables
+ *
+ * @return void
+ */
public function updateVariables()
{
$html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']);
$html = strtr($html, $this->service->html_variables['values']);
- //old block
@$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
//new block
@@ -2098,49 +2144,60 @@ class PdfBuilder
return $this;
}
- public function updateVariable(string $element, string $variable, string $value)
+ // public function updateVariable(string $element, string $variable, string $value)
+ // {
+ // $element = $this->document->getElementById($element);
+
+ // $original = $element->nodeValue;
+
+ // $element->nodeValue = '';
+
+ // $replaced = strtr($original, [$variable => $value]);
+
+ // $element->appendChild(
+ // $this->document->createTextNode($replaced)
+ // );
+
+ // return $element;
+ // }
+
+ public function getEmptyElements(): self
{
- $element = $this->document->getElementById($element);
+ foreach ($this->sections as $key => $element) {
+ if (isset($element['elements'])) {
+ $this->sections[$key] = $this->getEmptyChildren($element);
+ }
+ }
- $original = $element->nodeValue;
+ return $this;
+ }
- $element->nodeValue = '';
+ public function getEmptyChildren(array $element): array
+ {
+ foreach ($element['elements'] as $key => &$child) {
+ if ($this->isChildEmpty($child)) {
+ $child['is_empty'] = true;
+ }
- $replaced = strtr($original, [$variable => $value]);
-
- $element->appendChild(
- $this->document->createTextNode($replaced)
- );
+ if (isset($child['elements'])) {
+ $child = $this->getEmptyChildren($child);
+ }
+ }
return $element;
}
- public function getEmptyElements(): self
+ private function isChildEmpty(array $child): bool
{
- foreach ($this->sections as $element) {
- if (isset($element['elements'])) {
- $this->getEmptyChildrens($element['elements'], $this->service->html_variables);
- }
+ if (!isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
+ return true;
}
- return $this;
- }
-
- public function getEmptyChildrens(array $children)
- {
- foreach ($children as $key => $child) {
- if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
- $value = strtr($child['content'], $this->service->html_variables['values']);
- if ($value === '' || $value === ' ' || $value === ' ') {
- $child['is_empty'] = true;
- }
- }
-
- if (isset($child['elements'])) {
- $this->getEmptyChildrens($child['elements']);
- }
+ if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
+ $value = strtr($child['content'], $this->service->html_variables['values']);
+ return empty($value) || $value === ' ' || $value === ' ';
}
- return $this;
+ return false;
}
}
diff --git a/app/Services/Pdf/PdfConfiguration.php b/app/Services/Pdf/PdfConfiguration.php
index 0dae12a97c..8ea7d84771 100644
--- a/app/Services/Pdf/PdfConfiguration.php
+++ b/app/Services/Pdf/PdfConfiguration.php
@@ -400,18 +400,6 @@ class PdfConfiguration
$precision = strlen($parts[1]);
}
- /* 08-02-2023 special if block to render $0.5 to $0.50*/
- // if ($v < 1 && strlen($v) == 3) {
- // $precision = 2;
- // } elseif ($v < 1) {
- // $precision = strlen($v) - strrpos($v, '.') - 1;
- // } elseif ($v > 1) {
- // $precision = strlen($v) - strrpos($v, '.') - 1;
- // }
- // if (is_array($parts) && $parts[0] != 0) {
- // $precision = 2;
- // }
-
//04-04-2023 if currency = JPY override precision to 0
if ($this->currency->code == 'JPY') {
$precision = 0;
diff --git a/app/Services/Pdf/PdfMock.php b/app/Services/Pdf/PdfMock.php
index 05cc7f0021..fd8437b769 100644
--- a/app/Services/Pdf/PdfMock.php
+++ b/app/Services/Pdf/PdfMock.php
@@ -42,18 +42,20 @@ class PdfMock
private string $entity_string = 'invoice';
+ private PdfService $pdf_service;
+
public function __construct(public array $request, public Company $company)
{
}
- public function getPdf(): mixed
+ public function setPdfService(): self
{
//need to resolve the pdf type here, ie product / purchase order
$document_type = $this->request['entity_type'] == 'purchase_order' ? 'purchase_order' : 'product';
- $pdf_service = new PdfService($this->mock->invitation, $document_type);
+ $this->pdf_service = new PdfService($this->mock->invitation, $document_type);
- $pdf_config = (new PdfConfiguration($pdf_service));
+ $pdf_config = (new PdfConfiguration($this->pdf_service));
$pdf_config->entity = $this->mock;
$pdf_config->entity_string = $this->request['entity_type'];
$this->entity_string = $this->request['entity_type'];
@@ -76,29 +78,44 @@ class PdfMock
$pdf_config->design = Design::withTrashed()->find($this->decodePrimaryKey($pdf_config->entity_design_id));
}
- $pdf_service->config = $pdf_config;
+ $this->pdf_service->config = $pdf_config;
- if (isset($this->request['design'])) {
- $pdf_designer = (new PdfDesigner($pdf_service))->buildFromPartials($this->request['design']);
+ if (isset($this->request['design']) && is_array($this->request['design'])) {
+ $pdf_designer = (new PdfDesigner($this->pdf_service))->buildFromPartials($this->request['design']);
} else {
- $pdf_designer = (new PdfDesigner($pdf_service))->build();
+ $pdf_designer = (new PdfDesigner($this->pdf_service))->build();
}
- $pdf_service->designer = $pdf_designer;
+ $this->pdf_service->designer = $pdf_designer;
- $pdf_service->html_variables = $document_type == 'purchase_order' ? $this->getVendorStubVariables() : $this->getStubVariables();
+ $this->pdf_service->html_variables = $document_type == 'purchase_order' ? $this->getVendorStubVariables() : $this->getStubVariables();
- $pdf_builder = (new PdfBuilder($pdf_service))->build();
- $pdf_service->builder = $pdf_builder;
+ $pdf_builder = (new PdfBuilder($this->pdf_service))->build();
+ $this->pdf_service->builder = $pdf_builder;
- $html = $pdf_service->getHtml();
+ return $this;
+ }
+
+ public function getPdf(): mixed
+ {
+
+ $html = $this->pdf_service->getHtml();
+
+ return $this->pdf_service->resolvePdfEngine($html);
+
+ }
+
+ public function getHtml(): string
+ {
+ return $this->pdf_service->getHtml();
- return $pdf_service->resolvePdfEngine($html);
}
public function build(): self
{
$this->mock = $this->initEntity();
+
+ $this->setPdfService();
return $this;
}
diff --git a/app/Services/Pdf/PdfService.php b/app/Services/Pdf/PdfService.php
index 3b23761149..d506aa7811 100644
--- a/app/Services/Pdf/PdfService.php
+++ b/app/Services/Pdf/PdfService.php
@@ -143,10 +143,9 @@ class PdfService
$this->config = (new PdfConfiguration($this))->init();
-
- $this->html_variables = $this->config->client ?
- (new HtmlEngine($this->invitation))->generateLabelsAndValues() :
- (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
+ $this->html_variables = ($this->invitation instanceof \App\Models\PurchaseOrderInvitation) ?
+ (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues() :
+ (new HtmlEngine($this->invitation))->generateLabelsAndValues();
$this->designer = (new PdfDesigner($this))->build();
diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php
index c7e1a5abe8..64bfe19b48 100644
--- a/app/Services/PdfMaker/Design.php
+++ b/app/Services/PdfMaker/Design.php
@@ -22,6 +22,7 @@ use App\Utils\Traits\MakesInvoiceValues;
use DOMDocument;
use Illuminate\Support\Str;
+/** @deprecated */
class Design extends BaseDesign
{
use MakesInvoiceValues;
@@ -327,9 +328,9 @@ class Design extends BaseDesign
['element' => 'div', 'content' => $this->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
['element' => 'div', 'content' => $this->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
['element' => 'div', 'show_empty' => false, 'elements' => [
- ['element' => 'span', 'content' => "{$this->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
- ['element' => 'span', 'content' => "{$this->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
- ['element' => 'span', 'content' => "{$this->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
+ ['element' => 'p', 'content' => "{$this->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
+ ['element' => 'p', 'content' => "{$this->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
+ ['element' => 'p', 'content' => "{$this->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
]],
['element' => 'div', 'content' => optional($this->client->shipping_country)->name, 'show_empty' => false],
];
@@ -344,7 +345,7 @@ class Design extends BaseDesign
$variables = $this->context['pdf_variables']['client_details'];
foreach ($variables as $variable) {
- $elements[] = ['element' => 'div', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
}
return $elements;
@@ -405,7 +406,6 @@ class Design extends BaseDesign
$_variable = explode('.', $variable)[1];
$_customs = ['custom1', 'custom2', 'custom3', 'custom4'];
- /* 2/7/2022 don't show custom values if they are empty */
$var = str_replace("custom", "custom_value", $_variable);
if (in_array($_variable, $_customs) && !empty($this->entity->{$var})) {
diff --git a/app/Services/PdfMaker/PdfMakerUtilities.php b/app/Services/PdfMaker/PdfMakerUtilities.php
index be3b7b62e3..4c6750bf88 100644
--- a/app/Services/PdfMaker/PdfMakerUtilities.php
+++ b/app/Services/PdfMaker/PdfMakerUtilities.php
@@ -15,6 +15,7 @@ namespace App\Services\PdfMaker;
use DOMDocument;
use DOMXPath;
+/** @deprecated */
trait PdfMakerUtilities
{
private function initializeDomDocument()
diff --git a/app/Utils/PhantomJS/Phantom.php b/app/Utils/PhantomJS/Phantom.php
index 0f1483ba85..f7f1ec2f26 100644
--- a/app/Utils/PhantomJS/Phantom.php
+++ b/app/Utils/PhantomJS/Phantom.php
@@ -11,114 +11,11 @@
namespace App\Utils\PhantomJS;
-use App\Exceptions\PhantomPDFFailure;
-use App\Jobs\Util\SystemLogger;
-use App\Models\CreditInvitation;
-use App\Models\Design;
-use App\Models\InvoiceInvitation;
-use App\Models\QuoteInvitation;
-use App\Models\RecurringInvoiceInvitation;
-use App\Models\SystemLog;
-use App\Services\PdfMaker\Design as PdfDesignModel;
-use App\Services\PdfMaker\Design as PdfMakerDesign;
-use App\Services\PdfMaker\PdfMaker as PdfMakerService;
use App\Utils\CurlUtils;
-use App\Utils\HtmlEngine;
-use App\Utils\Traits\MakesHash;
-use App\Utils\Traits\Pdf\PageNumbering;
-use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Response;
-use Illuminate\Support\Facades\Storage;
-use Illuminate\Support\Str;
class Phantom
{
- use MakesHash;
- use PageNumbering;
-
- /**
- * Generate a PDF from the
- * Phantom JS API.
- *
- * @param $invitation
- */
- public function generate($invitation, $return_pdf = false)
- {
- $entity = false;
- $path = '';
-
- if ($invitation instanceof InvoiceInvitation) {
- $entity = 'invoice';
- $entity_design_id = 'invoice_design_id';
- } elseif ($invitation instanceof CreditInvitation) {
- $entity = 'credit';
- $entity_design_id = 'credit_design_id';
- } elseif ($invitation instanceof QuoteInvitation) {
- $entity = 'quote';
- $entity_design_id = 'quote_design_id';
- } elseif ($invitation instanceof RecurringInvoiceInvitation) {
- $entity = 'recurring_invoice';
- $entity_design_id = 'invoice_design_id';
- }
-
- $entity_obj = $invitation->{$entity};
-
- if ($entity == 'invoice') {
- $path = $entity_obj->client->invoice_filepath($invitation);
- }
-
- if ($entity == 'quote') {
- $path = $entity_obj->client->quote_filepath($invitation);
- }
-
- if ($entity == 'credit') {
- $path = $entity_obj->client->credit_filepath($invitation);
- }
-
- if ($entity == 'recurring_invoice') {
- $path = $entity_obj->client->recurring_invoice_filepath($invitation);
- }
-
- $file_path = $path.$entity_obj->numberFormatter().'.pdf';
-
- $url = config('ninja.app_url').'/phantom/'.$entity.'/'.$invitation->key.'?phantomjs_secret='.config('ninja.phantomjs_secret');
- info($url);
-
- $key = config('ninja.phantomjs_key');
- $phantom_url = "https://phantomjscloud.com/api/browser/v2/{$key}/";
- $pdf = CurlUtils::post($phantom_url, json_encode([
- 'url' => $url,
- 'renderType' => 'pdf',
- 'outputAsJson' => false,
- 'renderSettings' => [
- 'emulateMedia' => 'print',
- 'pdfOptions' => [
- 'preferCSSPageSize' => true,
- 'printBackground' => true,
- ],
- ],
- ]));
-
- $this->checkMime($pdf, $invitation, $entity);
-
- $numbered_pdf = $this->pageNumbering($pdf, $invitation->company);
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- if (! Storage::disk(config('filesystems.default'))->exists($path)) {
- Storage::disk(config('filesystems.default'))->makeDirectory($path);
- }
-
- $instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
-
- if ($return_pdf) {
- return $pdf;
- }
-
- return $file_path;
- }
public function convertHtmlToPdf($html)
{
@@ -143,95 +40,4 @@ class Phantom
return $response;
}
- /* Check if the returning PDF is valid. */
- private function checkMime($pdf, $invitation, $entity)
- {
- $finfo = new \finfo(FILEINFO_MIME);
-
- if ($finfo->buffer($pdf) != 'application/pdf; charset=binary') {
- SystemLogger::dispatch(
- $pdf,
- SystemLog::CATEGORY_PDF,
- SystemLog::EVENT_PDF_RESPONSE,
- SystemLog::TYPE_PDF_FAILURE,
- $invitation->contact->client,
- $invitation->company,
- );
-
- throw new PhantomPDFFailure('There was an error generating the PDF with Phantom JS');
- } else {
- SystemLogger::dispatch(
- 'Entity PDF generated sucessfully => '.$invitation->{$entity}->number,
- SystemLog::CATEGORY_PDF,
- SystemLog::EVENT_PDF_RESPONSE,
- SystemLog::TYPE_PDF_SUCCESS,
- $invitation->contact->client,
- $invitation->company,
- );
- }
- }
-
- public function displayInvitation(string $entity, string $invitation_key)
- {
- $key = $entity.'_id';
-
- $invitation_instance = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
- $invitation = $invitation_instance::where('key', $invitation_key)->first();
-
- $entity_obj = $invitation->{$entity};
-
- $entity_obj->load('client');
-
- App::setLocale($invitation->contact->preferredLocale());
-
- $entity_design_id = $entity.'_design_id';
-
- if ($entity == 'recurring_invoice') {
- $entity_design_id = 'invoice_design_id';
- }
-
- $design_id = $entity_obj->design_id ? $entity_obj->design_id : $this->decodePrimaryKey($entity_obj->client->getSetting($entity_design_id));
-
- $design = Design::withTrashed()->find($design_id);
- $html = new HtmlEngine($invitation);
-
- if ($design->is_custom) {
- $options = [
- 'custom_partials' => json_decode(json_encode($design->design), true),
- ];
- $template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
- } else {
- $template = new PdfMakerDesign(strtolower($design->name));
- }
-
- $state = [
- 'template' => $template->elements([
- 'client' => $entity_obj->client,
- 'entity' => $entity_obj,
- 'pdf_variables' => (array) $entity_obj->company->settings->pdf_variables,
- '$product' => $design->design->product,
- ]),
- 'variables' => $html->generateLabelsAndValues(),
- 'options' => [
- 'all_pages_header' => $entity_obj->client->getSetting('all_pages_header'),
- 'all_pages_footer' => $entity_obj->client->getSetting('all_pages_footer'),
- 'client' => $entity_obj->client,
- 'entity' => $entity_obj,
- ],
- 'process_markdown' => $entity_obj->client->company->markdown_enabled,
- ];
-
- $maker = new PdfMakerService($state);
-
- $data['html'] = $maker->design($template)
- ->build()
- ->getCompiledHTML(true);
-
- if (config('ninja.log_pdf_html')) {
- info($data['html']);
- }
-
- return view('pdf.html', $data);
- }
-
-}
+}
\ No newline at end of file
diff --git a/app/Utils/Traits/Pdf/PdfMaker.php b/app/Utils/Traits/Pdf/PdfMaker.php
index 744a20d531..ab4ed8051a 100644
--- a/app/Utils/Traits/Pdf/PdfMaker.php
+++ b/app/Utils/Traits/Pdf/PdfMaker.php
@@ -91,7 +91,7 @@ trait PdfMaker
}
$html = str_ireplace(['file:/', 'iframe', '