mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Repeating header and footers on Invoice PDFs (#3424)
* remove jobs table * Working on notifications * Working on notifications * Fixes for setting group level currency id on new client * Working on repeating headers * Use CSS to force headers and footers * recurring headers and footers * Preview PDF * Working on PDF Preview
This commit is contained in:
parent
5a7d6c4a7a
commit
7acc6ee300
@ -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 = [
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 {}
|
||||
</style>
|
||||
';
|
||||
}
|
||||
@ -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 '
|
||||
<table class="w-full table-auto mt-8">
|
||||
|
@ -106,7 +106,10 @@ class Business extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
|
||||
return '
|
||||
<table class="w-full table-auto mt-20">
|
||||
|
@ -106,8 +106,10 @@ class Clean extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
return '
|
||||
<table class="w-full table-auto mt-8">
|
||||
<thead class="text-left">
|
||||
|
@ -102,8 +102,11 @@ class Creative extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
|
||||
return '
|
||||
<table class="w-full table-auto mt-12 border-t-4 border-pink-700 bg-white">
|
||||
<thead class="text-left rounded-lg">
|
||||
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -95,8 +95,10 @@ class Elegant extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
return '
|
||||
<table class="w-full table-auto mb-6 mt-16">
|
||||
<thead class="text-left border-dashed border-b border-black">
|
||||
|
@ -110,8 +110,10 @@ class Hipster extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
return '
|
||||
<table class="w-full table-auto mt-24">
|
||||
<thead class="text-left">
|
||||
|
File diff suppressed because one or more lines are too long
@ -109,8 +109,10 @@ class Photo extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
return '
|
||||
<div class="px-16 py-16">
|
||||
<table class="w-full table-auto">
|
||||
|
@ -96,8 +96,10 @@ class Plain extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
return '
|
||||
<table class="w-full table-auto mt-8">
|
||||
<thead class="text-left bg-gray-300">
|
||||
|
@ -104,8 +104,10 @@ class Playful extends AbstractDesign
|
||||
];
|
||||
}
|
||||
|
||||
public function table() {
|
||||
public function task_table() {
|
||||
}
|
||||
|
||||
public function product_table() {
|
||||
return '
|
||||
<table class="w-full table-auto mt-20 mb-8">
|
||||
<thead class="text-left bg-teal-600 rounded-lg">
|
||||
|
@ -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;
|
||||
|
140
app/Http/Controllers/PreviewController.php
Normal file
140
app/Http/Controllers/PreviewController.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Designs\Custom;
|
||||
use App\Designs\Designer;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Jobs\Invoice\CreateInvoicePdf;
|
||||
use App\Jobs\Util\PreviewPdf;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesInvoiceHtml;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
||||
class PreviewController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
use MakesInvoiceHtml;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a template filled with entity variables
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/preview",
|
||||
* operationId="getPreview",
|
||||
* tags={"preview"},
|
||||
* summary="Returns a pdf preview",
|
||||
* description="Returns a pdf preview.",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(
|
||||
* name="entity",
|
||||
* in="path",
|
||||
* description="The PDF",
|
||||
* example="invoice",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="entity_id",
|
||||
* in="path",
|
||||
* description="The Entity ID",
|
||||
* example="X9f87dkf",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The pdf response",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
|
||||
if (request()->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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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') ?: '';
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
81
app/Jobs/Util/PreviewPdf.php
Normal file
81
app/Jobs/Util/PreviewPdf.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Util;
|
||||
|
||||
use App\Designs\Custom;
|
||||
use App\Designs\Designer;
|
||||
use App\Designs\Modern;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\Design;
|
||||
use App\Models\Invoice;
|
||||
use App\Utils\Traits\Pdf\PdfMaker;
|
||||
use App\Utils\Traits\MakesInvoiceHtml;
|
||||
use App\Utils\Traits\NumberFormatter;
|
||||
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;
|
||||
use Spatie\Browsershot\Browsershot;
|
||||
|
||||
class PreviewPdf implements ShouldQueue {
|
||||
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker;
|
||||
|
||||
public $invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public $contact;
|
||||
|
||||
private $disk;
|
||||
|
||||
public $design_string;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($design_string, Company $company)
|
||||
{
|
||||
|
||||
$this->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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 = [
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -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),
|
||||
|
@ -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 .= '<td class="'.$css['table_header_td_class'].'">' . ctrans('texts.'.$column.'') . '</td>';
|
||||
$table_header .= '<td class="table_header_td_class">' . ctrans('texts.'.$column.'') . '</td>';
|
||||
|
||||
//$table_header .= '</tr></thead>';
|
||||
|
||||
@ -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 .= '<tr class="">';
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$table_body .= '<td class="'.$css['table_body_td_class'].'">'. $item->{$column} . '</td>';
|
||||
$table_body .= '<td class="table_body_td_class">'. $item->{$column} . '</td>';
|
||||
}
|
||||
|
||||
$table_body .= '</tr>';
|
||||
@ -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);
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user