mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Working on html client portal
This commit is contained in:
parent
f37620300b
commit
22bbbf26fb
@ -136,8 +136,11 @@ class InvitationController extends Controller
|
||||
} else {
|
||||
$is_silent = 'true';
|
||||
|
||||
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent]);
|
||||
|
||||
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
|
||||
}
|
||||
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})]);
|
||||
|
||||
return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
|
||||
}
|
||||
|
@ -12,14 +12,20 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Utils\Number;
|
||||
use Livewire\Component;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Utils\VendorHtmlEngine;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Services\Pdf\PdfBuilder;
|
||||
use App\Services\Pdf\PdfService;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Services\Pdf\PdfDesigner;
|
||||
use App\Services\Pdf\PdfConfiguration;
|
||||
use App\Models\PurchaseOrderInvitation;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
|
||||
class PdfSlot extends Component
|
||||
{
|
||||
@ -33,113 +39,178 @@ class PdfSlot extends Component
|
||||
|
||||
public $url;
|
||||
|
||||
private $settings;
|
||||
|
||||
private $html_variables;
|
||||
|
||||
private $entity_type;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->db);
|
||||
}
|
||||
|
||||
public function getPdf()
|
||||
{
|
||||
// $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->entity_type = $this->resolveEntityType();
|
||||
|
||||
$this->settings = $this->entity->client ? $this->entity->client->getMergedSettings() : $this->entity->company->settings;
|
||||
|
||||
$this->html_variables = $this->entity->client ?
|
||||
(new HtmlEngine($this->invitation))->generateLabelsAndValues() :
|
||||
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
|
||||
|
||||
return render('components.livewire.pdf-slot', [
|
||||
'invitation' => $this->invitation,
|
||||
'entity' => $this->entity,
|
||||
'data' => $this->invitation->company->settings
|
||||
'data' => $this->invitation->company->settings,
|
||||
'entity_type' => $this->entity_type,
|
||||
'products' => $this->getProducts(),
|
||||
'services' => $this->getServices(),
|
||||
'amount' => Number::formatMoney($this->entity->amount, $this->entity->client ?: $this->entity->vendor),
|
||||
'balance' => Number::formatMoney($this->entity->balance, $this->entity->client ?: $this->entity->vendor),
|
||||
'company_details' => $this->getCompanyDetails(),
|
||||
'company_address' => $this->getCompanyAddress(),
|
||||
'entity_details' => $this->getEntityDetails(),
|
||||
'user_details' => $this->getUserDetails(),
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPdf()
|
||||
private function convertVariables($string): string
|
||||
{
|
||||
|
||||
// $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation);
|
||||
|
||||
$html = strtr($string, $this->html_variables['labels']);
|
||||
|
||||
$html = strtr($html, $this->html_variables['values']);
|
||||
|
||||
return $html;
|
||||
|
||||
}
|
||||
|
||||
public function getHtml()
|
||||
private function getCompanyAddress()
|
||||
{
|
||||
$pdf_service = new PdfService($this->invitation);
|
||||
|
||||
$pdf_service->config = (new PdfConfiguration($pdf_service))->init();
|
||||
|
||||
$pdf_service->html_variables = $pdf_service->config->client ?
|
||||
(new HtmlEngine($this->invitation))->generateLabelsAndValues() :
|
||||
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
|
||||
|
||||
|
||||
}
|
||||
$company_address = "";
|
||||
|
||||
public function getHtmlX()
|
||||
{
|
||||
|
||||
$pdf_service = new PdfService($this->invitation);
|
||||
|
||||
$pdf_service->config = (new PdfConfiguration($pdf_service))->init();
|
||||
|
||||
$pdf_service->html_variables = $pdf_service->config->client ?
|
||||
(new HtmlEngine($this->invitation))->generateLabelsAndValues() :
|
||||
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
|
||||
|
||||
$pdf_service->designer = (new PdfDesigner($pdf_service));
|
||||
|
||||
$pdf_service->builder = (new PdfBuilder($pdf_service));
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach(['company-details', 'company-address','client-details','entity-details','product-table','table-totals'] as $item) {
|
||||
|
||||
|
||||
$pdf_service->designer->template = '<div id="'.$item.'"></div>';
|
||||
|
||||
match($item){
|
||||
'company-details' => $block = $pdf_service->builder->companyDetails(),
|
||||
'company-address' => $block = $pdf_service->builder->companyAddress(),
|
||||
'client-details' => $block = $pdf_service->builder->clientDetails(),
|
||||
'entity-details' => $block = $pdf_service->builder->invoiceDetails(),
|
||||
'product-table' => $block = $this->productTable(),
|
||||
'table-totals' => $block = $pdf_service->builder->getTableTotals(),
|
||||
default => $block = [],
|
||||
};
|
||||
|
||||
$section = [
|
||||
$item => [
|
||||
'id' => $item,
|
||||
'elements' => $block,
|
||||
]
|
||||
];
|
||||
|
||||
$document = new \DOMDocument();
|
||||
$document->validateOnParse = true;
|
||||
@$document->loadHTML(mb_convert_encoding($pdf_service->designer->template, 'HTML-ENTITIES', 'UTF-8'));
|
||||
|
||||
$pdf_service->builder->document = $document;
|
||||
$pdf_service->builder->sections = $section;
|
||||
|
||||
$html = $pdf_service->builder
|
||||
->getEmptyElements()
|
||||
->updateElementProperties()
|
||||
->updateVariables();
|
||||
|
||||
// $pdf_service->builder->document->removeChild($pdf_service->builder->document->doctype);
|
||||
// $pdf_service->builder->document->replaceChild($pdf_service->builder->document->firstChild->firstChild->firstChild, $pdf_service->builder->document->firstChild);
|
||||
|
||||
$data[$item] = $pdf_service->builder->document->saveHTML();
|
||||
|
||||
$section = [];
|
||||
foreach($this->settings->pdf_variables->company_address as $variable) {
|
||||
$company_address .= "<p>{$variable}</p>";
|
||||
}
|
||||
// nlog($pdf_service->builder->document->saveHTML());
|
||||
|
||||
nlog($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function productTable()
|
||||
{
|
||||
return $this->convertVariables($company_address);
|
||||
|
||||
}
|
||||
|
||||
private function sectionBuilder($tag)
|
||||
private function getCompanyDetails()
|
||||
{
|
||||
$company_details = "";
|
||||
|
||||
foreach($this->settings->pdf_variables->company_details as $variable) {
|
||||
$company_details .= "<p>{$variable}</p>";
|
||||
}
|
||||
|
||||
return $this->convertVariables($company_details);
|
||||
|
||||
}
|
||||
|
||||
private function getEntityDetails()
|
||||
{
|
||||
$entity_details = "<dl class=''>";
|
||||
|
||||
if($this->entity_type == 'invoice' || $this->entity_type == 'recurring_invoice') {
|
||||
foreach($this->settings->pdf_variables->invoice_details as $variable)
|
||||
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
|
||||
|
||||
}
|
||||
elseif($this->entity_type == 'quote'){
|
||||
foreach($this->settings->pdf_variables->quote_details as $variable)
|
||||
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
|
||||
}
|
||||
elseif($this->entity_type == 'credit') {
|
||||
foreach($this->settings->pdf_variables->credit_details as $variable)
|
||||
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
|
||||
}
|
||||
elseif($this->entity_type == 'purchase_order'){
|
||||
foreach($this->settings->pdf_variables->purchase_order_details as $variable)
|
||||
$entity_details .= "<div class='px-1 py-1 sm:grid sm:grid-cols-4 sm:gap-0 sm:px-2 border-b-3 border-fuschia-500'><dt class='leading-4'>{$variable}_label</dt><dd class='text-sm font-mediumtext-gray-900'>{$variable}</dd></div>";
|
||||
}
|
||||
|
||||
$entity_details .= "</dl>";
|
||||
|
||||
return $this->convertVariables($entity_details);
|
||||
|
||||
}
|
||||
|
||||
private function getUserDetails()
|
||||
{
|
||||
$user_details = "";
|
||||
|
||||
if($this->entity_type == 'purchase_order') {
|
||||
foreach($this->settings->pdf_variables->vendor_details as $variable) {
|
||||
$user_details .= "<p>{$variable}</p>";
|
||||
}
|
||||
}
|
||||
else{
|
||||
foreach($this->settings->pdf_variables->client_details as $variable) {
|
||||
$user_details .= "<p>{$variable}</p>";
|
||||
}
|
||||
}
|
||||
|
||||
return $this->convertVariables($user_details);
|
||||
}
|
||||
|
||||
private function getProducts()
|
||||
{
|
||||
$product_items = collect($this->entity->line_items)->filter(function ($item) {
|
||||
return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
|
||||
})->map(function ($item){
|
||||
return [
|
||||
'quantity' => $item->quantity,
|
||||
'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor),
|
||||
'notes' => $item->notes,
|
||||
'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor),
|
||||
];
|
||||
});
|
||||
|
||||
return $product_items;
|
||||
}
|
||||
|
||||
private function getServices()
|
||||
{
|
||||
$task_items = collect($this->entity->line_items)->filter(function ($item) {
|
||||
return $item->type_id == 2;
|
||||
})->map(function ($item){
|
||||
return [
|
||||
'quantity' => $item->quantity,
|
||||
'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor),
|
||||
'notes' => $item->notes,
|
||||
'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor),
|
||||
];
|
||||
});
|
||||
|
||||
return $task_items;
|
||||
|
||||
}
|
||||
|
||||
private function resolveEntityType() :string
|
||||
{
|
||||
if ($this->invitation instanceof InvoiceInvitation) {
|
||||
return 'invoice';
|
||||
} elseif ($this->invitation instanceof QuoteInvitation) {
|
||||
return 'quote';
|
||||
} elseif ($this->invitation instanceof CreditInvitation) {
|
||||
return 'credit';
|
||||
} elseif ($this->invitation instanceof RecurringInvoiceInvitation) {
|
||||
return 'recurring_invoice';
|
||||
} elseif ($this->invitation instanceof PurchaseOrderInvitation) {
|
||||
return 'purchase_order';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,176 @@
|
||||
<div class="flex flex-col w-full">
|
||||
|
||||
<div>
|
||||
<dl>
|
||||
@foreach($data->pdf_variables->company_details as $cd)
|
||||
<dd>{{ $cd }}</dd>
|
||||
@endforeach
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<style>
|
||||
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td
|
||||
{
|
||||
max-width: 1px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
padding: 3px;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="w-full">
|
||||
<div class="flex flex-row content-center border-fuchsia-600 border-b-2">
|
||||
<div id="company-details" class="mx-auto">
|
||||
{!! $company_details !!}
|
||||
</div>
|
||||
|
||||
<div id="company-address" class="mx-auto">
|
||||
{!! $company_address !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div> {!! $entity_details !!}</div>
|
||||
|
||||
<div id="user-details">
|
||||
{!! $user_details !!}
|
||||
</div>
|
||||
|
||||
@if($products->count() > 0)
|
||||
<div id="product-details" class="py-6">
|
||||
<table width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left; width:70%; padding-left:2px;">Item</th>
|
||||
<th style="text-align:right; width:30%; padding-right:2px;">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($products as $product)
|
||||
<tr style="display: table-row;">
|
||||
<td>
|
||||
<div class="product-information">
|
||||
<div class="item-details">
|
||||
<p class="px-2 mt-2">{{ $product['quantity'] }} × {{ $product['cost'] }}</p>
|
||||
<p class="overflow-ellipsis overflow-hidden px-2 mb-2">{{ $product['notes'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="text-align:right; padding-right:2px;">{{ $product['line_total'] }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
@if($services->count() > 0)
|
||||
<div id="task-details" class="py-6">
|
||||
<table width="100%">
|
||||
<thead>
|
||||
<tr class="border-bottom>
|
||||
<th style="text-align:left; width:70%; padding-left:2px;">Service</th>
|
||||
<th style="text-align:right; width:30%; padding-right:2px;">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($services as $service)
|
||||
<tr style="display: table-row;">
|
||||
<td>
|
||||
<div class="">
|
||||
<div class="">
|
||||
<p class="px-2 mt-2">{{ $service['quantity'] }} × {{ $service['cost'] }}</p>
|
||||
<p class="overflow-ellipsis overflow-hidden px-2 mb-2">{{ $service['notes'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="text-align:right; padding-right:2px;">{{ $service['line_total'] }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
<div id="totals" class="mb-20">
|
||||
<table width="100%">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="display: table-row;">
|
||||
<td>
|
||||
<div class="">
|
||||
<div class="">
|
||||
<p class="px-2">{{ ctrans('texts.total') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="text-align:right; padding-right:2px;">{{ $amount }}</td>
|
||||
</tr>
|
||||
<tr style="display: table-row;">
|
||||
<td>
|
||||
<div class="">
|
||||
<div class="">
|
||||
<p class="px-2">{{ ctrans('texts.balance') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="text-align:right; padding-right:2px;">{{ $balance }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
@if(strlen($entity->public_notes) > 3)
|
||||
<div x-data="{ show_notes: false }" class="mb-10">
|
||||
|
||||
<button @click="show_notes = !show_notes" :aria-expanded="show_notes ? 'true' : 'false'" :class="{ 'active': show_notes }" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
|
||||
<span>{{ ctrans('texts.notes') }}</span>
|
||||
</button>
|
||||
|
||||
<div id="notes" class="py-10 border-b-2 border-fuschia-600" x-show="show_notes">
|
||||
{{ $entity->public_notes }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($entity->terms) > 3)
|
||||
<div x-data="{ show_terms: false }" class="mb-10">
|
||||
|
||||
<button @click="show_terms = !show_terms" :aria-expanded="show_terms ? 'true' : 'false'" :class="{ 'active': show_terms }" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
|
||||
<span>{{ ctrans('texts.terms') }}</span>
|
||||
</button>
|
||||
|
||||
<div id="terms" class="py-10 border-b-2 border-fuschia-600" x-show="show_terms">
|
||||
{{ $entity->terms }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($entity->footer) > 3)
|
||||
<div x-data="{ show_footer: false }" class="mb-10">
|
||||
|
||||
<button @click="show_footer = !show_footer" :aria-expanded="show_footer ? 'true' : 'false'" :class="{ 'active': show_footer }" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"/></svg>
|
||||
<span>{{ ctrans('texts.footer') }}</span>
|
||||
</button>
|
||||
|
||||
<div id="terms" class="py-10 border-b-2 border-fuschia-600" x-show="show_footer">
|
||||
{{ $entity->footer }}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
@ -41,4 +41,4 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@include('portal.ninja2020.components.html-viewer', ['data' => $data])
|
||||
@include('portal.ninja2020.components.html-viewer')
|
||||
|
Loading…
x
Reference in New Issue
Block a user