Updates for designs
This commit is contained in:
commit
3dd9e36997
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,218 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Vendor;
|
||||
|
||||
use App\Exceptions\FilePermissionsFailure;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Design;
|
||||
use App\Services\Pdf\PdfService;
|
||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\PhantomJS\Phantom;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesInvoiceHtml;
|
||||
use App\Utils\Traits\NumberFormatter;
|
||||
use App\Utils\Traits\Pdf\PageNumbering;
|
||||
use App\Utils\Traits\Pdf\PdfMaker;
|
||||
use App\Utils\VendorHtmlEngine;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/** @deprecated 26-10-2023 5.7.30x */
|
||||
class CreatePurchaseOrderPdf implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
use NumberFormatter;
|
||||
use MakesInvoiceHtml;
|
||||
use PdfMaker;
|
||||
use MakesHash;
|
||||
use PageNumbering;
|
||||
|
||||
public $entity;
|
||||
|
||||
public $company;
|
||||
|
||||
public $contact;
|
||||
|
||||
private $disk;
|
||||
|
||||
public $invitation;
|
||||
|
||||
public $entity_string = '';
|
||||
|
||||
public $vendor;
|
||||
|
||||
private string $path = '';
|
||||
|
||||
private string $file_path = '';
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param $invitation
|
||||
*/
|
||||
public function __construct($invitation, $disk = null)
|
||||
{
|
||||
$this->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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Pdf;
|
||||
|
||||
use App\Models\Design;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DesignExtractor
|
||||
{
|
||||
|
||||
private ?string $html = null;
|
||||
|
||||
public function __construct(private ?string $design = null, private array $options = [])
|
||||
{
|
||||
if($design) {
|
||||
$design = strtolower($design);
|
||||
|
||||
Str::endsWith('.html', $design) ? $this->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 '';
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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})) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace App\Services\PdfMaker;
|
|||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
|
||||
/** @deprecated */
|
||||
trait PdfMakerUtilities
|
||||
{
|
||||
private function initializeDomDocument()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ trait PdfMaker
|
|||
}
|
||||
|
||||
$html = str_ireplace(['file:/', 'iframe', '<embed', '<embed', '<object', '<object', '127.0.0.1', 'localhost', '<?xml encoding="UTF-8">', '/etc/'], '', $html);
|
||||
|
||||
// nlog($html);
|
||||
$generated = $pdf
|
||||
->setHtml($html)
|
||||
->generate();
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ class VendorHtmlEngine
|
|||
$data['$line_tax_values'] = ['value' => $this->lineTaxValues(), 'label' => ctrans('texts.taxes')];
|
||||
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->company->date_format(), $this->vendor->locale()) ?: ' ', 'label' => ctrans('texts.date')];
|
||||
|
||||
$data['$show_shipping_address'] = ['value' => strlen($this->entity->client->shipping_address1 ?? '') > 0 && $this->settings->show_shipping_address ? 'flex' : 'none', 'label' => ''];
|
||||
$data['$show_shipping_address_block'] = ['value' => strlen($this->entity->client->shipping_address1 ?? '') > 0 && $this->settings->show_shipping_address ? 'block' : 'none', 'label' => ''];
|
||||
$data['$show_shipping_address_visibility'] = ['value' => strlen($this->entity->client->shipping_address1 ?? '') > 0 && $this->settings->show_shipping_address ? 1 : 0, 'label' => ''];
|
||||
|
||||
$data['$due_date'] = ['value' => $this->translateDate($this->entity->due_date, $this->company->date_format(), $this->vendor->locale()) ?: ' ', 'label' => ctrans('texts.due_date')];
|
||||
|
||||
$data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->company->date_format(), $this->vendor->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
|
||||
|
|
@ -154,7 +158,7 @@ class VendorHtmlEngine
|
|||
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.purchase_order')];
|
||||
$data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.number')];
|
||||
$data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.purchase_order_number_short')];
|
||||
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?? ''), $this->company) ?: '', 'label' => ctrans('texts.invoice_terms')];
|
||||
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?? ''), $this->company) ?: '', 'label' => ctrans('texts.purchase_order_terms')];
|
||||
$data['$terms'] = &$data['$entity.terms'];
|
||||
$data['$view_link'] = ['value' => $this->buildViewButton($this->invitation->getLink(), ctrans('texts.view_purchase_order')), 'label' => ctrans('texts.view_purchase_order')];
|
||||
$data['$viewLink'] = &$data['$view_link'];
|
||||
|
|
@ -428,6 +432,9 @@ class VendorHtmlEngine
|
|||
|
||||
$data['$payments'] = ['value' => '', 'label' => ctrans('texts.payments')];
|
||||
|
||||
$data['$shipping'] = ['value' => '', 'label' => ctrans('texts.ship_to')];
|
||||
$data['$ship_to'] = &$data['$shipping'];
|
||||
|
||||
if ($this->entity->client()->exists()) {
|
||||
$data['$client1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client1', $this->entity->client->custom_value1, $this->entity->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client1')];
|
||||
$data['$client2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client2', $this->entity->client->custom_value2, $this->entity->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client2')];
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Design;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
|
|
@ -49,9 +48,9 @@ class DesignSeeder extends Seeder
|
|||
}
|
||||
}
|
||||
|
||||
foreach (Design::all() as $design) {
|
||||
$template = new PdfMakerDesign(strtolower($design->name));
|
||||
$template->document();
|
||||
foreach (Design::where('is_custom', false)->get() as $design) {
|
||||
|
||||
$template = new \App\Services\Pdf\DesignExtractor($design->name);
|
||||
|
||||
$design_object = new \stdClass;
|
||||
$design_object->includes = $template->getSectionHTML('style');
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@
|
|||
zoom: 80%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
|
||||
html {
|
||||
/* width: 210mm; */
|
||||
/* height: 200mm; */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
|
@ -40,11 +40,11 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
#spacer-table > * > tr > td {
|
||||
#spacer-table>*>tr>td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#spacer-table{
|
||||
#spacer-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
@ -61,14 +61,16 @@
|
|||
}
|
||||
|
||||
|
||||
#header, #header-spacer {
|
||||
#header,
|
||||
#header-spacer {
|
||||
height: 160px;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-width: $company_logo_size;
|
||||
object-fit: contain;
|
||||
|
|
@ -89,11 +91,19 @@
|
|||
}
|
||||
|
||||
#client-details {
|
||||
padding-right:1rem;
|
||||
padding-right: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height) !important;
|
||||
padding-left: 1.5rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
#vendor-details {
|
||||
padding-right: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height) !important;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
#shipping-details {
|
||||
|
|
@ -102,16 +112,16 @@
|
|||
line-height: var(--line-height) !important;
|
||||
}
|
||||
|
||||
#client-details > :first-child {
|
||||
#client-details> :first-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.client-entity-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
.client-wrapper-left-side {
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -127,7 +137,7 @@
|
|||
color: white !important;
|
||||
}
|
||||
|
||||
#entity-details > tr,
|
||||
#entity-details>tr,
|
||||
#entity-details th {
|
||||
font-weight: normal;
|
||||
line-height: var(--line-height) !important;
|
||||
|
|
@ -142,8 +152,8 @@
|
|||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
[data-ref="table"]:last-child{
|
||||
margin-bottom:0;
|
||||
[data-ref="table"]:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.task-time-details {
|
||||
|
|
@ -152,36 +162,46 @@
|
|||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
padding: 1.5rem;
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th:last-child {
|
||||
text-align: right;
|
||||
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
padding: 1.5rem;
|
||||
th.left-radius {
|
||||
padding-left: 1rem !important;
|
||||
}
|
||||
|
||||
th.right-radius {
|
||||
text-align:right !important;
|
||||
text-align: right !important;
|
||||
padding-right: 1rem !important;
|
||||
}
|
||||
|
||||
|
||||
td.right-radius {
|
||||
text-align: right !important;
|
||||
padding-right: 1rem !important;
|
||||
}
|
||||
|
||||
td.left-radius{
|
||||
td.left-radius {
|
||||
font-weight: bold;
|
||||
padding-left: 1rem !important;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr:nth-child(odd) {
|
||||
[data-ref="table"]>tbody>tr:nth-child(odd) {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
|
|
@ -199,43 +219,27 @@
|
|||
gap: 80px;
|
||||
}
|
||||
|
||||
#table-totals .totals-table-right-side > * {
|
||||
#table-totals .totals-table-right-side>* {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
#table-totals > .totals-table-right-side > * > :nth-child(1) {
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(1) {
|
||||
text-align: $dir_text_align;
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
#table-totals > .totals-table-right-side > * > :not([hidden]) ~ :not([hidden]) {
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
#table-totals > .totals-table-right-side > * > :nth-child(2) {
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due-label'],
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
|
@ -243,7 +247,7 @@
|
|||
.entity-label {
|
||||
text-transform: uppercase;
|
||||
color: var(--primary-color);
|
||||
padding-left: 1.5rem;
|
||||
padding-left: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +263,8 @@
|
|||
color: white;
|
||||
}
|
||||
|
||||
#footer, #footer-spacer {
|
||||
#footer,
|
||||
#footer-spacer {
|
||||
height: 160px;
|
||||
padding: 1rem 1rem;
|
||||
margin-top: 1rem;
|
||||
|
|
@ -269,7 +274,7 @@
|
|||
padding-top: 0.5rem
|
||||
}
|
||||
|
||||
[data-ref="footer_content"]{
|
||||
[data-ref="footer_content"] {
|
||||
padding-right: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
|
@ -304,23 +309,28 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#statement-invoice-table-totals > p {
|
||||
#statement-invoice-table-totals>p {
|
||||
margin-right: 2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.description-th'] {
|
||||
width:30%;
|
||||
overflow-wrap: break-word;
|
||||
[data-ref='product_table-product.description-th'],
|
||||
[data-ref='product_table-product.description-td'] {
|
||||
width: 30%;
|
||||
overflow-wrap: break-word;
|
||||
padding-right: 1rem !important;
|
||||
}
|
||||
|
||||
.left-radius {
|
||||
padding-left: 1rem;
|
||||
text-align: right;
|
||||
[data-ref='product_table-product.tax1-th'],
|
||||
[data-ref='product_table-product.tax1-td'],
|
||||
[data-ref='product_table-product.discount-th'],
|
||||
[data-ref='product_table-product.discount-td'] {
|
||||
width: 9%;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -331,24 +341,24 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
|
|
@ -359,14 +369,12 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
|
@ -401,69 +409,66 @@
|
|||
|
||||
<div id="header">
|
||||
<div class="logo-container">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo"/>
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo" />
|
||||
</div>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div id="body">
|
||||
<table id="spacer-table" cellspacing="0" >
|
||||
<table id="spacer-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="header-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="header-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="client-entity-wrapper">
|
||||
<div class="client-wrapper-left-side">
|
||||
<div>
|
||||
<h4 class="entity-label" style="margin-top:0px; margin-bottom:10px;">$entity_label</h4>
|
||||
<div id="client-details" cellspacing="0" cellpadding="0" ></div>
|
||||
<div id="vendor-details" cellspacing="0" cellpadding="0"></div>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="client-entity-wrapper">
|
||||
<div class="client-wrapper-left-side">
|
||||
<div>
|
||||
<h4 class="entity-label" style="margin-top:0px; margin-bottom:10px;">$entity_label</h4>
|
||||
<div id="client-details" cellspacing="0" cellpadding="0"></div>
|
||||
<div id="vendor-details" cellspacing="0" cellpadding="0"></div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="entity-label" style="opacity: 0; margin-top:0px; margin-bottom:10px;">&</h4>
|
||||
<div id="shipping-details" cellspacing="0" cellpadding="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="entity-label" style="opacity: 0; margin-top:0px; margin-bottom:10px;">&</h4>
|
||||
<div id="shipping-details" cellspacing="0" cellpadding="0"></div>
|
||||
|
||||
<div class="entity-details-wrapper-right-side">
|
||||
<h4 class="entity-label" style="margin-top:0px; margin-bottom:10px; color:transparent;">
|
||||
$entity_label</h4>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" dir="$dir" cellspacing="0" cellpadding="0"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="entity-details-wrapper-right-side">
|
||||
<h4 class="entity-label" style="margin-top:0px; margin-bottom:10px; color:transparent;">$entity_label</h4>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" dir="$dir" cellspacing="0" cellpadding="0"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="footer-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="footer-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -474,4 +479,4 @@ $entity_images
|
|||
<div style="width: 100%;">
|
||||
<p data-ref="footer_content">$entity_footer</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<style id="style">
|
||||
@import url($font_url);
|
||||
|
||||
|
||||
:root {
|
||||
--primary-color: $primary_color;
|
||||
--secondary-color: $secondary_color;
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
@ -22,7 +22,9 @@
|
|||
}
|
||||
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
|
|
@ -41,20 +43,29 @@
|
|||
|
||||
.header-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1.8fr 1fr 1fr;
|
||||
grid-gap: 20px;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: start;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
.company-logo-container {
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-width: $company_logo_size;
|
||||
}
|
||||
|
||||
.header-container > span {
|
||||
.header-container>span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#company-details {
|
||||
justify-self: center;
|
||||
align-self: start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #AAA9A9;
|
||||
|
|
@ -62,6 +73,8 @@
|
|||
}
|
||||
|
||||
#company-address {
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #b1b1b1;
|
||||
|
|
@ -73,6 +86,10 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.entity-issued-to:not(:empty)::after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
.client-and-entity-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
@ -84,10 +101,9 @@
|
|||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
vertical-align: top;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
#client-details > p:nth-child(2) {
|
||||
#client-details>p:nth-child(2) {
|
||||
color: var(--primary-color);
|
||||
font-size: 120%;
|
||||
}
|
||||
|
|
@ -107,21 +123,23 @@
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
#entity-details p { margin-right: 20px; }
|
||||
#entity-details p {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#entity-details th {
|
||||
font-weight: normal;
|
||||
padding-bottom: .5rem;
|
||||
}
|
||||
|
||||
#entity-details > tbody > tr > th:nth-child(2) {
|
||||
#entity-details>tbody>tr>th:nth-child(2) {
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
|
@ -133,12 +151,12 @@
|
|||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
padding: 1rem;
|
||||
color: white;
|
||||
font-weight: semibold;
|
||||
|
|
@ -157,27 +175,19 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr:nth-child(odd) > td {
|
||||
[data-ref="table"]>tbody>tr:nth-child(odd)>td {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr:nth-child(even) > td {
|
||||
[data-ref="table"]>tbody>tr:nth-child(even)>td {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
[data-element='product-table-balance-due-label'],
|
||||
[data-element='product-table-balance-due'],
|
||||
[data-element='task-table-balance-due-label'],
|
||||
[data-element='task-table-balance-due'] {
|
||||
color: var(--secondary-color) !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
#table-totals > *:last-child {
|
||||
#table-totals>*:last-child {
|
||||
border-bottom-left-radius: 1rem;
|
||||
border-bottom-right-radius: 1rem;
|
||||
}
|
||||
|
|
@ -189,7 +199,6 @@
|
|||
gap: 80px;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.8rem;
|
||||
padding-left: 0.7rem;
|
||||
/*page-break-inside:auto; this may cause weird breaking*/
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
|
@ -205,7 +214,7 @@
|
|||
padding-left: 7px;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
|
|
@ -216,31 +225,11 @@
|
|||
padding-right: 17px;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due-label'],
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
padding-left: 0.8rem
|
||||
}
|
||||
|
||||
#footer {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
|
@ -251,23 +240,26 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
[data-ref="totals_table-outstanding-label"]{
|
||||
[data-ref="totals_table-outstanding-label"] {
|
||||
background-color: var(--secondary-color);
|
||||
color: white;
|
||||
font-size:120%;
|
||||
font-weight:bold;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
padding: 1rem;
|
||||
border-top-left-radius: 7px;
|
||||
border-bottom-left-radius: 7px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-ref="totals_table-outstanding"] {
|
||||
background-color: var(--secondary-color);
|
||||
color: white;
|
||||
font-size:120%;
|
||||
font-weight:bold;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
padding: 1rem;
|
||||
border-top-right-radius: 7px;
|
||||
border-bottom-right-radius: 7px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-ref="statement-totals"] {
|
||||
|
|
@ -286,23 +278,26 @@
|
|||
.repeating-footer-space {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.repeating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.repeating-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
[data-element='product_table-product.description-td'], td {
|
||||
min-width:100%;
|
||||
[data-element='product_table-product.description-td'],
|
||||
td {
|
||||
min-width: 100%;
|
||||
max-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -313,25 +308,25 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
margin-top: 0.1em;
|
||||
|
|
@ -341,14 +336,12 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -383,57 +376,57 @@
|
|||
</style>
|
||||
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-container">
|
||||
<img
|
||||
src="$company.logo"
|
||||
class="company-logo"
|
||||
alt="$company.name logo"
|
||||
/>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="client-and-entity-wrapper">
|
||||
<div id="client-details"><p class="entity-issued-to">$entity_issued_to_label:</p></div>
|
||||
<div id="vendor-details"></div>
|
||||
<div id="shipping-details"></div>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-container">
|
||||
<div class="company-logo-container">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
</div>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="client-and-entity-wrapper">
|
||||
<div id="client-details">
|
||||
<p class="entity-issued-to">$entity_issued_to_label</p>
|
||||
</div>
|
||||
<div id="vendor-details"></div>
|
||||
<div id="shipping-details"></div>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
|
@ -443,4 +436,4 @@ $entity_images
|
|||
<div class="repeating-footerx" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -42,7 +42,6 @@
|
|||
.header-wrapper {
|
||||
display: grid;
|
||||
margin-top: 2rem;
|
||||
gap: 20px;
|
||||
grid-template-columns: 2fr 1fr 1fr;
|
||||
grid-template-areas: "a b c";
|
||||
grid-auto-columns: minmax(0, 5fr);
|
||||
|
|
@ -53,16 +52,20 @@
|
|||
.header-wrapper2 {
|
||||
display: grid;
|
||||
margin-top: 2rem;
|
||||
gap: 20px;
|
||||
grid-template-columns: 2fr 2fr auto;
|
||||
grid-template-areas: "a b c";
|
||||
grid-auto-columns: minmax(0, 5fr);
|
||||
grid-auto-flow: column;
|
||||
justify-content: left;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
|
||||
.header-wrapper2 .entity-container {
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
/* max-width: 65%;*/
|
||||
max-width: $company_logo_size;
|
||||
}
|
||||
|
||||
|
|
@ -87,16 +90,17 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.header-wrapper #company-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.header-wrapper #entity-details {
|
||||
padding-right: 0.5rem;
|
||||
text-align: left;
|
||||
line-height: var(--line-height);
|
||||
width: 100%;
|
||||
|
|
@ -113,6 +117,10 @@
|
|||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.entity-label {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
#entity-details {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
|
|
@ -121,7 +129,7 @@
|
|||
#entity-details th {
|
||||
font-weight: normal;
|
||||
line-height: 1.5rem;
|
||||
padding-right: 2rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
#client-details {
|
||||
|
|
@ -166,7 +174,7 @@
|
|||
td.left-radius {
|
||||
padding-left: 1rem !important;
|
||||
}
|
||||
|
||||
|
||||
th.right-radius {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
|
@ -201,6 +209,7 @@
|
|||
text-align: left;
|
||||
margin-top: .25rem;
|
||||
padding-left: 7px;
|
||||
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
|
|
@ -211,7 +220,7 @@
|
|||
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(2) {
|
||||
text-align: right;
|
||||
padding-right: 0px;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
#table-totals>* [data-element='total-table-balance-due-label'],
|
||||
|
|
@ -225,7 +234,6 @@
|
|||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -350,10 +358,10 @@
|
|||
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
||||
/** Hide company logo **/
|
||||
|
|
@ -405,7 +413,7 @@
|
|||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
<div id="shipping-details"></div>
|
||||
<div>
|
||||
<div class="entity-container">
|
||||
<p class="entity-label"
|
||||
style="font-size:32px; font-weight: bold; color:$primary_color;">$entity_label</p>
|
||||
<table id="entity-details" cellspacing="0" cellpadding="0" dir="ltr"></table>
|
||||
|
|
@ -443,6 +451,6 @@
|
|||
<div id="footer" style="">
|
||||
<div style="width: 100%;">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -40,19 +40,21 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
justify-content: space-between;
|
||||
width:100%;
|
||||
grid-template-columns: minmax(0, 1.5fr) auto minmax(0, 1fr);
|
||||
align-items: start;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.company-logo-container {
|
||||
display: inline-block;
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
|
|
@ -60,6 +62,8 @@
|
|||
}
|
||||
|
||||
#company-details {
|
||||
justify-self: center;
|
||||
align-self: start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
|
|
@ -70,6 +74,8 @@
|
|||
}
|
||||
|
||||
#company-address {
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
|
|
@ -85,9 +91,11 @@
|
|||
}
|
||||
|
||||
.client-and-entity-wrapper {
|
||||
padding: 1rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-columns: minmax(0, 2fr) auto minmax(0, 1fr);
|
||||
align-items: start;
|
||||
border-top: 1px solid #d8d8d8;
|
||||
border-bottom: 1px solid #d8d8d8;
|
||||
}
|
||||
|
|
@ -97,6 +105,9 @@
|
|||
text-align: left;
|
||||
margin-right: 20px;
|
||||
line-height: var(--line-height) !important;
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
#entity-details>tr,
|
||||
|
|
@ -110,7 +121,8 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
padding-right: 30px;
|
||||
justify-self: center;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
#client-details> :first-child {
|
||||
|
|
@ -121,6 +133,9 @@
|
|||
display: $show_shipping_address;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height);
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
|
|
@ -153,7 +168,7 @@
|
|||
padding: 1rem 1rem;
|
||||
}
|
||||
|
||||
th.right-radius {
|
||||
th.right-radius {
|
||||
padding-right: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
|
@ -162,10 +177,10 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-td']{
|
||||
[data-ref='product_table-product.item-td'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
|
||||
[data-ref="table"]>tbody>tr:nth-child(odd) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
|
@ -176,9 +191,8 @@
|
|||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: 0rem;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
gap: 80px;
|
||||
page-break-inside: avoid;
|
||||
page-break-inside: auto;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
|
|
@ -202,15 +216,6 @@
|
|||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
#table-totals>* [data-element='product-table-balance-due-label'],
|
||||
#table-totals>* [data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#table-totals>* [data-element='product-table-balance-due'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 0.5rem;
|
||||
|
|
@ -218,7 +223,6 @@
|
|||
|
||||
#footer {
|
||||
margin-top: 10px;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
/** Markdown-specific styles. **/
|
||||
|
|
@ -260,7 +264,8 @@
|
|||
bottom: 0;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.description-td'], td {
|
||||
[data-ref='product_table-product.description-td'],
|
||||
td {
|
||||
min-width: 100%;
|
||||
max-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
|
|
@ -363,7 +368,7 @@
|
|||
</div>
|
||||
<p class="entity-label">$entity_label</p>
|
||||
<div class="client-and-entity-wrapper">
|
||||
<div>
|
||||
<div class="entity-details-container">
|
||||
<table id="entity-details" cellspacing="0" cellpadding="0" dir="$dir"></table>
|
||||
</div>
|
||||
<div id="client-details"></div>
|
||||
|
|
@ -381,10 +386,15 @@
|
|||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
@ -21,7 +21,9 @@
|
|||
}
|
||||
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
|
|
@ -40,35 +42,35 @@
|
|||
|
||||
.header-wrapper {
|
||||
display: grid;
|
||||
grid-template-rows:0.5fr;
|
||||
grid-template-columns: auto auto auto auto;
|
||||
grid-template-rows: 0.5fr;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
grid-template-areas: "a b c d e";
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
grid-auto-flow: column;
|
||||
justify-content:left;
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
gap: 0px;
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-width: $company_logo_size;
|
||||
float:right;
|
||||
max-width: $company_logo_size;
|
||||
float: right;
|
||||
}
|
||||
|
||||
,logo-wrapper {
|
||||
grid-area: e;
|
||||
align-content: right;
|
||||
border:1px solid #000;
|
||||
.logo-wrapper {
|
||||
grid-area: e;
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
#entity-details {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
margin-right: 3rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#entity-details p {
|
||||
margin-top: 5px;
|
||||
#entity-details p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.header-wrapper #client-details,
|
||||
|
|
@ -84,51 +86,72 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-wrapper #client-details > *:first-child {
|
||||
.header-wrapper #client-details>*:first-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-wrapper .company-info-wrapper > * {
|
||||
.header-wrapper .company-info-wrapper>* {
|
||||
margin-bottom: 1rem;
|
||||
grid-row-end: 4;
|
||||
}
|
||||
|
||||
.entity-label-wrapper {
|
||||
/* display: flex; */
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
box-sizing: border-box;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
}
|
||||
|
||||
.entity-label-wrapper .entity-label > * {
|
||||
.entity-label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.entity-label h4 {
|
||||
margin-top: 0rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.entity-details-wrapper {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.entity-label-wrapper .entity-label>* {
|
||||
font-size: 3rem;
|
||||
|
||||
}
|
||||
|
||||
.entity-label-wrapper .entity-label > *:first-child {
|
||||
.entity-label-wrapper .entity-label>*:first-child {
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.entity-label-wrapper .entity-label > *:last-child {
|
||||
.entity-label-wrapper .entity-label>*:last-child {
|
||||
color: var(--primary-color);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.entity-label-wrapper #entity-details {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#shipping-details {
|
||||
display: $show_shipping_address;
|
||||
flex-direction: column;
|
||||
line-height: var(--line-height) !important;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.entity-label-wrapper #entity-details > tr,
|
||||
.entity-label-wrapper #entity-details>tr,
|
||||
.entity-label-wrapper #entity-details th {
|
||||
font-weight: normal;
|
||||
line-height: var(--line-height);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
|
@ -136,7 +159,7 @@
|
|||
|
||||
[data-ref="table"]:not(:empty) {
|
||||
border-top: 5px solid var(--primary-color);
|
||||
margin-top: 3rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.task-time-details {
|
||||
|
|
@ -152,15 +175,15 @@
|
|||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-td']{
|
||||
[data-ref='product_table-product.item-td'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
font-size: 1.1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
|
@ -173,11 +196,11 @@
|
|||
text-align: right !important;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr:nth-child(odd) {
|
||||
[data-ref="table"]>tbody>tr:nth-child(odd) {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
|
|
@ -187,8 +210,7 @@
|
|||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: 1rem;
|
||||
margin-right: .75rem;
|
||||
gap: 80px;
|
||||
page-break-inside:auto;
|
||||
page-break-inside: auto;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +224,7 @@
|
|||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
|
|
@ -212,25 +234,15 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
#table-totals>*>* {}
|
||||
|
||||
#table-totals > * > * {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
padding-left: 0.5rem;
|
||||
padding-right:0.8rem;
|
||||
padding-right: 0.8rem;
|
||||
}
|
||||
|
||||
#footer {
|
||||
|
|
@ -259,23 +271,26 @@
|
|||
.repeating-footer-space {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.repeating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.repeating-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
[data-element='product_table-product.description-td'], td {
|
||||
min-width:100%;
|
||||
|
||||
[data-element='product_table-product.description-td'],
|
||||
td {
|
||||
min-width: 100%;
|
||||
max-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -286,25 +301,25 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
|
|
@ -315,14 +330,12 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
|
@ -356,66 +369,62 @@
|
|||
</style>
|
||||
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-wrapper">
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
<div id="shipping-details"></div>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-wrapper">
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
<div id="shipping-details"></div>
|
||||
|
||||
<div class="company-info-wrapper">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="logo-wrapper">
|
||||
<img
|
||||
class="company-logo"
|
||||
src="$company.logo"
|
||||
alt="$company.name logo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entity-label-wrapper">
|
||||
<h1 class="entity-label">
|
||||
<span>$entity_label</span>
|
||||
<span>#$entity_number</span>
|
||||
</h1>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||
<div class="company-info-wrapper">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="logo-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<div class="entity-label-wrapper">
|
||||
<div class="entity-label">
|
||||
<h4>$entity_label</h4>
|
||||
<h4>#$entity_number</h4>
|
||||
</div>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
|
@ -424,6 +433,6 @@
|
|||
$entity_images
|
||||
|
||||
<div class="repeating-footerx" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
}
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
|
@ -20,7 +20,9 @@
|
|||
zoom: 80%;
|
||||
}
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +37,7 @@
|
|||
}
|
||||
|
||||
.company-logo {
|
||||
/* max-width: 55%;*/
|
||||
/* max-width: 55%;*/
|
||||
max-width: $company_logo_size;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
|
@ -47,15 +49,13 @@
|
|||
border-bottom: 4px solid;
|
||||
}
|
||||
|
||||
.company-logo-wrapper {
|
||||
|
||||
}
|
||||
.company-logo-wrapper {}
|
||||
|
||||
.right-radius {
|
||||
padding-right: 1rem;
|
||||
text-align:right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
.client-entity-wrapper {
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
|
|
@ -74,19 +74,19 @@
|
|||
table-layout: fixed;
|
||||
}
|
||||
|
||||
#entity-details p {
|
||||
#entity-details p {
|
||||
margin-right: 0px;
|
||||
margin-top: 0px;
|
||||
margin-top: 0px;
|
||||
white-space: nowrap;
|
||||
line-height: var(--line-height) !important;
|
||||
}
|
||||
|
||||
|
||||
.client-entity-wrapper .wrapper-info-text {
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
|
||||
.client-entity-wrapper .shipping-info-text {
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
|
|
@ -94,7 +94,10 @@
|
|||
display: $show_shipping_address;
|
||||
}
|
||||
|
||||
.wrapper-right-side {
|
||||
.wrapper-right-side {}
|
||||
|
||||
.text-with-client {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.client-entity-wrapper .wrapper-left-side #client-details,
|
||||
|
|
@ -122,17 +125,16 @@
|
|||
line-height: var(--line-height) !important;
|
||||
}
|
||||
|
||||
.client-entity-wrapper #entity-details > tr,
|
||||
.client-entity-wrapper #entity-details>tr,
|
||||
.client-entity-wrapper #entity-details th {
|
||||
font-weight: normal;
|
||||
padding-right:8px;
|
||||
padding-right: 8px;
|
||||
line-height: var(--line-height) !important;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 5
|
||||
px;
|
||||
margin-bottom: 5px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
|
@ -144,11 +146,11 @@
|
|||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
font-size: 1.1rem;
|
||||
padding-bottom: 1.5rem;
|
||||
padding-left: 1rem;
|
||||
|
|
@ -156,22 +158,22 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th:last-child {
|
||||
[data-ref="table"]>thead>tr>th:last-child {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
border-bottom: 1pt solid;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr:first-child > td {
|
||||
[data-ref="table"]>tbody>tr:first-child>td {
|
||||
border-top: 1pt solid !important;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td:last-child {
|
||||
[data-ref="table"]>tbody>tr>td:last-child {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
|
@ -185,10 +187,8 @@
|
|||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
margin-right: .75rem;
|
||||
gap: 80px;
|
||||
page-break-inside:auto;
|
||||
page-break-inside: auto;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
|
|
@ -210,31 +210,15 @@
|
|||
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(2) {
|
||||
text-align: right;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due-label'],
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 0.5rem;
|
||||
margin-right: .75rem;
|
||||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
padding-left: 1rem
|
||||
}
|
||||
[data-ref="total_table-footer"] {}
|
||||
|
||||
#footer {
|
||||
margin-top: 30px;
|
||||
|
|
@ -262,27 +246,31 @@
|
|||
.repeating-footer-space {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.repeating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.repeating-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.description-td'], td {
|
||||
min-width:100%;
|
||||
[data-ref='product_table-product.description-td'],
|
||||
td {
|
||||
min-width: 100%;
|
||||
max-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.description-th'] {
|
||||
overflow-wrap: break-word;
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -293,25 +281,25 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
|
|
@ -322,14 +310,12 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
|
@ -363,66 +349,66 @@
|
|||
</style>
|
||||
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="company-logo-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
</div>
|
||||
<hr class="double-border">
|
||||
<div class="client-entity-wrapper" style="">
|
||||
<div class="wrapper-left-side">
|
||||
<div class="text-with-client">
|
||||
<h2 class="wrapper-info-text">$to_label</h2>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
<div class="shipping-info" style="">
|
||||
<h2 class="shipping-info-text">$shipping_label</h2>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
<div class="company-info" style="">
|
||||
<h2 class="wrapper-info-text">$from_label</h2>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="wrapper-right-side">
|
||||
<h2 class="wrapper-info-text">$details_label</h2>
|
||||
<table id="entity-details" cellspacing="0" cellpadding="0" dir="$dir"></table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="company-logo-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
</div>
|
||||
<hr class="double-border">
|
||||
<div class="client-entity-wrapper" style="">
|
||||
<div class="wrapper-left-side">
|
||||
<div class="text-with-client">
|
||||
<h2 class="wrapper-info-text">$to_label</h2>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
<div class="shipping-info" style="">
|
||||
<h2 class="shipping-info-text">$shipping_label</h2>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
<div class="company-info" style="">
|
||||
<h2 class="wrapper-info-text">$from_label</h2>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="wrapper-right-side">
|
||||
<h2 class="wrapper-info-text">$details_label</h2>
|
||||
<table id="entity-details" cellspacing="0" cellpadding="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
|
@ -431,8 +417,6 @@
|
|||
$entity_images
|
||||
|
||||
<div class="repeating-footerx" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
</div>
|
||||
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
</div>
|
||||
|
|
@ -4,12 +4,12 @@
|
|||
:root {
|
||||
--primary-color: $primary_color;
|
||||
--secondary-color: $secondary_color;
|
||||
--line-height: 1.6;
|
||||
--line-height: 1.6;
|
||||
}
|
||||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
@ -20,7 +20,9 @@
|
|||
zoom: 80%;
|
||||
}
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +39,7 @@
|
|||
.header-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 0.5fr 1.5fr;
|
||||
gap: 20px;
|
||||
gap: 0px;
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +57,7 @@
|
|||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.header-wrapper .header-left-side-wrapper > * {
|
||||
.header-wrapper .header-left-side-wrapper>* {
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +65,7 @@
|
|||
.header-wrapper .header-left-side-wrapper #company-address {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.header-wrapper .header-right-side-wrapper {
|
||||
|
|
@ -75,20 +78,37 @@
|
|||
grid-template-areas: "a b c";
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
grid-auto-flow: column;
|
||||
justify-content:left;
|
||||
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.header-wrapper .header-right-side-wrapper #client-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 0.8rem;
|
||||
grid-area: a;
|
||||
justify-self: left !important;
|
||||
align-self: start !important;
|
||||
}
|
||||
|
||||
.header-wrapper .header-right-side-wrapper #shipping-details {
|
||||
display: $show_shipping_address;
|
||||
display: $show_shipping_address_block;
|
||||
flex-direction: column;
|
||||
margin-top: 0.8rem;
|
||||
grid-area: b;
|
||||
}
|
||||
|
||||
.header-wrapper .header-right-side-wrapper-left-shipping {
|
||||
padding-left: 1rem;
|
||||
border-left: 1px solid #303030;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header-wrapper .header-right-side-wrapper-right {
|
||||
grid-area: c;
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
justify-content: end;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.shipping-text-label {
|
||||
|
|
@ -105,6 +125,8 @@
|
|||
|
||||
.company-logo {
|
||||
max-width: $company_logo_size;
|
||||
float: right;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.entity-label {
|
||||
|
|
@ -113,7 +135,7 @@
|
|||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.entity-details-wrapper > * {
|
||||
.entity-details-wrapper>* {
|
||||
margin-right: 1.5rem;
|
||||
direction: $dir;
|
||||
}
|
||||
|
|
@ -129,10 +151,12 @@
|
|||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.entity-details-wrapper
|
||||
[data-element='entity-details-wrapper-invoice-number-label'],
|
||||
.entity-details-wrapper
|
||||
[data-element='entity-details-wrapper-amount-due'] {
|
||||
.entity-property-label:not(:empty)::after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
.entity-details-wrapper [data-element='entity-details-wrapper-invoice-number-label'],
|
||||
.entity-details-wrapper [data-element='entity-details-wrapper-amount-due'] {
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
@ -142,12 +166,12 @@
|
|||
}
|
||||
|
||||
td.left-radius {
|
||||
text-align:left !important;
|
||||
text-align: left !important;
|
||||
padding-left: 1rem !important;
|
||||
}
|
||||
|
||||
th.left-radius {
|
||||
text-align:left !important;
|
||||
text-align: left !important;
|
||||
padding-left: 0.5rem !important;
|
||||
}
|
||||
|
||||
|
|
@ -165,25 +189,25 @@
|
|||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
font-size: 1.1rem;
|
||||
padding-bottom: 1.5rem;
|
||||
padding-left: 1rem;
|
||||
border-left: 1px solid;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th:nth-last-child(2) {
|
||||
[data-ref="table"]>thead>tr>th:nth-last-child(2) {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
padding-left: 1rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
|
|
@ -193,15 +217,14 @@
|
|||
.right-radius {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
#table-totals {
|
||||
margin-top: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
gap: 80px;
|
||||
page-break-inside:auto;
|
||||
margin-right: 0rem;
|
||||
page-break-inside: auto;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +238,7 @@
|
|||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
|
|
@ -225,22 +248,7 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due-label'],
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 0rem;
|
||||
}
|
||||
|
|
@ -257,29 +265,27 @@
|
|||
|
||||
[data-ref='task_table-task.service-th'],
|
||||
[data-ref='product_table-product.item-th'] {
|
||||
padding-left:1rem !important;
|
||||
width:14%;
|
||||
}
|
||||
padding-left: 1rem !important;
|
||||
width: 14%;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.description-th'],
|
||||
[data-ref='product_table-product.description-th'],
|
||||
[data-ref='product_table-product.description-td'],
|
||||
[data-ref="task_table-task.description-td"] {
|
||||
min-width:100px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.description-th'] {
|
||||
overflow-wrap: break-word;
|
||||
min-width: 100px !important;
|
||||
;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref="product_table-product.unit_cost-td"],
|
||||
[data-ref="product_table-product.unit_cost-th"],
|
||||
[data-ref='task_table-task.cost-th'],
|
||||
[data-ref='task_table-task.cost-td']{
|
||||
text-align: center !important;
|
||||
[data-ref='task_table-task.cost-td'] {
|
||||
text-align: center !important;
|
||||
width: 10%;
|
||||
padding-left:0 !important;
|
||||
padding-right:0 !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
[data-ref="product_table-product.quantity-th"],
|
||||
|
|
@ -287,19 +293,27 @@
|
|||
[data-ref='task_table-task.hours-th'],
|
||||
[data-ref='task_table-task.hours-td'] {
|
||||
width: 10%;
|
||||
text-align: center !important;
|
||||
padding-left:0 !important;
|
||||
padding-right:0 !important;
|
||||
text-align: center !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
[data-ref="task_table-task.discount-th"],
|
||||
[data-ref="task_table-task.discount-td"],
|
||||
[data-ref="product_table-product.discount-th"],
|
||||
[data-ref="product_table-product.discount-td"] {
|
||||
text-align: left !important;
|
||||
width: 9%;
|
||||
}
|
||||
|
||||
[data-ref="task_table-task.tax1-th"],
|
||||
[data-ref="task_table-task.tax1-td"],
|
||||
[data-ref="product_table-product.tax1-th"],
|
||||
[data-ref="product_table-product.tax1-td"] {
|
||||
text-align: center;
|
||||
text-align: center !important;
|
||||
width: 9%;
|
||||
padding-left:0 !important;
|
||||
padding-right:0 !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
[data-ref="product_table-product.line_total-th"],
|
||||
|
|
@ -308,24 +322,21 @@
|
|||
width: 13%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[data-ref="task_table-task.line_total-th"],
|
||||
[data-ref="task_table-task.line_total-th"],
|
||||
[data-ref="task_table-task.line_total-td"] {
|
||||
text-align: right;
|
||||
width: 13%;
|
||||
}
|
||||
|
||||
[data-ref="totals_table-outstanding-label"] {
|
||||
font-weight:bold;
|
||||
font-size:120%;
|
||||
[data-ref="totals_table-outstanding-label"] {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
[data-ref="totals_table-outstanding"] {
|
||||
color: var(--primary-color);
|
||||
font-weight:bold;
|
||||
font-size:120%;
|
||||
[data-ref="totals_table-outstanding"] {
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
[data-ref="statement-totals"] {
|
||||
|
|
@ -344,17 +355,19 @@
|
|||
.repeating-footer-space {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.repeating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.repeating-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -365,24 +378,24 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
|
|
@ -393,13 +406,12 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
|
@ -433,90 +445,84 @@
|
|||
</style>
|
||||
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-wrapper">
|
||||
<div class="header-left-side-wrapper">
|
||||
<p class="header-text-label">$from_label:</p>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="header-right-side-wrapper">
|
||||
<div class="header-right-side-wrapper-left">
|
||||
<p class="header-text-label">$to_label:</p>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
<div class="header-right-side-wrapper-left">
|
||||
<p class="shipping-text-label">$shipping_label:</p>
|
||||
<div id="shipping-details"></div>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-wrapper">
|
||||
<div class="header-left-side-wrapper">
|
||||
<p class="header-text-label">$from_label:</p>
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="header-right-side-wrapper">
|
||||
<div class="header-right-side-wrapper-left">
|
||||
<p class="header-text-label">$to_label:</p>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
<div class="header-right-side-wrapper-left-shipping">
|
||||
<p class="shipping-text-label">$shipping_label:</p>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
<div class="header-right-side-wrapper-right">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right-side-wrapper-right">
|
||||
<img
|
||||
class="company-logo"
|
||||
src="$company.logo"
|
||||
alt="$company.name logo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="entity-label">$entity_label</h1>
|
||||
<div class="entity-details-wrapper">
|
||||
<div>
|
||||
<span class="entity-property-label" data-element="entity-details-wrapper-invoice-number-label">
|
||||
$entity_number_label:
|
||||
</span>
|
||||
<span class="entity-property-value">$entity_number</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="entity-property-label">$date_label:</span>
|
||||
<span class="entity-property-value">$date</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="entity-property-label">$payment_due_label:</span>
|
||||
<span class="entity-property-value">$payment_due</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="entity-property-label">$amount_due_label:</span>
|
||||
<span
|
||||
class="entity-property-value"
|
||||
data-element="entity-details-wrapper-amount-due"
|
||||
>$amount_due</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<h1 class="entity-label">$entity_label</h1>
|
||||
<div class="entity-details-wrapper">
|
||||
<div>
|
||||
<span class="entity-property-label"
|
||||
data-element="entity-details-wrapper-invoice-number-label">
|
||||
$entity_number_label
|
||||
</span>
|
||||
<span class="entity-property-value">$entity_number</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="entity-property-label">$date_label</span>
|
||||
<span class="entity-property-value">$date</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="entity-property-label">$payment_due_label</span>
|
||||
<span class="entity-property-value">$payment_due</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="entity-property-label">$amount_due_label</span>
|
||||
<span class="entity-property-value" data-element="entity-details-wrapper-amount-due"
|
||||
style="float:right;">$amount_due</span>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
|
@ -525,8 +531,7 @@
|
|||
$entity_images
|
||||
|
||||
<div class="repeating-footerx" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -1,488 +1,502 @@
|
|||
<style id="style">
|
||||
@import url($font_url);
|
||||
|
||||
:root {
|
||||
--primary-color: $primary_color;
|
||||
--secondary-color: $secondary_color;
|
||||
}
|
||||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: $font_name, Helvetica, sans-serif;
|
||||
font-size: $font_size !important;
|
||||
zoom: 80%;
|
||||
}
|
||||
|
||||
table tr td, table tr, th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@page {
|
||||
margin: 0 !important;
|
||||
size: $page_size $page_layout;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#spacer-table > * > tr > td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#spacer-table{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr;
|
||||
position:fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#header, #header-spacer {
|
||||
height: 160px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
text-align: left;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
#header .company-name {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
#entity-details {
|
||||
text-align: left;
|
||||
color: #fff4e9 !important;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
#entity-details > tr,
|
||||
#entity-details th {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.logo-client-wrapper {
|
||||
margin-left: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-width: $company_logo_size;
|
||||
}
|
||||
|
||||
#client-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
|
||||
}
|
||||
|
||||
#shipping-details {
|
||||
display: $show_shipping_address;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#client-details > * {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin: 0rem 2rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.task-time-details {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-ref="table"] th + th {
|
||||
border-left: 2px solid white;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
padding: 0.8rem;
|
||||
background-color: var(--secondary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.right-radius {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
border-bottom: 1px solid var(--secondary-color);
|
||||
padding: 0.8rem;
|
||||
}
|
||||
|
||||
td.left-radius {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
#footer {
|
||||
background-color: var(--primary-color);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
|
||||
#footer, #footer-spacer {
|
||||
height: 220px;
|
||||
padding: 0rem 0rem;
|
||||
margin-top: 0rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
width: calc(100% - 2rem);
|
||||
margin: 0 1rem;
|
||||
color: #fff4e9;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* Main footer text area */
|
||||
.footer-content > div:first-child {
|
||||
width: 50%;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
/* Company details/address wrapper */
|
||||
.footer-company-details-address-wrapper {
|
||||
width: 50%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
#company-details,
|
||||
#company-address {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#company-address > *,
|
||||
#company-details > * {
|
||||
}
|
||||
|
||||
#table-totals {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
#table-totals {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 2.5rem;
|
||||
padding-right: 3rem;
|
||||
gap: 80px;
|
||||
}
|
||||
|
||||
#table-totals .totals-table-right-side > * {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
#table-totals > .totals-table-right-side > * > :nth-child(1) {
|
||||
text-align: "left";
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> .totals-table-right-side
|
||||
> *
|
||||
> :not([hidden])
|
||||
~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
#table-totals > .totals-table-right-side > * > :nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#table-totals > * [data-element="product-table-balance-due-label"],
|
||||
#table-totals > * [data-element="product-table-balance-due"] {
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table[data-ref="table"] th,
|
||||
table[data-ref="table"] td {
|
||||
padding: 0.2rem 0.4rem;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
/** Markdown-specific styles. **/
|
||||
#product-table h3,
|
||||
#task-table h3,
|
||||
#delivery-note-table h3 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
[data-ref="statement-totals"] {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: right;
|
||||
margin-right: .75rem;
|
||||
}
|
||||
|
||||
[data-ref*=".line_total-td"] {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
[data-ref='product_table-product.description-th'] {
|
||||
min-width: 100px !important;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-td']{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-th']{
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.unit_cost-th'],
|
||||
[data-ref='product_table-product.quantity-th'],
|
||||
[data-ref='product_table-product.product1-th'],
|
||||
[data-ref='product_table-product.product2-th'],
|
||||
[data-ref='product_table-product.product3-th'],
|
||||
[data-ref='product_table-product.product4-th'],
|
||||
[data-ref='task_table-task.hours-th'],
|
||||
[data-ref='task_table-task.discount-th'],
|
||||
[data-ref='task_table-task.cost-th'],
|
||||
[data-ref='task_table-task.quantity-th'],
|
||||
[data-ref='task_table-task.task1-th'],
|
||||
[data-ref='task_table-task.task2-th'],
|
||||
[data-ref='task_table-task.task3-th'],
|
||||
[data-ref='task_table-task.task4-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.tax1-th'],
|
||||
[data-ref='task_table-task.tax1-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.line_total-th'],
|
||||
[data-ref='product_table-product.line_total-td'],
|
||||
[data-ref='task_table-task.line_total-th'],
|
||||
[data-ref='task_table-task.line_total-td'] {
|
||||
width: 12%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.description-th'] {
|
||||
overflow-wrap: break-word;
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.service-th']{
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
border: 0.25rem solid #555;
|
||||
display: inline-block;
|
||||
padding: 0.25rem 1rem;
|
||||
text-transform: uppercase;
|
||||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
}
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
||||
/** Hide company logo **/
|
||||
/* .company-logo { display: none } */
|
||||
|
||||
/* Hide company details */
|
||||
/* #company-details > * { display: none } */
|
||||
|
||||
/* Hide company address */
|
||||
/* #company-address > * { display: none } */
|
||||
|
||||
/* Hide public notes */
|
||||
/* [data-ref="total_table-public_notes"] { display: none } */
|
||||
|
||||
/* Hide terms label */
|
||||
/* [data-ref="total_table-terms-label"] { display: none } */
|
||||
|
||||
/* Hide totals table */
|
||||
/* #table-totals { display: none } */
|
||||
|
||||
/* Hide totals table left side */
|
||||
/* #table-totals div:first-child > * { display: none !important } */
|
||||
|
||||
/* Hide totals table right side */
|
||||
/* .totals-table-right-side { display: none } */
|
||||
|
||||
/** For more info, please check our docs: https://invoiceninja.github.io **/
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
@import url($font_url);
|
||||
|
||||
:root {
|
||||
--primary-color: $primary_color;
|
||||
--secondary-color: $secondary_color;
|
||||
}
|
||||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: $font_name, Helvetica, sans-serif;
|
||||
font-size: $font_size !important;
|
||||
zoom: 80%;
|
||||
}
|
||||
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@page {
|
||||
margin: 0 !important;
|
||||
size: $page_size $page_layout;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#spacer-table>*>tr>td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#spacer-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#header,
|
||||
#header-spacer {
|
||||
height: 160px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
text-align: left;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
#header .company-name {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
#entity-details {
|
||||
margin-top: 1rem;
|
||||
text-align: left;
|
||||
color: #fff4e9 !important;
|
||||
line-height: 1.6 !important;
|
||||
}
|
||||
|
||||
#entity-details>tr,
|
||||
#entity-details th {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.logo-client-wrapper {
|
||||
margin-left: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr auto auto;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-width: $company_logo_size;
|
||||
}
|
||||
|
||||
#client-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
margin-right: 2rem;
|
||||
float: right;
|
||||
|
||||
}
|
||||
|
||||
#shipping-details {
|
||||
display: $show_shipping_address;
|
||||
flex-direction: column;
|
||||
padding-left: 10px;
|
||||
padding-right: 2rem;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#shipping-details>* {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#client-details>* {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin: 0rem 2rem;
|
||||
}
|
||||
|
||||
[data-ref="delivery_note-client.city_state_postal"] p {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.task-time-details {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-ref="table"] th+th {
|
||||
border-left: 2px solid white;
|
||||
}
|
||||
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
padding: 0.8rem;
|
||||
background-color: var(--secondary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.right-radius {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
border-bottom: 1px solid var(--secondary-color);
|
||||
padding: 0.8rem;
|
||||
}
|
||||
|
||||
td.left-radius {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
#footer {
|
||||
background-color: var(--primary-color);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
|
||||
#footer,
|
||||
#footer-spacer {
|
||||
height: 220px;
|
||||
padding: 0rem 0rem;
|
||||
margin-top: 0rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
width: calc(100% - 2rem);
|
||||
margin: 0 1rem;
|
||||
color: #fff4e9;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* Main footer text area */
|
||||
.footer-content>div:first-child {
|
||||
width: 50%;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
/* Company details/address wrapper */
|
||||
.footer-company-details-address-wrapper {
|
||||
width: 50%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
#company-details,
|
||||
#company-address {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#company-address>*,
|
||||
#company-details>* {
|
||||
}
|
||||
|
||||
#table-totals {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
#table-totals {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 2rem;
|
||||
padding-right: 3rem;
|
||||
gap: 80px;
|
||||
}
|
||||
|
||||
#table-totals .totals-table-right-side>* {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(1) {
|
||||
text-align: "left";
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table[data-ref="table"] th,
|
||||
table[data-ref="table"] td {
|
||||
padding: 0.2rem 0.4rem;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
/** Markdown-specific styles. **/
|
||||
#product-table h3,
|
||||
#task-table h3,
|
||||
#delivery-note-table h3 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
[data-ref="statement-totals"] {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: right;
|
||||
margin-right: .75rem;
|
||||
}
|
||||
|
||||
[data-ref*=".line_total-td"] {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
[data-ref='product_table-product.description-th'],
|
||||
[data-ref='product_table-product.description-td'] {
|
||||
min-width: 150px !important;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-td'] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.unit_cost-th'],
|
||||
[data-ref='product_table-product.quantity-th'],
|
||||
[data-ref='product_table-product.product1-th'],
|
||||
[data-ref='product_table-product.product2-th'],
|
||||
[data-ref='product_table-product.product3-th'],
|
||||
[data-ref='product_table-product.product4-th'],
|
||||
[data-ref='task_table-task.hours-th'],
|
||||
[data-ref='task_table-task.cost-th'],
|
||||
[data-ref='task_table-task.quantity-th'],
|
||||
[data-ref='task_table-task.task1-th'],
|
||||
[data-ref='task_table-task.task2-th'],
|
||||
[data-ref='task_table-task.task3-th'],
|
||||
[data-ref='task_table-task.task4-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.tax1-th'],
|
||||
[data-ref='task_table-task.tax1-th'] {
|
||||
width: 7%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.discount-th'],
|
||||
[data-ref='task_table-task.discount-th'] {
|
||||
width: 8%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.line_total-th'],
|
||||
[data-ref='product_table-product.line_total-td'],
|
||||
[data-ref='task_table-task.line_total-th'],
|
||||
[data-ref='task_table-task.line_total-td'] {
|
||||
width: 12%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.description-th'] {
|
||||
overflow-wrap: break-word;
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.service-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
border: 0.25rem solid #555;
|
||||
display: inline-block;
|
||||
padding: 0.25rem 1rem;
|
||||
text-transform: uppercase;
|
||||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
||||
/** Hide company logo **/
|
||||
/* .company-logo { display: none } */
|
||||
|
||||
/* Hide company details */
|
||||
/* #company-details > * { display: none } */
|
||||
|
||||
/* Hide company address */
|
||||
/* #company-address > * { display: none } */
|
||||
|
||||
/* Hide public notes */
|
||||
/* [data-ref="total_table-public_notes"] { display: none } */
|
||||
|
||||
/* Hide terms label */
|
||||
/* [data-ref="total_table-terms-label"] { display: none } */
|
||||
|
||||
/* Hide totals table */
|
||||
/* #table-totals { display: none } */
|
||||
|
||||
/* Hide totals table left side */
|
||||
/* #table-totals div:first-child > * { display: none !important } */
|
||||
|
||||
/* Hide totals table right side */
|
||||
/* .totals-table-right-side { display: none } */
|
||||
|
||||
/** For more info, please check our docs: https://invoiceninja.github.io **/
|
||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
<div id="header">
|
||||
<div id="company-name-wrapper">
|
||||
<h1 class="company-name">$company.name</h1>
|
||||
</div>
|
||||
<div id="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" cellpadding="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body">
|
||||
<table id="spacer-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="header-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="logo-client-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo"/>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="spacer-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="header-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="logo-client-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo" />
|
||||
<div id="shipping-details"></div>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
</div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="footer-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
</div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="footer-spacer"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
$entity_images
|
||||
|
||||
<div id="footer">
|
||||
<div class="footer-content">
|
||||
<div class="footer-text">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
<div class="footer-company-details-address-wrapper">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
<div class="footer-content">
|
||||
<div class="footer-text">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-company-details-address-wrapper">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
.client-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
border: 0px solid #000;
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +64,8 @@
|
|||
|
||||
#entity-container {
|
||||
border: 0px solid #000;
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#entity-details {
|
||||
|
|
@ -72,8 +73,7 @@
|
|||
line-height: var(--line-height) !important;
|
||||
white-space: nowrap;
|
||||
border: 0px solid #000;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#entity-details>tr,
|
||||
|
|
@ -102,6 +102,8 @@
|
|||
line-height: var(--line-height);
|
||||
white-space: nowrap;
|
||||
border: 0px solid #000;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
|
|
@ -109,7 +111,6 @@
|
|||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.task-time-details {
|
||||
|
|
@ -144,8 +145,6 @@
|
|||
margin-top: 0rem;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
padding-top: .5rem;
|
||||
padding-left: .5rem;
|
||||
margin-right: .75rem;
|
||||
gap: 80px;
|
||||
page-break-inside: auto;
|
||||
|
|
@ -185,7 +184,7 @@
|
|||
}
|
||||
|
||||
[data-ref="total_table-footer"] {
|
||||
padding-left: 1rem
|
||||
padding-left: 0rem;
|
||||
}
|
||||
|
||||
#footer {
|
||||
|
|
@ -239,54 +238,11 @@
|
|||
}
|
||||
|
||||
[data-ref='product_table-product.description-td'],
|
||||
[data-ref='task_table-task.description-th']{
|
||||
min-width: 100px !important;
|
||||
[data-ref='task_table-task.description-th'] {
|
||||
min-width: 150px !important;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
[data-ref='product_table-product.item-th'],
|
||||
[data-ref='product_table-product.unit_cost-th'],
|
||||
[data-ref='task_table-task.service-th'],
|
||||
[data-ref='task_table-task.cost-th']{
|
||||
width: 12%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.quantity-th'],
|
||||
[data-ref='task_table-task.hours-th'],
|
||||
[data-ref='task_table-task.quantity-th'],
|
||||
[data-ref='product_table-product.product1-th'],
|
||||
[data-ref='product_table-product.product2-th'],
|
||||
[data-ref='product_table-product.product3-th'],
|
||||
[data-ref='product_table-product.product4-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.tax1-th'] {
|
||||
width: 6%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.line_total-th'],
|
||||
[data-ref='product_table-product.line_total-td'],
|
||||
[data-ref='task_table-task.line_total-th'],
|
||||
[data-ref='task_table-task.line_total-td'] {
|
||||
width: 12%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.discount-th'],
|
||||
[data-ref='task_table-task.task1-th'],
|
||||
[data-ref='task_table-task.task2-th'],
|
||||
[data-ref='task_table-task.task3-th'],
|
||||
[data-ref='task_table-task.task4-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[data-ref='task_table-task.tax1-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
|
|
@ -339,12 +295,12 @@
|
|||
|
||||
.pqrcode {}
|
||||
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,15 @@
|
|||
zoom: 80%;
|
||||
}
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
@page {
|
||||
|
|
@ -30,7 +32,6 @@
|
|||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
@ -47,10 +48,11 @@
|
|||
background-color: var(--primary-color);
|
||||
padding: 0.5rem;
|
||||
border-radius: 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
#entity-details p {
|
||||
margin-right: 20px;
|
||||
#entity-details p {
|
||||
margin-right: 20px;
|
||||
white-space: nowrap;
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.5rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
|
|
@ -64,25 +66,26 @@
|
|||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.header-wrapper #entity-details > tr,
|
||||
.header-wrapper #entity-details>tr,
|
||||
.header-wrapper #entity-details th {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
/* max-width: 65%;*/
|
||||
/* max-width: 65%;*/
|
||||
max-width: $company_logo_size;
|
||||
}
|
||||
|
||||
.contacts-wrapper {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
padding: 1rem 0rem 0rem 2rem;
|
||||
gap: 0px;
|
||||
padding: 1rem 0rem 0rem 1rem;
|
||||
grid-template-columns: 1fr 1fr auto;
|
||||
grid-template-areas: "a b c";
|
||||
grid-auto-columns: minmax(0, 5fr);
|
||||
grid-auto-flow: column;
|
||||
justify-content: space-between;
|
||||
margin-left: 1rem;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +118,7 @@
|
|||
|
||||
.contacts-wrapper .company-info {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--primary-color);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
|
@ -124,25 +127,37 @@
|
|||
|
||||
.contacts-wrapper #client-details {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.contacts-wrapper #vendor-details {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.contacts-wrapper #shipping-details {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
padding-right: 0rem;
|
||||
border-top: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
|
||||
.contact-wrapper-left-side,
|
||||
.contact-wrapper-left-side {
|
||||
border-bottom: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.contact-wrapper-right-side {
|
||||
border-bottom: 1px solid var(--primary-color);
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.shipping-wrapper-right-side {
|
||||
border-bottom: 1px solid var(--primary-color);
|
||||
display: $show_shipping_address_block;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
|
|
@ -161,11 +176,11 @@
|
|||
color: grey;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead {
|
||||
[data-ref="table"]>thead {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
font-size: 1.2rem;
|
||||
padding: 1rem;
|
||||
background: var(--primary-color);
|
||||
|
|
@ -185,17 +200,17 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
background-color: #F7F7F7;
|
||||
border-bottom: 1px solid var(--primary-color);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td.left-radius {
|
||||
[data-ref="table"]>tbody>tr>td.left-radius {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td.right-radius {
|
||||
[data-ref="table"]>tbody>tr>td.right-radius {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +235,7 @@
|
|||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
#table-totals>.totals-table-right-side> * > :not([hidden]) ~ :not([hidden]) {
|
||||
#table-totals>.totals-table-right-side>*> :not([hidden])~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
|
||||
|
|
@ -228,25 +243,6 @@
|
|||
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due-label'],
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='product-table-balance-due'] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +255,7 @@
|
|||
margin-left: -10px;
|
||||
}
|
||||
|
||||
#header > * {
|
||||
#header>* {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
|
@ -288,9 +284,9 @@
|
|||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
margin-left: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
#footer-colors > * {
|
||||
#footer-colors>* {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
|
@ -308,28 +304,31 @@
|
|||
.repeating-header,
|
||||
.repeating-header-space {
|
||||
height: 20px;
|
||||
page-break-inside:avoid;
|
||||
page-break-before:avoid;
|
||||
page-break-after:avoid;
|
||||
page-break-inside: avoid;
|
||||
page-break-before: avoid;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.repeating-footer,
|
||||
.repeating-footer-space {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.repeating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.repeating-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
[data-element='product_table-product.description-td'], td {
|
||||
min-width:100%;
|
||||
[data-element='product_table-product.description-td'],
|
||||
td {
|
||||
min-width: 100%;
|
||||
max-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref="shipping_address-label"] {
|
||||
|
|
@ -337,7 +336,7 @@
|
|||
}
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -348,25 +347,25 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
|
|
@ -377,16 +376,14 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
||||
/** Hide company logo **/
|
||||
|
|
@ -417,67 +414,67 @@
|
|||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||
</style>
|
||||
<div id="body">
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="header-wrapper">
|
||||
<div>
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
</div>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contacts-wrapper">
|
||||
<div class="contact-wrapper-left-side">
|
||||
<p class="contact-label">$from_label:</p>
|
||||
<div class="company-info">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="header-wrapper">
|
||||
<div>
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
</div>
|
||||
<div class="entity-details-wrapper">
|
||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-wrapper-right-side">
|
||||
<p class="contact-label">$to_label:</p>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
<div class="contacts-wrapper">
|
||||
<div class="contact-wrapper-left-side">
|
||||
<p class="contact-label">$from_label:</p>
|
||||
<div class="company-info">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-wrapper-right-side">
|
||||
<p class="contact-label">$to_label:</p>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
</div>
|
||||
|
||||
<div class="shipping-wrapper-right-side">
|
||||
<p class="shipping-label">$shipping_label:</p>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div class="shipping-wrapper-right-side">
|
||||
<p class="shipping-label">$shipping_label:</p>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
</div>
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="repeating-header">
|
||||
<div id="header">
|
||||
|
|
@ -494,7 +491,7 @@
|
|||
|
||||
$entity_images
|
||||
|
||||
<div class="repeating-footerx" id="footer">
|
||||
<div class="repeating-footerx" id="footer">
|
||||
<div data-ref="total_table-footer">$entity_footer</div>
|
||||
|
||||
<div id="footer-colors">
|
||||
|
|
@ -508,4 +505,4 @@ $entity_images
|
|||
<div style="background-color: #009B8F"><!-- 8 --></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
html {
|
||||
width: 210mm;
|
||||
height: 200mm;
|
||||
height: 200mm;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
@ -20,16 +20,19 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
table tr td, table tr, th {
|
||||
table tr td,
|
||||
table tr,
|
||||
th {
|
||||
font-size: $font_size !important;
|
||||
}
|
||||
|
||||
|
||||
@page {
|
||||
margin: 0;
|
||||
size: $page_size $page_layout;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
.header-wrapper,
|
||||
.body-wrapper {
|
||||
margin: $global_margin;
|
||||
|
|
@ -85,7 +88,8 @@
|
|||
.hero-section {
|
||||
margin-top: 5px;
|
||||
min-width: 100% !important;
|
||||
background: url('$tech_hero_image'); /** If you want to replace the image, this is the place to do it. */
|
||||
background: url('$tech_hero_image');
|
||||
/** If you want to replace the image, this is the place to do it. */
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
|
|
@ -163,7 +167,7 @@
|
|||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th {
|
||||
[data-ref="table"]>thead>tr>th {
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
padding: 1rem;
|
||||
|
|
@ -172,20 +176,20 @@
|
|||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > thead > tr > th:last-child {
|
||||
[data-ref="table"]>thead>tr>th:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td {
|
||||
[data-ref="table"]>tbody>tr>td {
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td:first-child {
|
||||
[data-ref="table"]>tbody>tr>td:first-child {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[data-ref="table"] > tbody > tr > td:last-child {
|
||||
[data-ref="table"]>tbody>tr>td:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +211,7 @@
|
|||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
#table-totals .totals-table-right-side > * {
|
||||
#table-totals .totals-table-right-side>* {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
|
@ -217,21 +221,17 @@
|
|||
padding: 7px;
|
||||
}
|
||||
|
||||
#table-totals > .totals-table-right-side > * > :nth-child(2) {
|
||||
#table-totals>.totals-table-right-side>*> :nth-child(2) {
|
||||
text-align: right;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='total-table-balance-due-label'],
|
||||
#table-totals
|
||||
> *
|
||||
[data-element='total-table-balance-due'] {
|
||||
#table-totals>* [data-element='total-table-balance-due-label'],
|
||||
#table-totals>* [data-element='total-table-balance-due'] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#table-totals > * > :last-child {
|
||||
#table-totals>*> :last-child {
|
||||
text-align: right;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
|
@ -267,10 +267,12 @@
|
|||
.repeating-footer-space {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.repeating-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.repeating-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
|
|
@ -278,63 +280,18 @@
|
|||
|
||||
|
||||
[data-ref='product_table-product.description-td'],
|
||||
[data-ref='task_table-task.description-td']{
|
||||
min-width: 100px !important;
|
||||
[data-ref='task_table-task.description-td'] {
|
||||
min-width: 150px !important;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.item-th'],
|
||||
[data-ref='task_table-task.service-th']{
|
||||
[data-ref='task_table-task.service-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.unit_cost-th'],
|
||||
[data-ref='task_table-task.cost-th'] {
|
||||
width: 13%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.quantity-th'],
|
||||
[data-ref='task_table-task.hours-th'],
|
||||
[data-ref='product_table-product.product1-th'],
|
||||
[data-ref='product_table-product.product2-th'],
|
||||
[data-ref='product_table-product.product3-th'],
|
||||
[data-ref='product_table-product.product4-th'] {
|
||||
width: 8%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.tax1-th'] {
|
||||
width: 6%;
|
||||
}
|
||||
|
||||
[data-ref='product_table-product.line_total-th'],
|
||||
[data-ref='product_table-product.line_total-td'] {
|
||||
width: 13%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.discount-th'],
|
||||
[data-ref='task_table-task.quantity-th'],
|
||||
[data-ref='task_table-task.task1-th'],
|
||||
[data-ref='task_table-task.task2-th'],
|
||||
[data-ref='task_table-task.task3-th'],
|
||||
[data-ref='task_table-task.task4-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
|
||||
[data-ref='task_table-task.tax1-th'] {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
[data-ref='task_table-task.line_total-th'],
|
||||
[data-ref='task_table-task.line_total-td'] {
|
||||
width: 13%;
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
|
||||
.stamp {
|
||||
transform: rotate(12deg);
|
||||
transform: rotate(12deg);
|
||||
color: #555;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
|
|
@ -345,25 +302,25 @@
|
|||
border-radius: 1rem;
|
||||
font-family: 'Courier';
|
||||
mix-blend-mode: multiply;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-paid {
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
color: #D23;
|
||||
border: 1rem double #D23;
|
||||
transform: rotate(-5deg);
|
||||
font-size: 6rem;
|
||||
font-family: "Open sans", Helvetica, Arial, sans-serif;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.2;
|
||||
z-index:200 !important;
|
||||
position: fixed;
|
||||
z-index: 200 !important;
|
||||
position: fixed;
|
||||
display: $show_paid_stamp;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
font-size: 1.2em;
|
||||
|
|
@ -374,16 +331,14 @@
|
|||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
.pqrcode {
|
||||
|
||||
}
|
||||
|
||||
#qr-bill{
|
||||
width:100% !important;
|
||||
.pqrcode {}
|
||||
|
||||
#qr-bill {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
/** Useful snippets, uncomment to enable. **/
|
||||
|
||||
/** Hide company logo **/
|
||||
|
|
@ -415,88 +370,88 @@
|
|||
</style>
|
||||
|
||||
<table style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-wrapper">
|
||||
<div class="logo-and-partial-entity-info">
|
||||
<div class="company-logo-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-header-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="body">
|
||||
<div class="header-wrapper">
|
||||
<div class="logo-and-partial-entity-info">
|
||||
<div class="company-logo-wrapper">
|
||||
<img class="company-logo" src="$company.logo" alt="$company.name logo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="top-right-side-section">
|
||||
<table style="white-space: nowrap;" cellspacing="10">
|
||||
<tr>
|
||||
<td>$entity_number_label:</td>
|
||||
<td class="header-invoice-number">$entity_number</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>$date_label </td>
|
||||
<td>$date</td>
|
||||
</tr>
|
||||
<tr class="header-payment-due-label">
|
||||
<td>$payment_due_label </td>
|
||||
<td>$due_date</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="header-amount-due-label">$amount_due_label</td>
|
||||
<td class="header-amount-due-value">$balance_due</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="top-right-side-section">
|
||||
<table style="white-space: nowrap;" cellspacing="10">
|
||||
<tr>
|
||||
<td>$entity_number_label:</td>
|
||||
<td class="header-invoice-number">$entity_number</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>$date_label:</td>
|
||||
<td>$date</td>
|
||||
</tr>
|
||||
<tr class="header-payment-due-label">
|
||||
<td>$payment_due_label:</td>
|
||||
<td>$due_date</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="header-amount-due-label">$amount_due_label:</td>
|
||||
<td class="header-amount-due-value">$balance_due</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-section">
|
||||
<div class="hero-contact-section">
|
||||
<div class="client-details">
|
||||
<span class="client-details-to-label">$to_label:</span>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
<span class="shipping-to-label">$shipping_label:</span>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
<div class="company-details">
|
||||
<span class="client-details-to-label">$from_label:</span>
|
||||
<div class="company-details-wrapper">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
<div class="hero-section">
|
||||
<div class="hero-contact-section">
|
||||
<div class="client-details">
|
||||
<span class="client-details-to-label">$to_label:</span>
|
||||
<div id="client-details"></div>
|
||||
<div id="vendor-details"></div>
|
||||
<span class="shipping-to-label">$shipping_label:</span>
|
||||
<div id="shipping-details"></div>
|
||||
</div>
|
||||
<div class="company-details">
|
||||
<span class="client-details-to-label">$from_label:</span>
|
||||
<div class="company-details-wrapper">
|
||||
<div id="company-details"></div>
|
||||
<div id="company-address"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body-wrapper">
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</div>
|
||||
<div class="body-wrapper">
|
||||
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
|
||||
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||
<div id="table-totals" cellspacing="0">$status_logo</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="repeating-footer-space"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="repeating-header" id="header"></div>
|
||||
|
|
@ -504,8 +459,6 @@
|
|||
$entity_images
|
||||
|
||||
<div class="repeating-footerx" id="footer">
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
</div>
|
||||
|
||||
<p data-ref="total_table-footer">$entity_footer</p>
|
||||
|
||||
</div>
|
||||
|
|
@ -97,7 +97,8 @@
|
|||
<div class="two-col-grid" style="">
|
||||
<ninja>
|
||||
<div class="pull-left">
|
||||
<h1 class="primary-color-highlight" style="margin-top:0;">$receipt_label {%if payments|length == 1%}#$number{% endif %}</h2>
|
||||
<h1 class="primary-color-highlight" style="margin-top:0;">$receipt_label {%if payments|length ==
|
||||
1%}#$number{% endif %}</h2>
|
||||
</div>
|
||||
</ninja>
|
||||
<div class="pull-right"><img src="$company.logo" class="company-logo"></div>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ use App\Http\Controllers\QuoteController;
|
|||
use App\Http\Controllers\RecurringInvoiceController;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\PhantomJS\Phantom;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('client', [ContactLoginController::class, 'showLoginForm'])->name('client.catchall')->middleware(['domain_db', 'contact_account','locale', 'throttle:portal']); //catch all
|
||||
|
|
@ -146,24 +145,6 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie
|
|||
Route::get('unsubscribe/{entity}/{invitation_key}', [App\Http\Controllers\ClientPortal\InvitationController::class, 'unsubscribe'])->name('unsubscribe');
|
||||
});
|
||||
|
||||
// Route::get('route/{hash}', function ($hash) {
|
||||
|
||||
// $route = '/';
|
||||
|
||||
// try {
|
||||
// $route = decrypt($hash);
|
||||
// }
|
||||
// catch (\Exception $e) {
|
||||
// abort(404);
|
||||
// }
|
||||
|
||||
// return redirect($route);
|
||||
|
||||
// })->middleware('throttle:404');
|
||||
|
||||
Route::get('phantom/{entity}/{invitation_key}', [Phantom::class, 'displayInvitation'])->middleware(['invite_db', 'phantom_secret'])->name('phantom_view');
|
||||
Route::get('blade/', [Phantom::class, 'blade'])->name('blade');
|
||||
|
||||
Route::get('.env', function () {
|
||||
})->middleware('throttle:honeypot');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,319 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace Tests\Feature\PdfMaker;
|
||||
|
||||
use App\Services\PdfMaker\Design;
|
||||
use App\Services\PdfMaker\PdfMaker;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PdfMakerTest extends TestCase
|
||||
{
|
||||
public $state = [
|
||||
'template' => [],
|
||||
'variables' => [
|
||||
'labels' => [],
|
||||
'values' => [],
|
||||
],
|
||||
];
|
||||
|
||||
public function testDesignLoadsCorrectly()
|
||||
{
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
$maker = new PdfMaker($this->state);
|
||||
|
||||
$maker->design($design);
|
||||
|
||||
$this->assertInstanceOf(Design::class, $maker->design);
|
||||
}
|
||||
|
||||
public function testHtmlDesignLoadsCorrectly()
|
||||
{
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
|
||||
$maker = new PdfMaker($this->state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
$this->assertStringContainsString('Template: Example', $maker->getCompiledHTML());
|
||||
}
|
||||
|
||||
public function testGetSectionUtility()
|
||||
{
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
|
||||
$maker = new PdfMaker($this->state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
$this->assertEquals('table', $maker->getSectionNode('product-table')->nodeName);
|
||||
}
|
||||
|
||||
public function testTableAttributesAreInjected()
|
||||
{
|
||||
$state = [
|
||||
'template' => [
|
||||
'product-table' => [
|
||||
'id' => 'product-table',
|
||||
'properties' => [
|
||||
'class' => 'my-awesome-class',
|
||||
'style' => 'margin-top: 10px;',
|
||||
'script' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
'variables' => [
|
||||
'labels' => [],
|
||||
'values' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
$maker = new PdfMaker($state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
$this->assertStringContainsString('my-awesome-class', $maker->getSection('product-table', 'class'));
|
||||
// $this->assertStringContainsString('margin-top: 10px', $maker->getSection('product-table', 'style'));
|
||||
// $this->assertStringContainsString('console.log(1)', $maker->getSection('product-table', 'script'));
|
||||
}
|
||||
|
||||
public function testVariablesAreReplaced()
|
||||
{
|
||||
$state = [
|
||||
'template' => [
|
||||
'product-table' => [
|
||||
'id' => 'product-table',
|
||||
],
|
||||
],
|
||||
'variables' => [
|
||||
'labels' => [],
|
||||
'values' => [
|
||||
'$company.name' => 'Invoice Ninja',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
$maker = new PdfMaker($state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
$this->assertStringContainsString('Invoice Ninja', $maker->getCompiledHTML());
|
||||
$this->assertStringContainsString('Invoice Ninja', $maker->getSection('header'));
|
||||
}
|
||||
|
||||
public function testElementContentIsGenerated()
|
||||
{
|
||||
$state = [
|
||||
'template' => [
|
||||
'product-table' => [
|
||||
'id' => 'product-table',
|
||||
'properties' => [],
|
||||
'elements' => [
|
||||
['element' => 'thead', 'content' => '', 'elements' => [
|
||||
['element' => 'th', 'content' => 'Company'],
|
||||
['element' => 'th', 'content' => 'Contact'],
|
||||
['element' => 'th', 'content' => 'Country', 'properties' => [
|
||||
'colspan' => 3,
|
||||
]],
|
||||
]],
|
||||
['element' => 'tr', 'content' => '', 'elements' => [
|
||||
['element' => 'td', 'content' => '$company'],
|
||||
['element' => 'td', 'content' => '$email'],
|
||||
['element' => 'td', 'content' => '$country', 'elements' => [
|
||||
['element' => 'a', 'content' => 'Click here for a link', 'properties' => [
|
||||
'href' => 'https://github.com/invoiceninja/invoiceninja',
|
||||
]],
|
||||
]],
|
||||
]],
|
||||
],
|
||||
],
|
||||
],
|
||||
'variables' => [
|
||||
'labels' => [],
|
||||
'values' => [
|
||||
'$company' => 'Invoice Ninja',
|
||||
'$email' => 'contact@invoiceninja.com',
|
||||
'$country' => 'UK',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
$maker = new PdfMaker($state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
$compiled = 'contact@invoiceninja.com';
|
||||
|
||||
$this->assertStringContainsString($compiled, $maker->getCompiledHTML());
|
||||
}
|
||||
|
||||
public function testConditionalRenderingOfElements()
|
||||
{
|
||||
$design1 = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
|
||||
$maker1 = new PdfMaker([
|
||||
'template' => [
|
||||
'header' => [
|
||||
'id' => 'header',
|
||||
'properties' => [],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$maker1
|
||||
->design($design1)
|
||||
->build();
|
||||
|
||||
$output1 = $maker1->getCompiledHTML();
|
||||
|
||||
$this->assertStringContainsString('<div id="header">', $output1);
|
||||
|
||||
$design2 = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
$maker2 = new PdfMaker([
|
||||
'template' => [
|
||||
'header' => [
|
||||
'id' => 'header',
|
||||
'properties' => ['hidden' => 'true'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$maker2
|
||||
->design($design2)
|
||||
->build();
|
||||
|
||||
$output2 = $maker2->getCompiledHTML();
|
||||
|
||||
$this->assertStringContainsString('<div id="header" hidden="true">$company.name</div>', $output2);
|
||||
|
||||
$this->assertNotSame($output1, $output2);
|
||||
}
|
||||
|
||||
public function testGeneratingPdf()
|
||||
{
|
||||
$state = [
|
||||
'template' => [
|
||||
'header' => [
|
||||
'id' => 'header',
|
||||
'properties' => ['class' => 'text-white bg-blue-600 p-2'],
|
||||
],
|
||||
'product-table' => [
|
||||
'id' => 'product-table',
|
||||
'properties' => ['class' => 'table-auto'],
|
||||
'elements' => [
|
||||
['element' => 'thead', 'content' => '', 'elements' => [
|
||||
['element' => 'tr', 'content' => '', 'elements' => [
|
||||
['element' => 'th', 'content' => 'Title', 'properties' => ['class' => 'px-4 py-2']],
|
||||
['element' => 'th', 'content' => 'Author', 'properties' => ['class' => 'px-4 py-2']],
|
||||
['element' => 'th', 'content' => 'Views', 'properties' => ['class' => 'px-4 py-2']],
|
||||
]],
|
||||
]],
|
||||
['element' => 'tbody', 'content' => '', 'elements' => [
|
||||
['element' => 'tr', 'content' => '', 'elements' => [
|
||||
['element' => 'td', 'content' => 'An amazing guy', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
['element' => 'td', 'content' => 'David Bomba', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
['element' => 'td', 'content' => '1M', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
]],
|
||||
['element' => 'tr', 'content' => '', 'elements' => [
|
||||
['element' => 'td', 'content' => 'Flutter master', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
['element' => 'td', 'content' => 'Hillel Coren', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
['element' => 'td', 'content' => '1M', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
]],
|
||||
['element' => 'tr', 'content' => '', 'elements' => [
|
||||
['element' => 'td', 'content' => 'Bosssssssss', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
['element' => 'td', 'content' => 'Shalom Stark', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
['element' => 'td', 'content' => '1M', 'properties' => ['class' => 'border px-4 py-2']],
|
||||
]],
|
||||
['element' => 'tr', 'content' => '', 'order' => 4, 'elements' => [
|
||||
['element' => 'td', 'content' => 'Three amazing guys', 'properties' => ['class' => 'border px-4 py-2', 'colspan' => '100%']],
|
||||
]],
|
||||
]],
|
||||
],
|
||||
],
|
||||
],
|
||||
'variables' => [
|
||||
'labels' => [],
|
||||
'values' => [
|
||||
'$title' => 'Invoice Ninja',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
$maker = new PdfMaker($state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testGetSectionHTMLWorks()
|
||||
{
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
|
||||
$html = $design
|
||||
->document()
|
||||
->getSectionHTML('product-table');
|
||||
|
||||
$this->assertStringContainsString('id="product-table"', $html);
|
||||
}
|
||||
|
||||
public function testWrapperHTMLWorks()
|
||||
{
|
||||
$design = new Design('example', ['custom_path' => base_path('tests/Feature/PdfMaker/')]);
|
||||
|
||||
$state = [
|
||||
'template' => [
|
||||
'product-table' => [
|
||||
'id' => 'product-table',
|
||||
'elements' => [
|
||||
['element' => 'p', 'content' => 'Example paragraph'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'variables' => [
|
||||
'labels' => [],
|
||||
'values' => [],
|
||||
],
|
||||
'options' => [
|
||||
'all_pages_header' => true,
|
||||
'all_pages_footer' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$maker = new PdfMaker($state);
|
||||
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
// exec('echo "" > storage/logs/laravel.log');
|
||||
|
||||
// nlog($maker->getCompiledHTML(true));
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,9 +17,6 @@ use App\Models\Design;
|
|||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Project;
|
||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use App\Services\PdfMaker\PdfMaker;
|
||||
use App\Services\Template\TemplateMock;
|
||||
use App\Services\Template\TemplateService;
|
||||
use App\Utils\HtmlEngine;
|
||||
|
|
@ -819,80 +816,4 @@ class TemplateTest extends TestCase
|
|||
// nlog("Plain PDF Gen Time: " . $end-$start);
|
||||
}
|
||||
|
||||
public function testTemplateGeneration()
|
||||
{
|
||||
$entity_obj = $this->invoice;
|
||||
|
||||
$design = new Design();
|
||||
$design->design = json_decode(json_encode($this->invoice->company->settings->pdf_variables), true);
|
||||
$design->name = 'test';
|
||||
$design->is_active = true;
|
||||
$design->is_template = true;
|
||||
$design->is_custom = true;
|
||||
$design->user_id = $this->invoice->user_id;
|
||||
$design->company_id = $this->invoice->company_id;
|
||||
|
||||
$design_object = new \stdClass();
|
||||
$design_object->includes = '';
|
||||
$design_object->header = '';
|
||||
$design_object->body = $this->body;
|
||||
$design_object->product = '';
|
||||
$design_object->task = '';
|
||||
$design_object->footer = '';
|
||||
|
||||
$design->design = $design_object;
|
||||
|
||||
$design->save();
|
||||
|
||||
$start = microtime(true);
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
App::setLocale($entity_obj->client->locale());
|
||||
$t->replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
|
||||
$html = new HtmlEngine($entity_obj->invitations()->first());
|
||||
|
||||
$options = [
|
||||
'custom_partials' => json_decode(json_encode($design->design), true),
|
||||
];
|
||||
$template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
|
||||
|
||||
$variables = $html->generateLabelsAndValues();
|
||||
|
||||
$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' => $variables,
|
||||
]),
|
||||
'variables' => $variables,
|
||||
'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],
|
||||
'invoices' => [$entity_obj],
|
||||
'variables' => $variables,
|
||||
],
|
||||
'process_markdown' => $entity_obj->client->company->markdown_enabled,
|
||||
];
|
||||
|
||||
$maker = new PdfMaker($state);
|
||||
$maker
|
||||
->design($template)
|
||||
->build();
|
||||
|
||||
$html = $maker->getCompiledHTML(true);
|
||||
|
||||
$end = microtime(true);
|
||||
|
||||
$this->assertNotNull($html);
|
||||
$this->assertStringContainsStringIgnoringCase($this->company->settings->name, $html);
|
||||
|
||||
// nlog("Twig Solo Gen Time: ". $end - $start);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ use App\Models\Invoice;
|
|||
use App\Models\PurchaseOrder;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Tests\MockAccountData;
|
||||
|
|
@ -49,52 +46,4 @@ class HtmlGenerationTest extends TestCase
|
|||
$this->assertNotNull($html);
|
||||
}
|
||||
|
||||
private function generateHtml($entity)
|
||||
{
|
||||
$entity_design_id = '';
|
||||
|
||||
if ($entity instanceof Invoice || $entity instanceof RecurringInvoice) {
|
||||
$entity_design_id = 'invoice_design_id';
|
||||
} elseif ($entity instanceof Quote) {
|
||||
$entity_design_id = 'quote_design_id';
|
||||
} elseif ($entity instanceof Credit) {
|
||||
$entity_design_id = 'credit_design_id';
|
||||
} elseif ($entity instanceof PurchaseOrder) {
|
||||
$entity_design_id = 'purchase_order_design_id';
|
||||
}
|
||||
|
||||
$entity_design_id = $entity->design_id ? $entity->design_id : $this->decodePrimaryKey($entity->client->getSetting($entity_design_id));
|
||||
|
||||
$design = Design::find($entity_design_id);
|
||||
$html = new HtmlEngine($entity->invitations->first());
|
||||
|
||||
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'),
|
||||
],
|
||||
];
|
||||
|
||||
$maker = new PdfMakerService($state);
|
||||
|
||||
return $maker->design($template)
|
||||
->build()
|
||||
->getCompiledHTML(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,18 @@
|
|||
|
||||
namespace Tests\Pdf;
|
||||
|
||||
use App\Services\Pdf\PdfConfiguration;
|
||||
use App\Services\Pdf\PdfService;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
use App\Models\Client;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use Tests\MockAccountData;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\VendorContact;
|
||||
use App\Services\Pdf\PdfService;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Services\Pdf\PdfConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -25,11 +33,433 @@ class PdfServiceTest extends TestCase
|
|||
{
|
||||
use MockAccountData;
|
||||
|
||||
private string $max_pdf_variables = '{"client_details":["$client.name","$contact.full_name","$client.address1","$client.city_state_postal","$client.number","$client.vat_number","$client.postal_city_state","$client.website","$client.country","$client.custom3","$client.id_number","$client.phone","$client.address2","$client.custom1","$contact.custom1"],"vendor_details":["$vendor.name","$vendor.number","$vendor.vat_number","$vendor.address1","$vendor.address2","$vendor.city_state_postal","$vendor.country","$vendor.phone","$contact.email","$vendor.id_number","$vendor.website","$vendor.custom2","$vendor.custom1","$vendor.custom4","$vendor.custom3","$contact.phone","$contact.full_name","$contact.custom2","$contact.custom1"],"purchase_order_details":["$purchase_order.number","$purchase_order.date","$purchase_order.total","$purchase_order.balance_due","$purchase_order.due_date","$purchase_order.po_number","$purchase_order.custom1","$purchase_order.custom2","$purchase_order.custom3"],"company_details":["$company.name","$company.email","$company.phone","$company.id_number","$company.vat_number","$company.website","$company.address2","$company.address1","$company.city_state_postal","$company.postal_city_state","$company.custom1","$company.custom3"],"company_address":["$company.address1","$company.city_state_postal","$company.country","$company.id_number","$company.vat_number","$company.website","$company.email","$company.name","$company.custom1"],"invoice_details":["$invoice.number","$invoice.date","$invoice.balance","$invoice.custom1","$invoice.due_date","$invoice.project","$invoice.balance_due","$invoice.custom3","$invoice.po_number","$invoice.custom2","$invoice.amount","$invoice.custom4"],"quote_details":["$quote.number","$quote.custom1","$quote.po_number","$quote.date","$quote.valid_until","$quote.total","$quote.custom2","$quote.custom3","$quote.custom4"],"credit_details":["$credit.number","$credit.balance","$credit.po_number","$credit.date","$credit.valid_until","$credit.total","$credit.custom1","$credit.custom2","$credit.custom3"],"product_columns":["$product.item","$product.product1","$product.description","$product.product2","$product.tax","$product.line_total","$product.quantity","$product.unit_cost","$product.discount","$product.product3","$product.product4","$product.gross_line_total"],"product_quote_columns":["$product.item","$product.description","$product.unit_cost","$product.quantity","$product.discount","$product.tax","$product.line_total"],"task_columns":["$task.service","$task.description","$task.rate","$task.hours","$task.discount","$task.line_total","$task.tax","$task.tax_amount","$task.task2","$task.task1","$task.task3"],"total_columns":["$total","$line_taxes","$total_taxes","$discount","$custom_surcharge1","$outstanding","$net_subtotal","$custom_surcharge2","$custom_surcharge3","$subtotal","$paid_to_date"],"statement_invoice_columns":["$invoice.number","$invoice.date","$due_date","$total","$balance"],"statement_payment_columns":["$invoice.number","$payment.date","$method","$statement_amount"],"statement_credit_columns":["$credit.number","$credit.date","$total","$credit.balance"],"statement_details":["$statement_date","$balance"],"delivery_note_columns":["$product.item","$product.description","$product.quantity"],"statement_unapplied_columns":["$payment.number","$payment.date","$payment.amount","$payment.payment_balance"]}';
|
||||
|
||||
private string $min_pdf_variables = '{"client_details":["$client.name","$client.vat_number","$client.address1","$client.city_state_postal","$client.country"],"vendor_details":["$vendor.name","$vendor.vat_number","$vendor.address1","$vendor.city_state_postal","$vendor.country"],"purchase_order_details":["$purchase_order.number","$purchase_order.date","$purchase_order.total"],"company_details":["$company.name","$company.address1","$company.city_state_postal"],"company_address":["$company.name","$company.website"],"invoice_details":["$invoice.number","$invoice.date","$invoice.due_date","$invoice.balance"],"quote_details":["$quote.number","$quote.date","$quote.valid_until"],"credit_details":["$credit.date","$credit.number","$credit.balance"],"product_columns":["$product.item","$product.description","$product.line_total"],"product_quote_columns":["$product.item","$product.description","$product.unit_cost","$product.quantity","$product.discount","$product.tax","$product.line_total"],"task_columns":["$task.description","$task.rate","$task.line_total"],"total_columns":["$total","$total_taxes","$outstanding"],"statement_invoice_columns":["$invoice.number","$invoice.date","$due_date","$total","$balance"],"statement_payment_columns":["$invoice.number","$payment.date","$method","$statement_amount"],"statement_credit_columns":["$credit.number","$credit.date","$total","$credit.balance"],"statement_details":["$statement_date","$balance"],"delivery_note_columns":["$product.item","$product.description","$product.quantity"],"statement_unapplied_columns":["$payment.number","$payment.date","$payment.amount","$payment.payment_balance"]}';
|
||||
|
||||
private string $fake_email;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
$this->fake_email = $this->faker->email();
|
||||
|
||||
}
|
||||
|
||||
private function stubInvoice($settings, array $company_props = [])
|
||||
{
|
||||
|
||||
$company = Company::factory()->create(array_merge([
|
||||
'account_id' => $this->account->id,
|
||||
'settings' => $settings
|
||||
], $company_props));
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $company->id
|
||||
]);
|
||||
|
||||
$contact = ClientContact::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $company->id,
|
||||
'client_id' => $client->id,
|
||||
'is_primary' => true,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'email' => 'john@doe.com',
|
||||
'phone' => '1234567890',
|
||||
'send_email' => true,
|
||||
]);
|
||||
|
||||
$invoice = Invoice::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $company->id,
|
||||
'client_id' => $client->id,
|
||||
'status_id' => Invoice::STATUS_DRAFT,
|
||||
]);
|
||||
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
$invoice = $invoice->service()->createInvitations()->markSent()->save();
|
||||
$invoice = $invoice->fresh();
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
private function stubPurchaseOrder($settings, array $company_props = [])
|
||||
{
|
||||
|
||||
$company = Company::factory()->create(array_merge([
|
||||
'account_id' => $this->account->id,
|
||||
'settings' => $settings
|
||||
], $company_props));
|
||||
|
||||
$vendor = Vendor::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $company->id
|
||||
]);
|
||||
|
||||
$contact = VendorContact::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $company->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'is_primary' => true,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'email' => 'john@doe.com',
|
||||
'phone' => '1234567890',
|
||||
'send_email' => true,
|
||||
]);
|
||||
|
||||
$po = PurchaseOrder::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $company->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'status_id' => PurchaseOrder::STATUS_DRAFT,
|
||||
]);
|
||||
|
||||
$po = $po->calc()->getInvoice();
|
||||
$po = $po->service()->createInvitations()->markSent()->save();
|
||||
$po = $po->fresh();
|
||||
|
||||
return $po;
|
||||
|
||||
}
|
||||
|
||||
public function testPurchaseOrderGeneration()
|
||||
{
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->pdf_variables = json_decode($this->max_pdf_variables);
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->website = 'www.invoiceninja.com';
|
||||
$settings->name = 'Invoice Ninja';
|
||||
$settings->address1 = 'Address 1';
|
||||
$settings->address2 = 'Address 2';
|
||||
$settings->city = 'City';
|
||||
$settings->state = 'State';
|
||||
$settings->postal_code = 'Postal Code';
|
||||
$settings->phone = '555-343-2323';
|
||||
$settings->email = $this->fake_email;
|
||||
$settings->country_id = '840';
|
||||
$settings->vat_number = 'vat number';
|
||||
$settings->id_number = 'id number';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1';
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->hide_empty_columns_on_pdf = true;
|
||||
|
||||
$po = $this->stubPurchaseOrder($settings, ['markdown_enabled' => true]);
|
||||
|
||||
$items = $po->line_items;
|
||||
|
||||
$first_item = $items[0];
|
||||
|
||||
$first_item->notes = $this->faker->paragraphs(2, true);
|
||||
|
||||
$items[] = $first_item;
|
||||
|
||||
$new_item = $items[0];
|
||||
$new_item->notes = '**Bold** _Italic_ [Link](https://www.google.com)
|
||||
+ this
|
||||
+ and that
|
||||
+ is something to think about';
|
||||
|
||||
$items[] = $new_item;
|
||||
|
||||
$po->line_items = $items;
|
||||
$po->calc()->getPurchaseOrder();
|
||||
|
||||
|
||||
$this->assertGreaterThan(0, $po->invitations()->count());
|
||||
|
||||
\App\Models\Design::where('is_custom', false)->cursor()->each(function ($design) use($po) {
|
||||
|
||||
$po->design_id = $design->id;
|
||||
$po->save();
|
||||
$po = $po->fresh();
|
||||
|
||||
$service = (new PdfService($po->invitations()->first(), 'purchase_order'))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/po_' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public function testMarkdownEnabled()
|
||||
{
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->pdf_variables = json_decode($this->max_pdf_variables);
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->website = 'www.invoiceninja.com';
|
||||
$settings->name = 'Invoice Ninja';
|
||||
$settings->address1 = 'Address 1';
|
||||
$settings->address2 = 'Address 2';
|
||||
$settings->city = 'City';
|
||||
$settings->state = 'State';
|
||||
$settings->postal_code = 'Postal Code';
|
||||
$settings->phone = '555-343-2323';
|
||||
$settings->email = $this->fake_email;
|
||||
$settings->country_id = '840';
|
||||
$settings->vat_number = 'vat number';
|
||||
$settings->id_number = 'id number';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1';
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->hide_empty_columns_on_pdf = true;
|
||||
|
||||
$invoice = $this->stubInvoice($settings, ['markdown_enabled' => true]);
|
||||
|
||||
$items = $invoice->line_items;
|
||||
|
||||
$first_item = $items[0];
|
||||
|
||||
$first_item->notes = $this->faker->paragraphs(2, true);
|
||||
|
||||
$items[] = $first_item;
|
||||
|
||||
$new_item = $items[0];
|
||||
$new_item->notes = '**Bold** _Italic_ [Link](https://www.google.com)
|
||||
+ this
|
||||
+ and that
|
||||
+ is something to think about';
|
||||
|
||||
$items[] = $new_item;
|
||||
|
||||
$invoice->line_items = $items;
|
||||
$invoice->calc()->getInvoice();
|
||||
|
||||
|
||||
$this->assertGreaterThan(0, $invoice->invitations()->count());
|
||||
|
||||
\App\Models\Design::where('is_custom', false)->cursor()->each(function ($design) use($invoice) {
|
||||
|
||||
$invoice->design_id = $design->id;
|
||||
$invoice->save();
|
||||
$invoice = $invoice->fresh();
|
||||
|
||||
$service = (new PdfService($invoice->invitations()->first()))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/markdown_' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testLargeDescriptionField()
|
||||
{
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->pdf_variables = json_decode($this->max_pdf_variables);
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->website = 'www.invoiceninja.com';
|
||||
$settings->name = 'Invoice Ninja';
|
||||
$settings->address1 = 'Address 1';
|
||||
$settings->address2 = 'Address 2';
|
||||
$settings->city = 'City';
|
||||
$settings->state = 'State';
|
||||
$settings->postal_code = 'Postal Code';
|
||||
$settings->phone = '555-343-2323';
|
||||
$settings->email = $this->fake_email;
|
||||
$settings->country_id = '840';
|
||||
$settings->vat_number = 'vat number';
|
||||
$settings->id_number = 'id number';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1';
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->hide_empty_columns_on_pdf = true;
|
||||
|
||||
$invoice = $this->stubInvoice($settings);
|
||||
|
||||
$items = $invoice->line_items;
|
||||
|
||||
$items[0]->notes = $this->faker->text(500);
|
||||
|
||||
$invoice->line_items = $items;
|
||||
$invoice->save();
|
||||
|
||||
$this->assertGreaterThan(0, $invoice->invitations()->count());
|
||||
|
||||
\App\Models\Design::where('is_custom', false)->cursor()->each(function ($design) use($invoice) {
|
||||
|
||||
$invoice->design_id = $design->id;
|
||||
$invoice->save();
|
||||
$invoice = $invoice->fresh();
|
||||
|
||||
$service = (new PdfService($invoice->invitations()->first()))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/desc_' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testMaxInvoiceFields()
|
||||
{
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->pdf_variables = json_decode($this->max_pdf_variables);
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->website = 'www.invoiceninja.com';
|
||||
$settings->name = 'Invoice Ninja';
|
||||
$settings->address1 = 'Address 1';
|
||||
$settings->address2 = 'Address 2';
|
||||
$settings->city = 'City';
|
||||
$settings->state = 'State';
|
||||
$settings->postal_code = 'Postal Code';
|
||||
$settings->phone = '555-343-2323';
|
||||
$settings->email = $this->fake_email;
|
||||
$settings->country_id = '840';
|
||||
$settings->vat_number = 'vat number';
|
||||
$settings->id_number = 'id number';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1';
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->hide_empty_columns_on_pdf = true;
|
||||
|
||||
$invoice = $this->stubInvoice($settings);
|
||||
|
||||
$this->assertGreaterThan(0, $invoice->invitations()->count());
|
||||
|
||||
\App\Models\Design::where('is_custom', false)->cursor()->each(function ($design) use($invoice) {
|
||||
|
||||
$invoice->design_id = $design->id;
|
||||
$invoice->save();
|
||||
$invoice = $invoice->fresh();
|
||||
|
||||
$service = (new PdfService($invoice->invitations()->first()))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/max_fields_' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public function testMinInvoiceFields()
|
||||
{
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->pdf_variables = json_decode($this->min_pdf_variables);
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->website = 'www.invoiceninja.com';
|
||||
$settings->name = 'Invoice Ninja';
|
||||
$settings->address1 = 'Address 1';
|
||||
$settings->address2 = 'Address 2';
|
||||
$settings->city = 'City';
|
||||
$settings->state = 'State';
|
||||
$settings->postal_code = 'Postal Code';
|
||||
$settings->phone = '555-343-2323';
|
||||
$settings->email = $this->fake_email;
|
||||
$settings->country_id = '840';
|
||||
$settings->vat_number = 'vat number';
|
||||
$settings->id_number = 'id number';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1';
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->hide_empty_columns_on_pdf = true;
|
||||
|
||||
$invoice = $this->stubInvoice($settings);
|
||||
|
||||
\App\Models\Design::where('is_custom', false)->cursor()->each(function ($design) use ($invoice) {
|
||||
|
||||
$invoice->design_id = $design->id;
|
||||
$invoice->save();
|
||||
$invoice = $invoice->fresh();
|
||||
|
||||
$service = (new PdfService($invoice->invitations->first()))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/min_fields_' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testStatementPdfGeneration()
|
||||
{
|
||||
|
||||
$pdf = $this->client->service()->statement([
|
||||
'client_id' => $this->client->hashed_id,
|
||||
'start_date' => '2000-01-01',
|
||||
'end_date' => '2023-01-01',
|
||||
'show_aging_table' => true,
|
||||
'show_payments_table' => true,
|
||||
'status' => 'all'
|
||||
]);
|
||||
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/statement.pdf', $pdf);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function testMultiDesignGeneration()
|
||||
{
|
||||
|
||||
if (config('ninja.testvars.travis')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
\App\Models\Design::where('is_custom',false)->cursor()->each(function ($design){
|
||||
|
||||
$this->invoice->design_id = $design->id;
|
||||
$this->invoice->save();
|
||||
$this->invoice = $this->invoice->fresh();
|
||||
|
||||
$invitation = $this->invoice->invitations->first();
|
||||
|
||||
$service = (new PdfService($invitation))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
\App\Models\Design::where('is_custom', false)->cursor()->each(function ($design) {
|
||||
|
||||
|
||||
$this->invoice->design_id = $design->id;
|
||||
$this->invoice->save();
|
||||
$this->invoice = $this->invoice->fresh();
|
||||
|
||||
$invitation = $this->invoice->invitations->first();
|
||||
|
||||
$service = (new PdfService($invitation, 'delivery_note'))->boot();
|
||||
$pdf = $service->getPdf();
|
||||
|
||||
$this->assertNotNull($pdf);
|
||||
|
||||
\Illuminate\Support\Facades\Storage::put('/pdf/dn_' . $design->name.'.pdf', $pdf);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public function testPdfGeneration()
|
||||
|
|
|
|||
Loading…
Reference in New Issue