diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 748727942408..1153285ecc4e 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -220,8 +220,8 @@ class CompanySettings extends BaseSettings { public $secondary_font = 'Roboto'; public $hide_paid_to_date = false; public $embed_documents = false; - public $all_pages_header = true; - public $all_pages_footer = true; + public $all_pages_header = false; + public $all_pages_footer = false; public $pdf_variables = []; public static $casts = [ diff --git a/app/Designs/AbstractDesign.php b/app/Designs/AbstractDesign.php index 591a4ba757d9..25fd82218593 100644 --- a/app/Designs/AbstractDesign.php +++ b/app/Designs/AbstractDesign.php @@ -19,7 +19,9 @@ abstract class AbstractDesign abstract public function body(); - abstract public function table(); + abstract public function product_table(); + + abstract public function task_table(); abstract public function footer(); diff --git a/app/Designs/Bold.php b/app/Designs/Bold.php index 431995e52461..c49facd2a520 100644 --- a/app/Designs/Bold.php +++ b/app/Designs/Bold.php @@ -39,7 +39,11 @@ class Bold extends AbstractDesign { size: auto; margin-top: 5mm; - } + } + + .text-left .table_header_thead_class {} + .px-12 .text-2xl .px-4 .py-2 .table_header_td_class {} + .bg-gray-200 .py-5 .pl-12 .table_body_td_class {} '; } @@ -95,8 +99,10 @@ class Bold extends AbstractDesign 'table_body_td_class' => "bg-gray-200 py-5 pl-12", ]; } + public function task_table() { + } - public function table() { + public function product_table() { return ' diff --git a/app/Designs/Business.php b/app/Designs/Business.php index 3e244d8abc16..9406342c085a 100644 --- a/app/Designs/Business.php +++ b/app/Designs/Business.php @@ -106,7 +106,10 @@ class Business extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + + public function product_table() { return '
diff --git a/app/Designs/Clean.php b/app/Designs/Clean.php index fe410d490b74..4b4342338004 100644 --- a/app/Designs/Clean.php +++ b/app/Designs/Clean.php @@ -106,8 +106,10 @@ class Clean extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
diff --git a/app/Designs/Creative.php b/app/Designs/Creative.php index 5b3ddd18f7eb..de8f4ba7c165 100644 --- a/app/Designs/Creative.php +++ b/app/Designs/Creative.php @@ -102,8 +102,11 @@ class Creative extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { + return '
diff --git a/app/Designs/Custom.php b/app/Designs/Custom.php index 30ba401a9e50..72de8f97a636 100644 --- a/app/Designs/Custom.php +++ b/app/Designs/Custom.php @@ -19,8 +19,10 @@ class Custom extends AbstractDesign private $body; - private $table; + private $product_table; + private $task_table; + private $footer; private $table_styles; @@ -33,8 +35,10 @@ class Custom extends AbstractDesign $this->body = $design->body; - $this->table = $design->table; + $this->product_table = $design->product_table; + $this->task_table = $design->task_table; + $this->footer = $design->footer; $this->table_styles = $design->table_styles; @@ -67,18 +71,23 @@ class Custom extends AbstractDesign } - public function table() + public function product_table() { - return $this->table; + return $this->product_table; } + public function task_table() + { + return $this->task_table; + } + public function footer() { return $this->footer; - + } } \ No newline at end of file diff --git a/app/Designs/Designer.php b/app/Designs/Designer.php index 3d1d002134bc..63b395ecb4de 100644 --- a/app/Designs/Designer.php +++ b/app/Designs/Designer.php @@ -26,6 +26,8 @@ class Designer { protected $entity_string; + protected $entity; + private static $custom_fields = [ 'invoice1', 'invoice2', @@ -49,8 +51,9 @@ class Designer { 'company4', ]; - public function __construct($design, $input_variables, $entity_string) + public function __construct($entity, $design, $input_variables, $entity_string) { + $this->entity = $entity; $this->design = $design; @@ -65,27 +68,63 @@ class Designer { * formatted HTML * @return string The HTML design built */ - public function build($entity):Designer + public function build():Designer { - $this->exportVariables($entity) + $this->setHtml() + ->exportVariables() ->setDesign($this->getSection('include')) ->setDesign($this->getSection('header')) ->setDesign($this->getSection('body')) - ->setDesign($this->getTable($entity)) + ->setDesign($this->getProductTable($this->entity)) ->setDesign($this->getSection('footer')); return $this; } - public function getTable($entity):string + public function init() + { + $this->setHtml() + ->exportVariables(); + + return $this; + } + + public function getHeader() { - $table_header = $entity->table_header($this->input_variables['product_columns'], $this->design->table_styles()); - $table_body = $entity->table_body($this->input_variables['product_columns'], $this->design->table_styles()); + $this->setDesign($this->getSection('include')) + ->setDesign($this->getSection('header')); - $data = str_replace('$table_header', $table_header, $this->getSection('table')); + return $this; + } + + public function getFooter() + { + + $this->setDesign($this->getSection('footer')); + + return $this; + } + + public function getBody() + { + + $this->setDesign($this->getSection('include')) + ->setDesign($this->getSection('body')) + ->setDesign($this->getProductTable()); + + return $this; + } + + public function getProductTable():string + { + + $table_header = $this->entity->table_header($this->input_variables['product_columns'], $this->design->table_styles()); + $table_body = $this->entity->table_body($this->input_variables['product_columns'], $this->design->table_styles()); + + $data = str_replace('$table_header', $table_header, $this->getSection('product_table')); $data = str_replace('$table_body', $table_body, $data); return $data; @@ -97,6 +136,13 @@ class Designer { return $this->html; } + public function setHtml() + { + $this->html = ''; + + return $this; + } + private function setDesign($section) { @@ -117,10 +163,10 @@ class Designer { return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{$section}()); } - private function exportVariables($entity) + private function exportVariables() { - $company = $entity->company; + $company = $this->entity->company; $this->exported_variables['$client_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['client_details']), $this->clientDetails($company)); $this->exported_variables['$company_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_details']), $this->companyDetails($company)); @@ -170,35 +216,6 @@ class Designer { return $output; } - // private function exportVariables() - // { - // /* - // * $entity_labels - // * $entity_details - // */ - // $header = $this->design->header(); - - // /* - // * $company_logo - full URL - // * $client_details - // */ - // $body = $this->design->body(); - - // /* - // * $table_header - // * $table_body - // * $total_labels - // * $total_values - // */ - // $table = $this->design->table(); - - // /* - // * $company_details - // * $company_address - // */ - // $footer = $this->design->footer(); - // } - private function clientDetails(Company $company) { diff --git a/app/Designs/Elegant.php b/app/Designs/Elegant.php index 78020a3837b1..1523055e4d58 100644 --- a/app/Designs/Elegant.php +++ b/app/Designs/Elegant.php @@ -95,8 +95,10 @@ class Elegant extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
diff --git a/app/Designs/Hipster.php b/app/Designs/Hipster.php index 89b67ea4f344..926f0b15491a 100644 --- a/app/Designs/Hipster.php +++ b/app/Designs/Hipster.php @@ -110,8 +110,10 @@ class Hipster extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
diff --git a/app/Designs/Modern.php b/app/Designs/Modern.php index ccbc98c99f86..df84ccb1c31c 100644 --- a/app/Designs/Modern.php +++ b/app/Designs/Modern.php @@ -31,6 +31,35 @@ class Modern extends AbstractDesign @@ -42,8 +71,7 @@ class Modern extends AbstractDesign public function header() { return ' - -
+

$company.name

@@ -63,6 +91,7 @@ class Modern extends AbstractDesign public function body() { return ' +
$company_logo @@ -88,12 +117,14 @@ class Modern extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
- + $table_header @@ -107,12 +138,12 @@ class Modern extends AbstractDesign
$entity.public_notes
-
-
+
+
$total_tax_labels $line_tax_labels
-
+
$total_tax_values $line_tax_values
@@ -120,7 +151,7 @@ class Modern extends AbstractDesign
-
+

$terms_label

$terms
@@ -128,8 +159,8 @@ class Modern extends AbstractDesign
-
-
+
+

$balance_due_label

$balance_due

@@ -137,13 +168,16 @@ class Modern extends AbstractDesign
+ '; } public function footer() { return ' -
+ +
+
- - + '; diff --git a/app/Designs/Photo.php b/app/Designs/Photo.php index 2ec146db90f5..3de79489f06c 100644 --- a/app/Designs/Photo.php +++ b/app/Designs/Photo.php @@ -109,8 +109,10 @@ class Photo extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
diff --git a/app/Designs/Plain.php b/app/Designs/Plain.php index 4e8196b1a2ee..d8f6f1833b66 100644 --- a/app/Designs/Plain.php +++ b/app/Designs/Plain.php @@ -96,8 +96,10 @@ class Plain extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
diff --git a/app/Designs/Playful.php b/app/Designs/Playful.php index 3dac04574e91..765ff1d9beeb 100644 --- a/app/Designs/Playful.php +++ b/app/Designs/Playful.php @@ -104,8 +104,10 @@ class Playful extends AbstractDesign ]; } - public function table() { + public function task_table() { + } + public function product_table() { return '
diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index 8ed79a833257..d989c0e8d81c 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -11,6 +11,7 @@ namespace App\Http\Controllers\ClientPortal; +use App\Events\Misc\InvitationWasViewed; use App\Http\Controllers\Controller; use App\Models\InvoiceInvitation; use App\Utils\Traits\MakesDates; diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php new file mode 100644 index 000000000000..0368fffd79a6 --- /dev/null +++ b/app/Http/Controllers/PreviewController.php @@ -0,0 +1,140 @@ +has('entity') && + request()->has('entity_id') && + request()->has('body')) + { + + $invoice_design = new Custom((object)request()->input('body')); + + $entity = ucfirst(request()->input('entity')); + + $class = "App\Models\\$entity"; + + $pdf_class = "App\Jobs\\$entity\\Create{$entity}Pdf"; + + $entity_obj = $class::whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first(); + + if(!$entity_obj) + return $this->blankEntity(); + + $entity_obj->load('client'); + + $designer = new Designer($entity_obj, $invoice_design, $entity_obj->client->getSetting('pdf_variables'), lcfirst($entity)); + + $html = $this->generateInvoiceHtml($designer->build()->getHtml(), $entity_obj); + + $file_path = PreviewPdf::dispatchNow($html, auth()->user()->company()); + + return response()->download($file_path)->deleteFileAfterSend(true); + + } + + return $this->blankEntity(); + + } + + private function blankEntity() + { + + return response()->json(['message' => 'Blank Entity not implemented.'], 200); + + // $invoice_design = new Custom((object)request()->input('body')); + + // $file_path = PreviewPdf::dispatchNow(request()->input('body'), auth()->user()->company()); + + // return response()->download($file_path)->deleteFileAfterSend(true); + } + + + +} diff --git a/app/Http/Controllers/TemplateController.php b/app/Http/Controllers/TemplateController.php index a979f92e13bd..c218bfd8e26f 100644 --- a/app/Http/Controllers/TemplateController.php +++ b/app/Http/Controllers/TemplateController.php @@ -11,10 +11,13 @@ namespace App\Http\Controllers; +use App\Utils\Traits\MakesHash; use League\CommonMark\CommonMarkConverter; class TemplateController extends BaseController { + use MakesHash; + public function __construct() { parent::__construct(); @@ -100,7 +103,7 @@ class TemplateController extends BaseController { if (request()->has('entity') && request()->has('entity_id')) { $class = 'App\Models\\'.ucfirst(request()->input('entity')); - $entity_obj = $class::whereId(request()->input('entity_id'))->company()->first(); + $entity_obj = $class::whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first(); } $subject = request()->input('subject') ?: ''; diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index 6353b58998a5..3f38584817a2 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -88,16 +88,16 @@ class StoreClientRequest extends Request if(empty($input['group_settings_id'])) { - $input['settings']->currency_id = auth()->user()->company()->settings->currency_id; + $input['settings']->currency_id =(string) auth()->user()->company()->settings->currency_id; } else { $group_settings = GroupSetting::find($input['group_settings_id']); if($group_settings && property_exists($group_settings->settings, 'currency_id') && is_int($group_settings->settings->currency_id)) - $input['settings']->currency_id = $group_settings->currency_id; + $input['settings']->currency_id = (string)$group_settings->settings->currency_id; else - $input['settings']->currency_id = auth()->user()->company()->settings->currency_id; + $input['settings']->currency_id = (string)auth()->user()->company()->settings->currency_id; } } diff --git a/app/Jobs/Credit/CreateCreditPdf.php b/app/Jobs/Credit/CreateCreditPdf.php index 73e17ab742c5..31f03b875ff4 100644 --- a/app/Jobs/Credit/CreateCreditPdf.php +++ b/app/Jobs/Credit/CreateCreditPdf.php @@ -86,10 +86,10 @@ class CreateCreditPdf implements ShouldQueue { $credit_design = new $class(); } - $designer = new Designer($credit_design, $this->credit->client->getSetting('pdf_variables'), 'credit'); + $designer = new Designer($this->credit, $credit_design, $this->credit->client->getSetting('pdf_variables'), 'credit'); //get invoice design - $html = $this->generateInvoiceHtml($designer->build($this->credit)->getHtml(), $this->credit, $this->contact); + $html = $this->generateInvoiceHtml($designer->build()->getHtml(), $this->credit, $this->contact); //todo - move this to the client creation stage so we don't keep hitting this unnecessarily Storage::makeDirectory($path, 0755); diff --git a/app/Jobs/Invoice/CreateInvoicePdf.php b/app/Jobs/Invoice/CreateInvoicePdf.php index fdec828b083d..ce757b4f408f 100644 --- a/app/Jobs/Invoice/CreateInvoicePdf.php +++ b/app/Jobs/Invoice/CreateInvoicePdf.php @@ -84,10 +84,10 @@ class CreateInvoicePdf implements ShouldQueue { $invoice_design = new $class(); } - $designer = new Designer($invoice_design, $this->invoice->client->getSetting('pdf_variables'), 'invoice'); + $designer = new Designer($this->invoice, $invoice_design, $this->invoice->client->getSetting('pdf_variables'), 'invoice'); //get invoice design - $html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice, $this->contact); + $html = $this->generateInvoiceHtml($designer->build()->getHtml(), $this->invoice, $this->contact); //todo - move this to the client creation stage so we don't keep hitting this unnecessarily Storage::makeDirectory($path, 0755); diff --git a/app/Jobs/Quote/CreateQuotePdf.php b/app/Jobs/Quote/CreateQuotePdf.php index 495b60544f9f..023aafbe72e8 100644 --- a/app/Jobs/Quote/CreateQuotePdf.php +++ b/app/Jobs/Quote/CreateQuotePdf.php @@ -65,6 +65,8 @@ class CreateQuotePdf implements ShouldQueue { MultiDB::setDB($this->company->db); + $settings = $this->quote->client->getMergedSettings(); + $this->quote->load('client'); if(!$this->contact) @@ -74,7 +76,6 @@ class CreateQuotePdf implements ShouldQueue { $path = $this->quote->client->quote_filepath(); - $file_path = $path . $this->quote->number . '.pdf'; $design = Design::find($this->quote->client->getSetting('quote_design_id')); @@ -86,16 +87,46 @@ class CreateQuotePdf implements ShouldQueue { $quote_design = new $class(); } - $designer = new Designer($quote_design, $this->quote->client->getSetting('pdf_variables'), 'quote'); - - //get invoice design - $html = $this->generateInvoiceHtml($designer->build($this->quote)->getHtml(), $this->quote, $this->contact); + $designer = new Designer($this->quote, $quote_design, $this->quote->client->getSetting('pdf_variables'), 'quote'); //todo - move this to the client creation stage so we don't keep hitting this unnecessarily Storage::makeDirectory($path, 0755); //\Log::error($html); - $pdf = $this->makePdf(null, null, $html); + + $all_pages_header = $settings->all_pages_header; + $all_pages_footer = $settings->all_pages_footer; + + $quote_number = $this->quote->number; + + + // if($all_pages_header && $all_pages_footer){ + // $all_pages_header = $designer->init()->getHeader()->getHtml(); + // $all_pages_footer = $designer->init()->getFooter()->getHtml(); + // $design_body = $designer->init()->getBody()->getHtml(); + // $quote_number = "header_and_footer"; + // } + // elseif($all_pages_header){ + // $all_pages_header = $designer->init()->getHeader()->getHtml(); + // $design_body = $designer->init()->getBody()->getFooter()->getHtml(); + // $quote_number = "header_only"; + // } + // elseif($all_pages_footer){ + // $all_pages_footer = $designer->init()->getFooter()->getHtml(); + // $design_body = $designer->init()->getHeader()->getBody()->getHtml(); + // $quote_number = "footer_only"; + // } + // else{ + $design_body = $designer->build()->getHtml(); + + + + + //get invoice design + $html = $this->generateInvoiceHtml($design_body, $this->quote, $this->contact); + + $pdf = $this->makePdf($all_pages_header, $all_pages_footer, $html); + $file_path = $path . $quote_number . '.pdf'; $instance = Storage::disk($this->disk)->put($file_path, $pdf); diff --git a/app/Jobs/Util/PreviewPdf.php b/app/Jobs/Util/PreviewPdf.php new file mode 100644 index 000000000000..b2faec401c0c --- /dev/null +++ b/app/Jobs/Util/PreviewPdf.php @@ -0,0 +1,81 @@ +company = $company; + + $this->design_string = $design_string; + + $this->disk = $disk ?? config('filesystems.default'); + + } + + public function handle() { + + + $path = $this->company->company_key; + + //Storage::makeDirectory($path, 0755); + + $file_path = $path . '/stream.pdf'; + + $pdf = $this->makePdf(null, null, $this->design_string); + + $instance = Storage::disk('local')->put($file_path, $pdf); + + return storage_path('app') .'/'. $file_path; + } + + +} diff --git a/app/Listeners/Misc/InvitationViewedListener.php b/app/Listeners/Misc/InvitationViewedListener.php index 3b3c2d6d151b..aed6d113ce9d 100644 --- a/app/Listeners/Misc/InvitationViewedListener.php +++ b/app/Listeners/Misc/InvitationViewedListener.php @@ -47,7 +47,7 @@ class InvitationViewedListener implements ShouldQueue $notification->is_system = true; - Notification::route('slack', $payment->company->slack_webhook_url) + Notification::route('slack', $invitation->company->slack_webhook_url) ->notify($notification); } diff --git a/app/Models/Client.php b/app/Models/Client.php index 3e09ccb36b8c..756461d93b52 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -448,19 +448,25 @@ class Client extends BaseModel implements HasLocalePreference public function invoice_filepath() { - return $this->client_hash . '/invoices/'; + return $this->company->company_key . '/' . $this->client_hash . '/invoices/'; } public function quote_filepath() { - return $this->client_hash . '/quotes/'; + return $this->company->company_key . '/' . $this->client_hash . '/quotes/'; } public function credit_filepath() { - return $this->client_hash . '/credits/'; + return $this->company->company_key . '/' . $this->client_hash . '/credits/'; } + public function company_filepath() + { + return $this->company->company_key . '/'; + } + + public function setInvoiceDefaults() :Invoice { $invoice_factory = InvoiceFactory::create($this->company_id, auth()->user()->id); diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 6e5e01af4442..e2e7661f9ed4 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -87,6 +87,14 @@ class Invoice extends BaseModel 'line_items', 'client_id', 'footer', + 'custom_surcharge1', + 'custom_surcharge2', + 'custom_surcharge3', + 'custom_surcharge4', + 'custom_surcharge_tax1', + 'custom_surcharge_tax2', + 'custom_surcharge_tax3', + 'custom_surcharge_tax4', ]; protected $casts = [ diff --git a/app/Notifications/Admin/EntityViewedNotification.php b/app/Notifications/Admin/EntityViewedNotification.php index 1d6459934abb..b1cfc2207961 100644 --- a/app/Notifications/Admin/EntityViewedNotification.php +++ b/app/Notifications/Admin/EntityViewedNotification.php @@ -11,7 +11,7 @@ use Illuminate\Notifications\Notification; class EntityViewedNotification extends Notification implements ShouldQueue { - use Queueable, Dispatchable; + use Queueable; /** * Create a new notification instance. @@ -36,6 +36,7 @@ class EntityViewedNotification extends Notification implements ShouldQueue public function __construct($invitation, $entity_name, $is_system = false, $settings = null) { + $this->entity_name = $entity_name; $this->entity = $invitation->{$entity_name}; $this->contact = $invitation->contact; $this->company = $invitation->company; @@ -89,16 +90,34 @@ class EntityViewedNotification extends Notification implements ShouldQueue $logo = $this->company->present()->logo(); $amount = Number::formatMoney($this->entity->amount, $this->entity->client); + // return (new SlackMessage) + // ->success() + // ->from(ctrans('texts.notification_bot')) + // ->image($logo) + // ->content(ctrans("texts.notification_{$this->entity_name}_viewed", + // [ + // 'amount' => $amount, + // 'client' => $this->contact->present()->name(), + // $this->entity_name => $this->entity->number + // ])); + return (new SlackMessage) - ->success() - ->from(ctrans('texts.notification_bot')) - ->image($logo) - ->content(ctrans("texts.notification_{$this->entity_name}_viewed", + ->from(ctrans('texts.notification_bot')) + ->success() + ->image('https://app.invoiceninja.com/favicon-v2.png') + ->content(ctrans("texts.notification_{$this->entity_name}_viewed", [ 'amount' => $amount, 'client' => $this->contact->present()->name(), $this->entity_name => $this->entity->number - ])); + ])) + ->attachment(function ($attachment) use($amount){ + $attachment->title(ctrans('texts.entity_number_placeholder', ['entity' => ucfirst($this->entity_name), 'entity_number' => $this->entity->number]), $this->invitation->getAdminLink()) + ->fields([ + ctrans('texts.client') => $this->contact->present()->name(), + ctrans('texts.status_viewed') => $this->invitation->viewed_date, + ]); + }); } @@ -107,11 +126,6 @@ class EntityViewedNotification extends Notification implements ShouldQueue { $amount = Number::formatMoney($this->entity->amount, $this->entity->client); - $subject = ctrans("texts.notification_{$this->entity_name}_viewed_subject", - [ - 'client' => $this->contact->present()->name(), - $this->entity_name => $this->entity->number, - ]); $data = [ 'title' => $subject, @@ -127,5 +141,20 @@ class EntityViewedNotification extends Notification implements ShouldQueue 'logo' => $this->company->present()->logo(), ]; + + return $data; + + } + + private function buildSubject() + { + $subject = ctrans("texts.notification_{$this->entity_name}_viewed_subject", + [ + 'client' => $this->contact->present()->name(), + $this->entity_name => $this->entity->number, + ]); + + return $subject; + } } diff --git a/app/Notifications/Admin/InvoiceSentNotification.php b/app/Notifications/Admin/InvoiceSentNotification.php index 3c3a67e4a202..994abd9d78bb 100644 --- a/app/Notifications/Admin/InvoiceSentNotification.php +++ b/app/Notifications/Admin/InvoiceSentNotification.php @@ -33,6 +33,7 @@ class InvoiceSentNotification extends Notification implements ShouldQueue public function __construct($invitation, $company, $is_system = false, $settings = null) { + $this->invitation = $invitation; $this->invoice = $invitation->invoice; $this->contact = $invitation->contact; $this->company = $company; @@ -131,7 +132,7 @@ class InvoiceSentNotification extends Notification implements ShouldQueue 'invoice' => $this->invoice->number ])) ->attachment(function ($attachment) use($amount){ - $attachment->title(ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number]), 'http://linky') + $attachment->title(ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number]), $this->invitation->getAdminLink()) ->fields([ ctrans('texts.client') => $this->contact->present()->name(), ctrans('texts.amount') => $amount, diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index dec9999af69e..d97d2af0fb10 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -127,7 +127,10 @@ class InvoiceTransformer extends EntityTransformer 'custom_surcharge2' => (float)$invoice->custom_surcharge2, 'custom_surcharge3' => (float)$invoice->custom_surcharge3, 'custom_surcharge4' => (float)$invoice->custom_surcharge4, - 'custom_surcharge_taxes' => (bool) $invoice->custom_surcharge_taxes, + 'custom_surcharge_tax1' => (float) $invoice->custom_surcharge_tax1, + 'custom_surcharge_tax2' => (float) $invoice->custom_surcharge_tax2, + 'custom_surcharge_tax3' => (float) $invoice->custom_surcharge_tax3, + 'custom_surcharge_tax4' => (float) $invoice->custom_surcharge_tax4, 'line_items' => $invoice->line_items ?: (array)[], 'backup' => $invoice->backup ?: '', 'entity_type' => 'invoice', diff --git a/app/Transformers/PaymentTransformer.php b/app/Transformers/PaymentTransformer.php index 91992e5469fa..35e89174de1b 100644 --- a/app/Transformers/PaymentTransformer.php +++ b/app/Transformers/PaymentTransformer.php @@ -80,6 +80,10 @@ class PaymentTransformer extends EntityTransformer 'invitation_id' => (string) $payment->invitation_id ?: '', 'private_notes' => (string) $payment->private_notes ?: '', 'number' => (string) $payment->number ?: '', + 'custom_value1' => (string) $payment->custom_value1 ?: '', + 'custom_value2' => (string) $payment->custom_value2 ?: '', + 'custom_value3' => (string) $payment->custom_value3 ?: '', + 'custom_value4' => (string) $payment->custom_value4 ?: '', 'client_id' => (string) $this->encodePrimaryKey($payment->client_id), 'client_contact_id' => (string) $this->encodePrimaryKey($payment->client_contact_id), 'company_gateway_id' => (string) $this->encodePrimaryKey($payment->company_gateway_id), diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index f8c0ac354e37..dafd121af306 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -505,7 +505,7 @@ trait MakesInvoiceValues } - public function table_header(array $columns, array $css) :?string + public function table_header($columns, $css) :?string { /* Table Header */ @@ -516,7 +516,7 @@ trait MakesInvoiceValues $column_headers = $this->transformColumnsForHeader($columns); foreach ($column_headers as $column) - $table_header .= ''; + $table_header .= ''; //$table_header .= ''; @@ -524,7 +524,7 @@ trait MakesInvoiceValues } - public function table_body(array $columns, array $css) :?string + public function table_body($columns, $css) :?string { $table_body = ''; @@ -538,7 +538,7 @@ trait MakesInvoiceValues $table_body .= ''; foreach ($columns as $column) { - $table_body .= ''; + $table_body .= ''; } $table_body .= ''; @@ -555,6 +555,9 @@ trait MakesInvoiceValues */ private function transformColumnsForHeader(array $columns) :array { + if(count($columns) == 0) + return []; + $pre_columns = $columns; $columns = array_intersect($columns, self::$master_columns); diff --git a/app/Utils/Traits/Pdf/PdfMaker.php b/app/Utils/Traits/Pdf/PdfMaker.php index 8a38a7c7e28d..d10191a0cab6 100644 --- a/app/Utils/Traits/Pdf/PdfMaker.php +++ b/app/Utils/Traits/Pdf/PdfMaker.php @@ -18,15 +18,44 @@ trait PdfMaker * @return string The PDF string */ public function makePdf($header, $footer, $html) { - return Browsershot::html($html) - //->showBrowserHeaderAndFooter() - //->headerHtml($header) - //->footerHtml($footer) - ->deviceScaleFactor(1) - ->showBackground() - ->waitUntilNetworkIdle(true) ->pdf(); - //->margins(10,10,10,10) - //->savePdf('test.pdf'); + + + + // if($header && $footer){ + // $browser = Browsershot::html($html) + // ->headerHtml($header) + // ->footerHtml($footer); + // } + // elseif($header){ + // $browser = Browsershot::html($html) + // ->headerHtml($header); + // } + // else if($footer){ + // $browser = Browsershot::html($html) + // ->footerHtml($footer); + // } + // else { + // $browser = Browsershot::html($html); + // } + + $browser = Browsershot::html($html); + + + return $browser->deviceScaleFactor(1) + ->showBackground() + ->deviceScaleFactor(1) + ->waitUntilNetworkIdle(true) + ->pdf(); + + // return Browsershot::html($html) + // //->showBrowserHeaderAndFooter() + // //->headerHtml($header) + // //->footerHtml($footer) + // ->deviceScaleFactor(1) + // ->showBackground() + // ->waitUntilNetworkIdle(true) ->pdf(); + // //->margins(10,10,10,10) + // //->savePdf('test.pdf'); } } diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index e64b62206512..1dc2282536a4 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -969,6 +969,9 @@ class CreateUsersTable extends Migration $t->softDeletes('deleted_at', 6); $t->boolean('is_deleted')->default(false); $t->boolean('is_manual')->default(false); + $t->decimal('exchange_rate', 16, 6)->default(1); + $t->unsignedInteger('currency_id'); + $t->unsignedInteger('exchange_currency_id'); $t->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); $t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade')->onUpdate('cascade'); diff --git a/database/seeds/DesignSeeder.php b/database/seeds/DesignSeeder.php index 440e0f726202..cb108fb3aae9 100644 --- a/database/seeds/DesignSeeder.php +++ b/database/seeds/DesignSeeder.php @@ -46,7 +46,8 @@ class DesignSeeder extends Seeder $design_object->header = $invoice_design->header(); $design_object->body = $invoice_design->body(); $design_object->table_styles = $invoice_design->table_styles(); - $design_object->table = $invoice_design->table(); + $design_object->product_table = $invoice_design->product_table(); + $design_object->task_table = $invoice_design->task_table(); $design_object->footer = $invoice_design->footer(); $design->design = $design_object; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 56e738e7d0cf..baa861a8453d 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3129,8 +3129,8 @@ $LANG = array( 'notification_partial_payment_paid' => 'A partial payment of :amount was made by client :client towards :invoice', 'notification_bot' => 'Notification Bot', 'invoice_number_placeholder' => 'Invoice # :invoice', + 'entity_number_placeholder' => ':entity # :entity_number', 'email_link_not_working' => 'If button above isn\'t working for you, please click on the link', - ); return $LANG; diff --git a/routes/api.php b/routes/api.php index 9e141ed296c7..f7451ed4a42f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -116,6 +116,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('refresh', 'Auth\LoginController@refresh'); Route::post('templates', 'TemplateController@show')->name('templates.show'); + + Route::post('preview', 'PreviewController@show')->name('previews.show'); Route::post('self-update', 'SelfUpdateController@update')->middleware('password_protected'); diff --git a/tests/Integration/DesignTest.php b/tests/Integration/DesignTest.php index e5120fb780ae..a016987c0ef6 100644 --- a/tests/Integration/DesignTest.php +++ b/tests/Integration/DesignTest.php @@ -32,9 +32,9 @@ class DesignTest extends TestCase $modern = new Modern(); - $designer = new Designer($modern, $this->company->settings->pdf_variables, 'quote'); + $designer = new Designer($this->invoice, $modern, $this->company->settings->pdf_variables, 'quote'); - $html = $designer->build($this->invoice)->getHtml(); + $html = $designer->build()->getHtml(); $this->assertNotNull($html); @@ -48,7 +48,7 @@ class DesignTest extends TestCase $this->invoice->uses_inclusive_taxes = false; $settings = $this->invoice->client->settings; - $settings->invoice_design_id = "5"; + $settings->invoice_design_id = "4"; $this->client->settings = $settings; $this->client->save(); @@ -61,16 +61,16 @@ class DesignTest extends TestCase $modern = new Modern(); - $designer = new Designer($modern, $this->company->settings->pdf_variables, 'quote'); + $designer = new Designer($this->quote, $modern, $this->company->settings->pdf_variables, 'quote'); - $html = $designer->build($this->quote)->getHtml(); + $html = $designer->build()->getHtml(); $this->assertNotNull($html); //\Log::error($html); $settings = $this->invoice->client->settings; - $settings->quote_design_id = "6"; + $settings->quote_design_id = "4"; $this->quote->client_id = $this->client->id; $this->quote->setRelation('client', $this->client); @@ -82,19 +82,101 @@ class DesignTest extends TestCase CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first()); } + // public function testQuoteDesignWithRepeatingHeader() + // { + + // $modern = new Modern(); + + // $designer = new Designer($this->quote, $modern, $this->company->settings->pdf_variables, 'quote'); + + // $html = $designer->build()->getHtml(); + + // $this->assertNotNull($html); + + // //\Log::error($html); + + // $settings = $this->invoice->client->settings; + // $settings->quote_design_id = "4"; + // $settings->all_pages_header = true; + + // $this->quote->client_id = $this->client->id; + // $this->quote->setRelation('client', $this->client); + // $this->quote->save(); + + // $this->client->settings = $settings; + // $this->client->save(); + + // CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first()); + // } + + // public function testQuoteDesignWithRepeatingFooter() + // { + + // $modern = new Modern(); + + // $designer = new Designer($this->quote, $modern, $this->company->settings->pdf_variables, 'quote'); + + // $html = $designer->build()->getHtml(); + + // $this->assertNotNull($html); + + // //\Log::error($html); + + // $settings = $this->invoice->client->settings; + // $settings->quote_design_id = "4"; + // $settings->all_pages_footer = true; + + // $this->quote->client_id = $this->client->id; + // $this->quote->setRelation('client', $this->client); + // $this->quote->save(); + + // $this->client->settings = $settings; + // $this->client->save(); + + // CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first()); + // } + + // public function testQuoteDesignWithRepeatingHeaderAndFooter() + // { + + // $modern = new Modern(); + + // $designer = new Designer($this->quote, $modern, $this->company->settings->pdf_variables, 'quote'); + + // $html = $designer->build()->getHtml(); + + // $this->assertNotNull($html); + + // //\Log::error($html); + + // $settings = $this->invoice->client->settings; + // $settings->quote_design_id = "4"; + // $settings->all_pages_header = true; + // $settings->all_pages_footer = true; + + // $this->quote->client_id = $this->client->id; + // $this->quote->setRelation('client', $this->client); + // $this->quote->save(); + + // $this->client->settings = $settings; + // $this->client->save(); + + // CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first()); + // } + public function testCreditDesignExists() { $modern = new Modern(); - $designer = new Designer($modern, $this->company->settings->pdf_variables, 'credit'); + $designer = new Designer($this->credit, $modern, $this->company->settings->pdf_variables, 'credit'); - $html = $designer->build($this->credit)->getHtml(); + $html = $designer->build()->getHtml(); $this->assertNotNull($html); $settings = $this->invoice->client->settings; - $settings->quote_design_id = "6"; + $settings->quote_design_id = "4"; $this->credit->client_id = $this->client->id; $this->credit->setRelation('client', $this->client); @@ -106,32 +188,32 @@ class DesignTest extends TestCase CreateCreditPdf::dispatchNow($this->credit, $this->credit->company, $this->credit->client->primary_contact()->first()); } - public function testAllDesigns() - { + // public function testAllDesigns() + // { - for($x=1; $x<=10; $x++) - { + // for($x=1; $x<=10; $x++) + // { - $settings = $this->invoice->client->settings; - $settings->quote_design_id = (string)$x; + // $settings = $this->invoice->client->settings; + // $settings->quote_design_id = (string)$x; - $this->quote->client_id = $this->client->id; - $this->quote->setRelation('client', $this->client); - $this->quote->save(); + // $this->quote->client_id = $this->client->id; + // $this->quote->setRelation('client', $this->client); + // $this->quote->save(); - $this->client->settings = $settings; - $this->client->save(); + // $this->client->settings = $settings; + // $this->client->save(); - CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first()); + // CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first()); - $this->quote->number = $this->getNextQuoteNumber($this->quote->client); - $this->quote->save(); + // $this->quote->number = $this->getNextQuoteNumber($this->quote->client); + // $this->quote->save(); - } + // } - $this->assertTrue(true); + // $this->assertTrue(true); - } + // } } diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index d49fa405ac46..1a18e60e05c7 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -394,6 +394,51 @@ trait MockAccountData $line_items[] = $item; + $item = InvoiceItemFactory::create(); + $item->quantity = 1; + $item->cost =10; + + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + $line_items[] = $item; + return $line_items; }
' . ctrans('texts.'.$column.'') . '' . ctrans('texts.'.$column.'') . '
'. $item->{$column} . ''. $item->{$column} . '