Template scaffold

This commit is contained in:
David Bomba 2023-09-25 15:56:32 +10:00
parent 909d9ed9df
commit ae7915353f
7 changed files with 240 additions and 80 deletions

View File

@ -11,42 +11,47 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\DataMapper\Analytics\LivePreview; use App\Models\Task;
use App\Factory\CreditFactory; use App\Utils\Ninja;
use App\Factory\InvoiceFactory; use App\Models\Quote;
use App\Factory\QuoteFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Http\Requests\Preview\DesignPreviewRequest;
use App\Http\Requests\Preview\PreviewInvoiceRequest;
use App\Jobs\Util\PreviewPdf;
use App\Libraries\MultiDB;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation; use App\Models\Payment;
use App\Models\Quote; use App\Models\Project;
use App\Models\RecurringInvoice; use App\Utils\HtmlEngine;
use App\Repositories\CreditRepository; use App\Libraries\MultiDB;
use App\Repositories\InvoiceRepository; use App\Factory\QuoteFactory;
use App\Repositories\QuoteRepository; use App\Jobs\Util\PreviewPdf;
use App\Repositories\RecurringInvoiceRepository; use App\Models\ClientContact;
use App\Services\Pdf\PdfMock; use App\Services\Pdf\PdfMock;
use App\Factory\CreditFactory;
use App\Factory\InvoiceFactory;
use App\Utils\Traits\MakesHash;
use App\Models\RecurringInvoice;
use App\Utils\PhantomJS\Phantom;
use App\Models\InvoiceInvitation;
use App\Services\PdfMaker\Design; use App\Services\PdfMaker\Design;
use App\Utils\HostedPDF\NinjaPdf;
use Illuminate\Support\Facades\DB;
use App\Services\PdfMaker\PdfMaker;
use Illuminate\Support\Facades\App;
use App\Repositories\QuoteRepository;
use App\Repositories\CreditRepository;
use App\Utils\Traits\MakesInvoiceHtml;
use Turbo124\Beacon\Facades\LightLogs;
use App\Repositories\InvoiceRepository;
use App\Utils\Traits\Pdf\PageNumbering;
use App\Factory\RecurringInvoiceFactory;
use Illuminate\Support\Facades\Response;
use App\DataMapper\Analytics\LivePreview;
use App\Services\Template\TemplateService;
use App\Repositories\RecurringInvoiceRepository;
use App\Http\Requests\Preview\DesignPreviewRequest;
use App\Services\PdfMaker\Design as PdfDesignModel; use App\Services\PdfMaker\Design as PdfDesignModel;
use App\Services\PdfMaker\Design as PdfMakerDesign; use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker; use App\Http\Requests\Preview\PreviewInvoiceRequest;
use App\Utils\HostedPDF\NinjaPdf; use App\Models\PurchaseOrder;
use App\Utils\HtmlEngine;
use App\Utils\Ninja;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesInvoiceHtml;
use App\Utils\Traits\Pdf\PageNumbering;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Response;
use Turbo124\Beacon\Facades\LightLogs;
class PreviewController extends BaseController class PreviewController extends BaseController
{ {
@ -67,6 +72,11 @@ class PreviewController extends BaseController
public function show() public function show()
{ {
// if(request()->has('template')){
return $this->template();
// }
nlog("wooops");
if (request()->has('entity') && if (request()->has('entity') &&
request()->has('entity_id') && request()->has('entity_id') &&
! empty(request()->input('entity')) && ! empty(request()->input('entity')) &&
@ -132,7 +142,6 @@ class PreviewController extends BaseController
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
} }
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
@ -342,6 +351,64 @@ class PreviewController extends BaseController
return $response; return $response;
} }
private function template()
{
/** @var \App\Models\User $user */
$user = auth()->user();
/** @var \App\Models\Company $company */
$company = $user->company();
// $template = request()->input('design');
$design_object = json_decode(json_encode(request()->input('design')),1);
$data = [
'invoices' => Invoice::whereHas('payments')->with('client','payments')->company()->orderBy('id','desc')->take(5)->get(),
'quotes' => Quote::query()->company()->with('client')->orderBy('id','desc')->take(5)->get(),
'credits' => Credit::query()->company()->with('client')->orderBy('id','desc')->take(5)->get(),
'payments' => Payment::query()->company()->with('client')->orderBy('id','desc')->take(5)->get(),
'purchase_orders' => PurchaseOrder::query()->with('vendor')->company()->orderBy('id','desc')->take(5)->get(),
'tasks' => Task::query()->with('client','invoice')->company()->orderBy('id','desc')->take(5)->get(),
'projects' => Project::query()->with('tasks','client')->company()->orderBy('id','desc')->take(5)->get(),
];
nlog($design_object);
$ts = (new TemplateService());
$ts->setTemplate($design_object)
->build($data);
$html = $ts->getHtml();
if (request()->query('html') == 'true') {
return $html;
}
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
return (new Phantom)->convertHtmlToPdf($html);
}
if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
$pdf = (new NinjaPdf())->build($html);
$numbered_pdf = $this->pageNumbering($pdf, $company);
if ($numbered_pdf) {
$pdf = $numbered_pdf;
}
return $pdf;
}
$file_path = (new PreviewPdf($html, $company))->handle();
$response = Response::make($file_path, 200);
$response->header('Content-Type', 'application/pdf');
return $response;
}
private function blankEntity() private function blankEntity()
{ {

View File

@ -12,6 +12,7 @@
namespace App\Services\PdfMaker; namespace App\Services\PdfMaker;
use App\Services\Template\TemplateService;
use League\CommonMark\CommonMarkConverter; use League\CommonMark\CommonMarkConverter;
class PdfMaker class PdfMaker
@ -78,17 +79,13 @@ class PdfMaker
$replacements = []; $replacements = [];
$contents = $this->document->getElementsByTagName('ninja'); $contents = $this->document->getElementsByTagName('ninja');
$twig = (new TemplateService())->twig;
foreach ($contents as $content) { foreach ($contents as $content) {
$template = $content->ownerDocument->saveHTML($content); $template = $content->ownerDocument->saveHTML($content);
$loader = new \Twig\Loader\FilesystemLoader(storage_path());
$twig = new \Twig\Environment($loader);
$string_extension = new \Twig\Extension\StringLoaderExtension();
$twig->addExtension($string_extension);
$template = $twig->createTemplate(html_entity_decode($template)); $template = $twig->createTemplate(html_entity_decode($template));
$template = $template->render($this->options); $template = $template->render($this->options);

View File

@ -15,7 +15,6 @@ use App\Models\Task;
use App\Models\Quote; use App\Models\Quote;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Design; use App\Models\Design;
use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\Project; use App\Models\Project;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
@ -24,7 +23,6 @@ use App\Models\ClientContact;
use App\Models\PurchaseOrder; use App\Models\PurchaseOrder;
use App\Utils\VendorHtmlEngine; use App\Utils\VendorHtmlEngine;
use App\Utils\PaymentHtmlEngine; use App\Utils\PaymentHtmlEngine;
use Illuminate\Support\Collection;
use Twig\Extra\Intl\IntlExtension; use Twig\Extra\Intl\IntlExtension;
use App\Transformers\TaskTransformer; use App\Transformers\TaskTransformer;
use App\Transformers\QuoteTransformer; use App\Transformers\QuoteTransformer;
@ -41,9 +39,11 @@ class TemplateService
private \DomDocument $document; private \DomDocument $document;
public \Twig\Environment $twig;
private string $compiled_html = ''; private string $compiled_html = '';
public function __construct(public Design $template) public function __construct(public ?Design $template = null)
{ {
$this->template = $template; $this->template = $template;
$this->init(); $this->init();
@ -59,6 +59,12 @@ class TemplateService
$this->document = new \DOMDocument(); $this->document = new \DOMDocument();
$this->document->validateOnParse = true; $this->document->validateOnParse = true;
$loader = new \Twig\Loader\FilesystemLoader(storage_path());
$this->twig = new \Twig\Environment($loader);
$string_extension = new \Twig\Extension\StringLoaderExtension();
$this->twig->addExtension($string_extension);
$this->twig->addExtension(new IntlExtension());
return $this; return $this;
} }
@ -93,24 +99,19 @@ class TemplateService
{ {
$data = $this->preProcessDataBlocks($data); $data = $this->preProcessDataBlocks($data);
$replacements = []; $replacements = [];
nlog($data);
// nlog($data);
$contents = $this->document->getElementsByTagName('ninja'); $contents = $this->document->getElementsByTagName('ninja');
foreach ($contents as $content) { foreach ($contents as $content) {
$template = $content->ownerDocument->saveHTML($content); $template = $content->ownerDocument->saveHTML($content);
$loader = new \Twig\Loader\FilesystemLoader(storage_path()); $template = $this->twig->createTemplate(html_entity_decode($template));
$twig = new \Twig\Environment($loader);
$string_extension = new \Twig\Extension\StringLoaderExtension();
$twig->addExtension($string_extension);
$twig->addExtension(new IntlExtension());
$template = $twig->createTemplate(html_entity_decode($template));
$template = $template->render($data); $template = $template->render($data);
nlog($template); // nlog($template);
$f = $this->document->createDocumentFragment(); $f = $this->document->createDocumentFragment();
$f->appendXML($template); $f->appendXML($template);
@ -140,8 +141,12 @@ nlog($data);
$html = $this->getHtml(); $html = $this->getHtml();
foreach($variables as $key => $variable) { foreach($variables as $key => $variable) {
$html = strtr($html, $variable['labels']);
$html = strtr($html, $variable['values']); if(isset($variable['labels']) && isset($variable['values']))
{
$html = strtr($html, $variable['labels']);
$html = strtr($html, $variable['values']);
}
} }
@$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); @$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
@ -169,6 +174,9 @@ nlog($data);
*/ */
private function compose(): self private function compose(): self
{ {
if(!$this->template)
return $this;
$html = ''; $html = '';
$html .= $this->template->design->includes; $html .= $this->template->design->includes;
$html .= $this->template->design->header; $html .= $this->template->design->header;
@ -181,6 +189,27 @@ nlog($data);
} }
/**
* Inject the template components
* manually
*
* @return self
*/
public function setTemplate(array $partials): self
{nlog($partials);
$html = '';
$html .= $partials['design']['includes'];
$html .= $partials['design']['header'];
$html .= $partials['design']['body'];
$html .= $partials['design']['footer'];
@$this->document->loadHTML($html);
return $this;
}
/** /**
* Resolves the labels and values needed to replace the string * Resolves the labels and values needed to replace the string
* holders in the template. * holders in the template.
@ -200,7 +229,7 @@ nlog($data);
'payments' => $processed = (new PaymentHtmlEngine($value->first(), $value->first()->client->contacts()->first()))->generateLabelsAndValues(), 'payments' => $processed = (new PaymentHtmlEngine($value->first(), $value->first()->client->contacts()->first()))->generateLabelsAndValues(),
'tasks' => $processed = [], 'tasks' => $processed = [],
'projects' => $processed = [], 'projects' => $processed = [],
'purchase_orders' => $processed = (new VendorHtmlEngine($value->first()->invitations()->first()))->generateLabelsAndValues(), 'purchase_orders' => $processed = $value->first() && $value->first()->invitations()->first() ? (new VendorHtmlEngine($value->first()->invitations()->first()))->generateLabelsAndValues() : [],
}; };
return $processed; return $processed;
@ -251,6 +280,7 @@ nlog($data);
if($invoice['payments']['data'] ?? false) { if($invoice['payments']['data'] ?? false) {
foreach($invoice['payments']['data'] as $keyx => $payment) { foreach($invoice['payments']['data'] as $keyx => $payment) {
$invoices['data'][$key]['payments'][$keyx]['paymentables']= $payment['paymentables']['data'] ?? []; $invoices['data'][$key]['payments'][$keyx]['paymentables']= $payment['paymentables']['data'] ?? [];
$invoices['data'][$key]['payments'][$keyx]['type']= $payment['type']['data'] ?? [];
} }
} }
@ -264,12 +294,22 @@ nlog($data);
$it = new QuoteTransformer(); $it = new QuoteTransformer();
$it->setDefaultIncludes(['client']); $it->setDefaultIncludes(['client']);
$manager = new Manager(); $manager = new Manager();
$manager->setSerializer(new ArraySerializer()); $manager->parseIncludes(['client']);
$resource = new \League\Fractal\Resource\Collection($quotes, $it, Quote::class); $resource = new \League\Fractal\Resource\Collection($quotes, $it, null);
$i = $manager->createData($resource)->toArray(); $resources = $manager->createData($resource)->toArray();
foreach($resources['data'] as $key => $resource) {
$resources['data'][$key]['client'] = $resource['client']['data'] ?? [];
$resources['data'][$key]['client']['contacts'] = $resource['client']['data']['contacts']['data'] ?? [];
}
return $resources['data'];
$i['client']['contacts'] = $i['client']['contacts'][ClientContact::class];
return $i[Quote::class];
} }
@ -278,10 +318,18 @@ nlog($data);
$it = new CreditTransformer(); $it = new CreditTransformer();
$it->setDefaultIncludes(['client']); $it->setDefaultIncludes(['client']);
$manager = new Manager(); $manager = new Manager();
$manager->setSerializer(new ArraySerializer());
$resource = new \League\Fractal\Resource\Collection($credits, $it, Credit::class); $resource = new \League\Fractal\Resource\Collection($credits, $it, Credit::class);
$i = $manager->createData($resource)->toArray(); $resources = $manager->createData($resource)->toArray();
return $i[Credit::class];
foreach($resources['data'] as $key => $resource) {
$resources['data'][$key]['client'] = $resource['client']['data'] ?? [];
$resources['data'][$key]['client']['contacts'] = $resource['client']['data']['contacts']['data'] ?? [];
}
return $resources['data'];
} }
@ -290,22 +338,40 @@ nlog($data);
$it = new PaymentTransformer(); $it = new PaymentTransformer();
$it->setDefaultIncludes(['client','invoices','paymentables']); $it->setDefaultIncludes(['client','invoices','paymentables']);
$manager = new Manager(); $manager = new Manager();
$manager->setSerializer(new ArraySerializer()); $resource = new \League\Fractal\Resource\Collection($payments, $it, null);
$resource = new \League\Fractal\Resource\Collection($payments, $it, Payment::class); $resources = $manager->createData($resource)->toArray();
$i = $manager->createData($resource)->toArray();
return $i[Payment::class]; foreach($resources['data'] as $key => $resource) {
$resources['data'][$key]['client'] = $resource['client']['data'] ?? [];
$resources['data'][$key]['client']['contacts'] = $resource['client']['data']['contacts']['data'] ?? [];
$resources['data'][$key]['invoices'] = $invoice['invoices']['data'] ?? [];
}
return $resources['data'];
} }
private function processTasks($tasks): array private function processTasks($tasks): array
{ {
$it = new TaskTransformer(); $it = new TaskTransformer();
$it->setDefaultIncludes(['client','tasks','project','invoice']); $it->setDefaultIncludes(['client','project','invoice']);
$manager = new Manager(); $manager = new Manager();
$manager->setSerializer(new ArraySerializer()); $resource = new \League\Fractal\Resource\Collection($tasks, $it, null);
$resource = new \League\Fractal\Resource\Collection($tasks, $it, Task::class); $resources = $manager->createData($resource)->toArray();
$i = $manager->createData($resource)->toArray();
return $i[Task::class]; foreach($resources['data'] as $key => $resource) {
$resources['data'][$key]['client'] = $resource['client']['data'] ?? [];
$resources['data'][$key]['client']['contacts'] = $resource['client']['data']['contacts']['data'] ?? [];
$resources['data'][$key]['project'] = $resource['project']['data'] ?? [];
$resources['data'][$key]['invoice'] = $resource['invoice'] ?? [];
}
return $resources['data'];
} }

View File

@ -12,10 +12,11 @@
namespace App\Transformers; namespace App\Transformers;
use App\Models\Client; use App\Models\Client;
use App\Models\Document;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\Document;
use App\Models\Paymentable; use App\Models\Paymentable;
use App\Models\PaymentType;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
class PaymentTransformer extends EntityTransformer class PaymentTransformer extends EntityTransformer
@ -72,9 +73,7 @@ class PaymentTransformer extends EntityTransformer
public function includeType(Payment $payment) public function includeType(Payment $payment)
{ {
return [ return $this->includeItem($payment, new PaymentTypeTransformer, PaymentType::class);
'type' => $payment->type->translatedType() ?? '',
];
} }
public function transform(Payment $payment) public function transform(Payment $payment)

View File

@ -0,0 +1,25 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Transformers;
use App\Models\Payment;
class PaymentTypeTransformer extends EntityTransformer
{
public function transform(Payment $payment)
{
return [
'name' => $payment->translatedType()
];
}
}

View File

@ -55,7 +55,7 @@ class TaskTransformer extends EntityTransformer
{ {
$transformer = new InvoiceTransformer($this->serializer); $transformer = new InvoiceTransformer($this->serializer);
if (!$task->user) { if (!$task->invoice) {
return null; return null;
} }

View File

@ -102,7 +102,13 @@ class TemplateTest extends TestCase
'; ';
private string $payments_body = ' private string $payments_body = '
<ninja> CoName: $company.name
ClName: $client.name
InNumber: $invoice.number
<ninja>
CoName: $company.name
ClName: $client.name
InNumber: $invoice.number
<table class="min-w-full text-left text-sm font-light"> <table class="min-w-full text-left text-sm font-light">
<thead class="border-b font-medium dark:border-neutral-500"> <thead class="border-b font-medium dark:border-neutral-500">
<tr class="text-sm leading-normal"> <tr class="text-sm leading-normal">
@ -129,7 +135,7 @@ class TemplateTest extends TestCase
{% for payment in invoice.payments|filter(payment => payment.is_deleted == false) %} {% for payment in invoice.payments|filter(payment => payment.is_deleted == false) %}
{% for pivot in payment.paymentables %} {% for pivot in payment.paymentables %}
<tr class="border-b dark:border-neutral-500"> <tr class="border-b dark:border-neutral-500">
<td class="whitespace-nowrap px-6 py-4 font-medium">{{ payment.number }}</td> <td class="whitespace-nowrap px-6 py-4 font-medium">{{ payment.number }}</td>
<td class="whitespace-nowrap px-6 py-4 font-medium">{{ payment.date }}</td> <td class="whitespace-nowrap px-6 py-4 font-medium">{{ payment.date }}</td>
@ -221,8 +227,8 @@ class TemplateTest extends TestCase
$data['invoices'] = $invoices; $data['invoices'] = $invoices;
$ts = $replicated_design->service()->build($data); $ts = $replicated_design->service()->build($data);
nlog("results = "); // nlog("results = ");
nlog($ts->getHtml()); // nlog($ts->getHtml());
$this->assertNotNull($ts->getHtml()); $this->assertNotNull($ts->getHtml());
} }
@ -253,8 +259,8 @@ class TemplateTest extends TestCase
$ts = $replicated_design->service()->build($data); $ts = $replicated_design->service()->build($data);
nlog("results = "); // nlog("results = ");
nlog($ts->getHtml()); // nlog($ts->getHtml());
$this->assertNotNull($ts->getHtml()); $this->assertNotNull($ts->getHtml());
} }