diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 8e7a8c16cd52..e8f208c427c5 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -167,7 +167,7 @@ class CompanySettings extends BaseSettings 'credit_number_prefix' => '', 'client_number_prefix' => '', 'auto_archive_invoice' => 'FALSE', - 'design' => 'pdf.design1', + 'design' => 'views/pdf/design1.blade.php', 'translations' => (object) [], ]; diff --git a/app/Listeners/Invoice/CreateInvoicePdf.php b/app/Listeners/Invoice/CreateInvoicePdf.php index a93491551def..8c4762cce318 100644 --- a/app/Listeners/Invoice/CreateInvoicePdf.php +++ b/app/Listeners/Invoice/CreateInvoicePdf.php @@ -14,6 +14,7 @@ namespace App\Listeners\Invoice; use App\Utils\Traits\MakesHash; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Spatie\Browsershot\Browsershot; @@ -72,7 +73,7 @@ class CreateInvoicePdf implements ShouldQueue //->showBrowserHeaderAndFooter() //->headerHtml($header) //->footerHtml($footer) - ->waitUntilNetworkIdle()->pdf(); + ->waitUntilNetworkIdle(false)->pdf(); //->margins(10,10,10,10) //->savePdf('test.pdf'); } @@ -88,8 +89,57 @@ class CreateInvoicePdf implements ShouldQueue */ private function generateInvoiceHtml($design, $invoice) :string { - - return view($design, array_merge($invoice->makeLabels(), $invoice->makeValues()))->render(); + + $variables = array_merge($invoice->makeLabels(), $invoice->makeValues()); + $design = str_replace(array_keys($variables), array_values($variables), $design); +// + $data['invoice'] = $invoice; + + return $this->renderView($design, $data); + + //return view($design, $data)->render(); } + + /** + * Parses the blade file string and processes the template variables + * + * @param string $string The Blade file string + * @param array $data The array of template variables + * @return string The return HTML string + * + */ + private function renderView($string, $data) :string + { + if (!$data) { + $data = []; + } + + $data['__env'] = app(\Illuminate\View\Factory::class); + + $php = Blade::compileString($string); + + $obLevel = ob_get_level(); + ob_start(); + extract($data, EXTR_SKIP); + + try { + eval('?' . '>' . $php); + } catch (\Exception $e) { + while (ob_get_level() > $obLevel) { + ob_end_clean(); + } + + throw $e; + } catch (\Throwable $e) { + while (ob_get_level() > $obLevel) { + ob_end_clean(); + } + + throw new \FatalThrowableError($e); + } + + return ob_get_clean(); + } + } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index e962cd363c08..761b9f3a34db 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -20,7 +20,9 @@ use App\Utils\Traits\NumberFormatter; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Storage; use Laracasts\Presenter\PresentableTrait; class Invoice extends BaseModel @@ -242,11 +244,11 @@ class Invoice extends BaseModel /** * Returns the template for the invoice * - * @return string Either the template view, OR the template HTML stirng + * @return string Either the template view, OR the template HTML string */ public function design() :string { - return $this->settings->design ?: 'pdf.design1'; + return File::exists(resource_path($this->settings->design)) ? File::get(resource_path($this->settings->design)) : File::get(resource_path('views/pdf/design1.blade.php')); } } \ No newline at end of file diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index c3aeec559512..fffd698c9ed8 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -26,6 +26,11 @@ class CompanyPresenter extends EntityPresenter return $this->entity->name ?: ctrans('texts.untitled_account'); } + public function logo() + { + return strlen($this->entity->logo > 0) ? $this->entity->logo : 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png'; + } + public function address() { $str = ''; diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index 8af7bf528301..6fc5db848c4d 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -39,89 +39,101 @@ trait MakesInvoiceValues ]; private static $labels = [ - 'invoice', - 'invoice_date', - 'due_date', - 'invoice_number', - 'po_number', - 'discount', - 'taxes', - 'tax', - 'item', - 'description', - 'unit_cost', - 'quantity', - 'line_total', - 'subtotal', - 'paid_to_date', - 'balance_due', - 'partial_due', - 'terms', - 'your_invoice', - 'quote', - 'your_quote', - 'quote_date', - 'quote_number', - 'total', - 'invoice_issued_to', - 'quote_issued_to', - 'rate', - 'hours', - 'balance', - 'from', - 'to', - 'invoice_to', - 'quote_to', - 'details', - 'invoice_no', - 'quote_no', - 'valid_until', - 'client_name', - 'address1', - 'address2', - 'id_number', - 'vat_number', - 'city_state_postal', - 'postal_city_state', - 'country', - 'email', - 'contact_name', - 'company_name', - 'website', - 'phone', - 'blank', - 'surcharge', - 'tax_invoice', - 'tax_quote', - 'statement', - 'statement_date', - 'your_statement', - 'statement_issued_to', - 'statement_to', - 'credit_note', - 'credit_date', - 'credit_number', - 'credit_issued_to', - 'credit_to', - 'your_credit', - 'work_phone', - 'invoice_total', - 'outstanding', - 'invoice_due_date', - 'quote_due_date', - 'service', - 'product_key', - 'unit_cost', - 'custom_value1', - 'custom_value2', - 'delivery_note', - 'date', - 'method', - 'payment_date', - 'reference', - 'amount', - 'amount_paid', - ]; + 'invoice_date', + 'due_date', + 'invoice_number', + 'po_number', + 'discount', + 'taxes', + 'tax', + 'item', + 'description', + 'unit_cost', + 'quantity', + 'line_total', + 'subtotal', + 'paid_to_date', + 'balance_due', + 'partial_due', + 'terms', + 'your_invoice', + 'quote', + 'your_quote', + 'quote_date', + 'quote_number', + 'total', + 'invoice_issued_to', + 'quote_issued_to', + 'rate', + 'hours', + 'balance', + 'from', + 'to', + 'invoice_to', + 'quote_to', + 'details', + 'invoice_no', + 'quote_no', + 'valid_until', + 'client_name', + 'address1', + 'address2', + 'id_number', + 'vat_number', + 'city_state_postal', + 'postal_city_state', + 'country', + 'email', + 'contact_name', + 'company_name', + 'website', + 'phone', + 'blank', + 'surcharge', + 'tax_invoice', + 'tax_quote', + 'statement', + 'statement_date', + 'your_statement', + 'statement_issued_to', + 'statement_to', + 'credit_note', + 'credit_date', + 'credit_number', + 'credit_issued_to', + 'credit_to', + 'your_credit', + 'work_phone', + 'invoice_total', + 'outstanding', + 'invoice_due_date', + 'quote_due_date', + 'service', + 'product_key', + 'unit_cost', + 'custom_value1', + 'custom_value2', + 'delivery_note', + 'date', + 'method', + 'payment_date', + 'reference', + 'amount', + 'amount_paid', + ]; + + public function makeInvoiceTemplateKeys() + { + $data = []; + + foreach(self::$labels as $label) + $data[] = '$'.$label.'_label'; + + foreach(self::$labels as $label) + $data[] = '$'.$label; + + return $data; + } /** * Iterates and translates all labels @@ -133,7 +145,7 @@ trait MakesInvoiceValues $data = []; foreach(self::$labels as $label) - $data[$label . '_label'] = ctrans('texts.'.$label); + $data['$'.$label . '_label'] = ctrans('texts.'.$label); return $data; } @@ -147,89 +159,90 @@ trait MakesInvoiceValues { $data = []; - $data['invoice'] = $this; - $data['invoice_date'] = $this->invoice_date; - $data['due_date'] = $this->due_date; - $data['invoice_number'] = $this->invoice_number; - $data['po_number'] = $this->po_number; - // $data['discount'] = ; - // $data['taxes'] = ; - // $data['tax'] = ; - // $data['item'] = ; - // $data['description'] = ; - // $data['unit_cost'] = ; - // $data['quantity'] = ; - // $data['line_total'] = ; - // $data['subtotal'] = ; - // $data['paid_to_date'] = ; - $data['balance_due'] = Number::formatMoney($this->balance, $this->client->currency(), $this->client->country, $this->client->settings); - $data['partial_due'] = Number::formatMoney($this->partial, $this->client->currency(), $this->client->country, $this->client->settings); - $data['terms'] = $this->terms; - // $data['your_invoice'] = ; - // $data['quote'] = ; - // $data['your_quote'] = ; - // $data['quote_date'] = ; - // $data['quote_number'] = ; - $data['total'] = Number::formatMoney($this->amount, $this->client->currency(), $this->client->country, $this->client->settings); - // $data['invoice_issued_to'] = ; - // $data['quote_issued_to'] = ; - // $data['rate'] = ; - // $data['hours'] = ; - // $data['balance'] = ; - // $data['from'] = ; - // $data['to'] = ; - // $data['invoice_to'] = ; - // $data['quote_to'] = ; - // $data['details'] = ; - $data['invoice_no'] = $this->invoice_number; - // $data['quote_no'] = ; - // $data['valid_until'] = ; - $data['client_name'] = $this->present()->clientName(); - $data['address1'] = $this->client->address1; - $data['address2'] = $this->client->address2; - $data['id_number'] = $this->client->id_number; - $data['vat_number'] = $this->client->vat_number; - $data['city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, FALSE); - $data['postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, TRUE); - $data['country'] = $this->client->country->name; - $data['email'] = isset($this->client->primary_contact()->first()->email) ?: 'no primary contact set'; - $data['contact_name'] = $this->client->present()->primary_contact_name(); - $data['company_name'] = $this->company->name; - $data['website'] = $this->client->present()->website(); - $data['phone'] = $this->client->present()->phone(); - //$data['blank'] = ; - //$data['surcharge'] = ; + $data['$invoice_date'] = $this->invoice_date; + $data['$due_date'] = $this->due_date; + $data['$invoice_number'] = $this->invoice_number; + $data['$po_number'] = $this->po_number; + // $data['$discount'] = ; + // $data['$taxes'] = ; + // $data['$tax'] = ; + // $data['$item'] = ; + // $data['$description'] = ; + // $data['$unit_cost'] = ; + // $data['$quantity'] = ; + // $data['$line_total'] = ; + // $data['$subtotal'] = ; + // $data['$paid_to_date'] = ; + $data['$balance_due'] = Number::formatMoney($this->balance, $this->client->currency(), $this->client->country, $this->client->settings); + $data['$partial_due'] = Number::formatMoney($this->partial, $this->client->currency(), $this->client->country, $this->client->settings); + $data['$terms'] = $this->terms; + // $data['$your_invoice'] = ; + // $data['$quote'] = ; + // $data['$your_quote'] = ; + // $data['$quote_date'] = ; + // $data['$quote_number'] = ; + $data['$total'] = Number::formatMoney($this->amount, $this->client->currency(), $this->client->country, $this->client->settings); + // $data['$invoice_issued_to'] = ; + // $data['$quote_issued_to'] = ; + // $data['$rate'] = ; + // $data['$hours'] = ; + // $data['$balance'] = ; + // $data['$from'] = ; + // $data['$to'] = ; + // $data['$invoice_to'] = ; + // $data['$quote_to'] = ; + // $data['$details'] = ; + $data['$invoice_no'] = $this->invoice_number; + // $data['$quote_no'] = ; + // $data['$valid_until'] = ; + $data['$client_name'] = $this->present()->clientName(); + $data['$address1'] = $this->client->address1; + $data['$address2'] = $this->client->address2; + $data['$id_number'] = $this->client->id_number; + $data['$vat_number'] = $this->client->vat_number; + $data['$city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, FALSE); + $data['$postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, TRUE); + $data['$country'] = $this->client->country->name; + $data['$email'] = isset($this->client->primary_contact()->first()->email) ?: 'no primary contact set'; + $data['$contact_name'] = $this->client->present()->primary_contact_name(); + $data['$company_name'] = $this->company->present()->name(); + $data['$company_address'] = $this->company->present()->address(); + $data['$company_logo'] = $this->company->present()->logo(); + $data['$website'] = $this->client->present()->website(); + $data['$phone'] = $this->client->present()->phone(); + //$data['$blank'] = ; + //$data['$surcharge'] = ; /* - $data['tax_invoice'] = - $data['tax_quote'] = - $data['statement'] = ; - $data['statement_date'] = ; - $data['your_statement'] = ; - $data['statement_issued_to'] = ; - $data['statement_to'] = ; - $data['credit_note'] = ; - $data['credit_date'] = ; - $data['credit_number'] = ; - $data['credit_issued_to'] = ; - $data['credit_to'] = ; - $data['your_credit'] = ; - $data['work_phone'] = ; - $data['invoice_total'] = ; - $data['outstanding'] = ; - $data['invoice_due_date'] = ; - $data['quote_due_date'] = ; - $data['service'] = ; - $data['product_key'] = ; - $data['unit_cost'] = ; - $data['custom_value1'] = ; - $data['custom_value2'] = ; - $data['delivery_note'] = ; - $data['date'] = ; - $data['method'] = ; - $data['payment_date'] = ; - $data['reference'] = ; - $data['amount'] = ; - $data['amount_paid'] =; + $data['$tax_invoice'] = + $data['$tax_quote'] = + $data['$statement'] = ; + $data['$statement_date'] = ; + $data['$your_statement'] = ; + $data['$statement_issued_to'] = ; + $data['$statement_to'] = ; + $data['$credit_note'] = ; + $data['$credit_date'] = ; + $data['$credit_number'] = ; + $data['$credit_issued_to'] = ; + $data['$credit_to'] = ; + $data['$your_credit'] = ; + $data['$work_phone'] = ; + $data['$invoice_total'] = ; + $data['$outstanding'] = ; + $data['$invoice_due_date'] = ; + $data['$quote_due_date'] = ; + $data['$service'] = ; + $data['$product_key'] = ; + $data['$unit_cost'] = ; + $data['$custom_value1'] = ; + $data['$custom_value2'] = ; + $data['$delivery_note'] = ; + $data['$date'] = ; + $data['$method'] = ; + $data['$payment_date'] = ; + $data['$reference'] = ; + $data['$amount'] = ; + $data['$amount_paid'] =; */ return $data; diff --git a/database/factories/CompanyFactory.php b/database/factories/CompanyFactory.php index 6b159a0c24e5..fbbb1675846a 100644 --- a/database/factories/CompanyFactory.php +++ b/database/factories/CompanyFactory.php @@ -18,5 +18,6 @@ $factory->define(App\Models\Company::class, function (Faker $faker) { 'country_id' => 4, 'work_phone' => $faker->phoneNumber, 'work_email' => $faker->safeEmail, + 'logo' => 'https://www.invoiceninja.com/wp-content/themes/invoice-ninja/images/logo.png', ]; }); diff --git a/resources/views/pdf/design1.blade.php b/resources/views/pdf/design1.blade.php index 09f2dd4d881b..8bb01d4df7aa 100644 --- a/resources/views/pdf/design1.blade.php +++ b/resources/views/pdf/design1.blade.php @@ -111,13 +111,14 @@
- + - {{$invoice_number_label}}: {{ $invoice->invoice_number }}
- {{$invoice_date_label}}: {{ $invoice->invoice_date }}
- {{$invoice_due_date_label}}: {{ $invoice->due_date }} + $invoice_number_label: $invoice_number
+ $invoice_date_label: $invoice_date
+ $invoice_due_date_label: $due_date +
@@ -129,19 +130,20 @@
- {{$client_name}}
- {{$address1}}
- {{$address2}}
- {{$city_state_postal}}
- {{$country}}
- {{$vat_number}}
+ $client_name
+ $address1
+ $address2
+ $city_state_postal
+ $country
+ $vat_number
- {{$company_name}}
- {{$phone}}
- {{$email}}
+ $company_name
+ $company_address
+ $phone
+ $email