Download Invoice by Invitation (#3312)

* style cs

* Style CS

* Throw Record not found exception if invalid primary key hash is provided

* Improve error handling

* Create abstract implementation for designs

* working on custom designs

* Add Design Model

* invoice services

* Download Invoice by Invitation
This commit is contained in:
David Bomba 2020-02-12 11:41:17 +11:00 committed by GitHub
parent dee99b1a62
commit be3ade65f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 498 additions and 201 deletions

View File

@ -0,0 +1,27 @@
<?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\Designs;
abstract class AbstractDesign
{
abstract public function header();
abstract public function body();
abstract public function table();
abstract public function footer();
abstract public function table_styles();
}

77
app/Designs/Custom.php Normal file
View File

@ -0,0 +1,77 @@
<?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\Designs;
class Custom extends AbstractDesign
{
private $header;
private $body;
private $table;
private $footer;
private $table_styles;
public function __construct(array $data)
{
$this->header = $data['header'];
$this->body = $data['body'];
$this->table = $data['table'];
$this->footer = $data['footer'];
$this->table_styles = $data['table_styles'];
}
public function header()
{
return $this->header;
}
public function body()
{
return $this->body;
}
public function table_styles()
{
return $this->table_styles;
}
public function table()
{
return $this->table;
}
public function footer()
{
return $this->footer;
}
}

View File

@ -14,8 +14,7 @@ namespace App\Designs;
use App\Models\Company; use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
class Designer class Designer {
{
protected $design; protected $design;
@ -48,11 +47,10 @@ class Designer
'company4', 'company4',
]; ];
public function __construct($design, $input_variables) public function __construct($design, $input_variables) {
{
$this->design = $design; $this->design = $design;
$this->input_variables = (array)$input_variables; $this->input_variables = (array) $input_variables;
} }
/** /**
@ -60,8 +58,7 @@ class Designer
* formatted HTML * formatted HTML
* @return string The HTML design built * @return string The HTML design built
*/ */
public function build(Invoice $invoice) :Designer public function build(Invoice $invoice):Designer {
{
$this->exportVariables($invoice) $this->exportVariables($invoice)
->setDesign($this->getSection('header')) ->setDesign($this->getSection('header'))
@ -72,8 +69,7 @@ class Designer
return $this; return $this;
} }
public function getTable(Invoice $invoice) :string public function getTable(Invoice $invoice):string {
{
$table_header = $invoice->table_header($this->input_variables['table_columns'], $this->design->table_styles()); $table_header = $invoice->table_header($this->input_variables['table_columns'], $this->design->table_styles());
$table_body = $invoice->table_body($this->input_variables['table_columns'], $this->design->table_styles()); $table_body = $invoice->table_body($this->input_variables['table_columns'], $this->design->table_styles());
@ -85,13 +81,11 @@ class Designer
} }
public function getHtml() :string public function getHtml():string {
{
return $this->html; return $this->html;
} }
private function setDesign($section) private function setDesign($section) {
{
$this->html .= $section; $this->html .= $section;
@ -105,13 +99,11 @@ class Designer
* @param string $section the method name to be executed ie header/body/table/footer * @param string $section the method name to be executed ie header/body/table/footer
* @return string The HTML of the template section * @return string The HTML of the template section
*/ */
public function getSection($section) :string public function getSection($section):string {
{ return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{ $section}());
return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{$section}());
} }
private function exportVariables($invoice) private function exportVariables($invoice) {
{
$company = $invoice->company; $company = $invoice->company;
$this->exported_variables['$client_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['client_details']), $this->clientDetails($company)); $this->exported_variables['$client_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['client_details']), $this->clientDetails($company));
@ -123,23 +115,21 @@ class Designer
return $this; return $this;
} }
private function processVariables($input_variables, $variables) :string private function processVariables($input_variables, $variables):string {
{
$output = ''; $output = '';
foreach($input_variables as $value) foreach ($input_variables as $value)
$output .= $variables[$value]; $output .= $variables[$value];
return $output; return $output;
} }
private function processLabels($input_variables, $variables) :string private function processLabels($input_variables, $variables):string {
{
$output = ''; $output = '';
foreach($input_variables as $value) { foreach ($input_variables as $value) {
$tmp = str_replace("</span>", "_label</span>", $variables[$value]); $tmp = str_replace("</span>", "_label</span>", $variables[$value]);
@ -178,8 +168,7 @@ class Designer
// $footer = $this->design->footer(); // $footer = $this->design->footer();
// } // }
private function clientDetails(Company $company) private function clientDetails(Company $company) {
{
$data = [ $data = [
'name' => '<p>$client.name</p>', 'name' => '<p>$client.name</p>',
@ -204,8 +193,7 @@ class Designer
return $this->processCustomFields($company, $data); return $this->processCustomFields($company, $data);
} }
private function companyDetails(Company $company) private function companyDetails(Company $company) {
{
$data = [ $data = [
'company_name' => '<span>$company.company_name</span>', 'company_name' => '<span>$company.company_name</span>',
'id_number' => '<span>$company.id_number</span>', 'id_number' => '<span>$company.id_number</span>',
@ -222,8 +210,7 @@ class Designer
return $this->processCustomFields($company, $data); return $this->processCustomFields($company, $data);
} }
private function companyAddress(Company $company) private function companyAddress(Company $company) {
{
$data = [ $data = [
'address1' => '<span>$company.address1</span>', 'address1' => '<span>$company.address1</span>',
@ -240,8 +227,7 @@ class Designer
return $this->processCustomFields($company, $data); return $this->processCustomFields($company, $data);
} }
private function invoiceDetails(Company $company) private function invoiceDetails(Company $company) {
{
$data = [ $data = [
'invoice_number' => '<span>$invoice_number</span>', 'invoice_number' => '<span>$invoice_number</span>',
@ -255,55 +241,51 @@ class Designer
'invoice2' => '<span>$invoice2</span>', 'invoice2' => '<span>$invoice2</span>',
'invoice3' => '<span>$invoice3</span>', 'invoice3' => '<span>$invoice3</span>',
'invoice4' => '<span>$invoice4</span>', 'invoice4' => '<span>$invoice4</span>',
'surcharge1' =>'<span>$surcharge1</span>', 'surcharge1' => '<span>$surcharge1</span>',
'surcharge2' =>'<span>$surcharge2</span>', 'surcharge2' => '<span>$surcharge2</span>',
'surcharge3' =>'<span>$surcharge3</span>', 'surcharge3' => '<span>$surcharge3</span>',
'surcharge4' =>'<span>$surcharge4</span>', 'surcharge4' => '<span>$surcharge4</span>',
]; ];
return $this->processCustomFields($company, $data); return $this->processCustomFields($company, $data);
} }
private function processCustomFields(Company $company, $data) private function processCustomFields(Company $company, $data) {
{
$custom_fields = $company->custom_fields; $custom_fields = $company->custom_fields;
foreach(self::$custom_fields as $cf) if (!$custom_fields) {
{ return [];
}
if(!property_exists($custom_fields, $cf) || (strlen($custom_fields->{$cf}) == 0)) foreach (self::$custom_fields as $cf) {
if (!property_exists($custom_fields, $cf) || (strlen($custom_fields->{ $cf}) == 0)) {
unset($data[$cf]); unset($data[$cf]);
} }
}
return $data; return $data;
} }
private function processInputVariables($company, $variables) private function processInputVariables($company, $variables) {
{
$custom_fields = $company->custom_fields; $custom_fields = $company->custom_fields;
$matches = array_intersect(self::$custom_fields, $variables); $matches = array_intersect(self::$custom_fields, $variables);
foreach($matches as $match) foreach ($matches as $match) {
{
if(!property_exists($custom_fields, $match) || (strlen($custom_fields->{$match}) == 0)) if (!property_exists($custom_fields, $match) || (strlen($custom_fields->{ $match}) == 0)) {
{ foreach ($variables as $key => $value) {
foreach($variables as $key => $value) if ($value == $match) {
{
if($value == $match)
{
unset($variables[$key]); unset($variables[$key]);
} }
} }
} }
} }
return $variables; return $variables;

View File

@ -11,15 +11,13 @@
namespace App\Designs; namespace App\Designs;
class Modern class Modern extends AbstractDesign
{ {
public function __construct() public function __construct() {
{
} }
public function header() public function header() {
{
return ' return '
<!DOCTYPE html> <!DOCTYPE html>
@ -53,8 +51,7 @@ class Modern
} }
public function body() public function body() {
{
return ' return '
<div class="flex justify-between px-12 pt-12"> <div class="flex justify-between px-12 pt-12">
@ -74,8 +71,7 @@ class Modern
} }
public function table_styles() public function table_styles() {
{
return [ return [
'table_header_thead_class' => "text-left text-white bg-gray-900", 'table_header_thead_class' => "text-left text-white bg-gray-900",
'table_header_td_class' => "px-4 py-2", 'table_header_td_class' => "px-4 py-2",
@ -83,8 +79,7 @@ class Modern
]; ];
} }
public function table() public function table() {
{
return ' return '
<div class="px-12 pt-5 pb-20"> <div class="px-12 pt-5 pb-20">
@ -115,14 +110,14 @@ class Modern
</div> </div>
</div> </div>
<div class="flex px-4 mt-4 w-full items-end mt-5"> <div class="flex px-4 mt-4 w-full items-end mt-5" style="page-break-inside: avoid;">
<div class="w-1/2"> <div class="w-1/2">
<p class="font-semibold">$terms_label</p> <p class="font-semibold">$terms_label</p>
$terms $terms
</div> </div>
</div> </div>
<div class="mt-8 px-4 py-2 bg-gray-900 text-white"> <div class="mt-8 px-4 py-2 bg-gray-900 text-white" style="page-break-inside: avoid;">
<div class="w-1/2"></div> <div class="w-1/2"></div>
<div class="w-auto flex justify-end"> <div class="w-auto flex justify-end">
<div class="w-56"> <div class="w-56">
@ -136,8 +131,7 @@ class Modern
'; ';
} }
public function footer() public function footer() {
{
return ' return '
<div class="bg-orange-600 flex justify-between py-8 px-12" style="page-break-inside: avoid;"> <div class="bg-orange-600 flex justify-between py-8 px-12" style="page-break-inside: avoid;">

View File

@ -28,10 +28,13 @@ use App\Jobs\Invoice\CreateInvoicePdf;
use App\Jobs\Invoice\EmailInvoice; use App\Jobs\Invoice\EmailInvoice;
use App\Jobs\Invoice\StoreInvoice; use App\Jobs\Invoice\StoreInvoice;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Repositories\InvoiceRepository; use App\Repositories\InvoiceRepository;
use App\Transformers\InvoiceTransformer; use App\Transformers\InvoiceTransformer;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
/** /**
* Class InvoiceController * Class InvoiceController
@ -57,9 +60,11 @@ class InvoiceController extends BaseController {
* @param \App\Repositories\InvoiceRepository $invoice_repo The invoice repo * @param \App\Repositories\InvoiceRepository $invoice_repo The invoice repo
*/ */
public function __construct(InvoiceRepository $invoice_repo) { public function __construct(InvoiceRepository $invoice_repo) {
parent::__construct(); parent::__construct();
$this->invoice_repo = $invoice_repo; $this->invoice_repo = $invoice_repo;
} }
/** /**
@ -75,8 +80,8 @@ class InvoiceController extends BaseController {
* tags={"invoices"}, * tags={"invoices"},
* summary="Gets a list of invoices", * summary="Gets a list of invoices",
* description="Lists invoices, search and filters allow fine grained lists to be generated. * description="Lists invoices, search and filters allow fine grained lists to be generated.
*
Query parameters can be added to performed more fine grained filtering of the invoices, these are handled by the InvoiceFilters class which defines the methods available", * Query parameters can be added to performed more fine grained filtering of the invoices, these are handled by the InvoiceFilters class which defines the methods available",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"), * @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
@ -519,17 +524,17 @@ class InvoiceController extends BaseController {
* tags={"invoices"}, * tags={"invoices"},
* summary="Performs a custom action on an invoice", * summary="Performs a custom action on an invoice",
* description="Performs a custom action on an invoice. * description="Performs a custom action on an invoice.
*
The current range of actions are as follows * The current range of actions are as follows
- clone_to_invoice * - clone_to_invoice
- clone_to_quote * - clone_to_quote
- history * - history
- delivery_note * - delivery_note
- mark_paid * - mark_paid
- download * - download
- archive * - archive
- delete * - delete
- email", * - email",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"), * @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
@ -649,11 +654,11 @@ class InvoiceController extends BaseController {
public function downloadPdf($invitation_key) { public function downloadPdf($invitation_key) {
$invitation = InvoiceInvitation::whereKey($invitation_key)->company()->first(); $invitation = $this->invoice_repo->getInvitationByKey($invitation_key);
$contact = $invitation->contact; $contact = $invitation->contact;
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
$file_path = CreateInvoicePdf::dispatchNow($invoice, $invoice->company, $contact); $file_path = $invoice->service()->getInvoicePdf(); //CreateInvoicePdf::dispatchNow($invoice, $invoice->company, $contact);
return response()->download($file_path); return response()->download($file_path);

View File

@ -39,27 +39,35 @@ class CreateInvoicePdf implements ShouldQueue {
public $company; public $company;
public $contact; public $contact;
private $disk;
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @return void * @return void
*/ */
public function __construct(Invoice $invoice, Company $company, ClientContact $contact) { public function __construct(Invoice $invoice, Company $company, ClientContact $contact, $disk = 'public')
{
$this->invoice = $invoice; $this->invoice = $invoice;
$this->company = $company; $this->company = $company;
$this->contact = $contact; $this->contact = $contact;
$this->disk = $disk ?? config('filesystems.default');
} }
public function handle() { public function handle() {
MultiDB::setDB($this->company->db); MultiDB::setDB($this->company->db);
App::setLocale($this->contact->preferredLocale()); App::setLocale($this->contact->preferredLocale());
$this->invoice->load('client'); $this->invoice->load('client');
$path = 'public/'.$this->invoice->client->client_hash.'/invoices/'; $path = $this->invoice->client->client_hash . '/invoices/';
$file_path = $path.$this->invoice->number.'.pdf'; $file_path = $path . $this->invoice->number . '.pdf';
$modern = new Modern(); $modern = new Modern();
$designer = new Designer($modern, $this->invoice->client->getSetting('invoice_variables')); $designer = new Designer($modern, $this->invoice->client->getSetting('invoice_variables'));
@ -74,9 +82,14 @@ class CreateInvoicePdf implements ShouldQueue {
//create pdf //create pdf
$pdf = $this->makePdf(null, null, $html); $pdf = $this->makePdf(null, null, $html);
$path = Storage::put($file_path, $pdf); $instance = Storage::disk($this->disk)->put($file_path, $pdf);
return $path; \Log::error($instance);
//$instance = Storage::disk($this->disk)->putFileAs($path, $pdf, $this->invoice->number . '.pdf');
//$instance = Storage::putFileAs($path, $pdf, $this->invoice->number . '.pdf');
return $instance;
} }
/** /**

View File

@ -12,15 +12,14 @@
namespace App\Jobs\Invoice; namespace App\Jobs\Invoice;
use App\Models\Invoice; use App\Models\Invoice;
use App\Repositories\InvoiceRepository;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class InvoiceNotification implements ShouldQueue class InvoiceNotification implements ShouldQueue {
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $invoice; public $invoice;
@ -30,8 +29,7 @@ class InvoiceNotification implements ShouldQueue
* *
* @return void * @return void
*/ */
public function __construct(Invoice $invoice) public function __construct(Invoice $invoice) {
{
$this->invoice = $invoice; $this->invoice = $invoice;
} }
@ -41,8 +39,7 @@ class InvoiceNotification implements ShouldQueue
* *
* @return void * @return void
*/ */
public function handle() public function handle() {
{
//notification for the invoice. //notification for the invoice.
// //

View File

@ -47,7 +47,7 @@ class UploadFile implements ShouldQueue
public $entity; public $entity;
public function __construct($file, $type, $user, $company, $entity, $disk = 'local') public function __construct($file, $type, $user, $company, $entity, $disk = 'public')
{ {
$this->file = $file; $this->file = $file;
$this->type = $type; $this->type = $type;

25
app/Models/Design.php Normal file
View File

@ -0,0 +1,25 @@
<?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\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Design extends BaseModel
{
public function company()
{
return $this->belongsTo(Company::class);
}
}

View File

@ -342,9 +342,14 @@ class Invoice extends BaseModel
/** TODO// DOCUMENT THIS FUNCTIONALITY */ /** TODO// DOCUMENT THIS FUNCTIONALITY */
public function pdf_url() public function pdf_url()
{ {
$public_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf'; // $public_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
// $storage_path = 'public/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$public_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$storage_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$storage_path = 'public/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
if (!Storage::exists($storage_path)) { if (!Storage::exists($storage_path)) {
event(new InvoiceWasUpdated($this, $this->company)); event(new InvoiceWasUpdated($this, $this->company));

View File

@ -131,4 +131,9 @@ class InvoiceRepository extends BaseRepository {
public function markSent(Invoice $invoice):?Invoice { public function markSent(Invoice $invoice):?Invoice {
return $invoice->service()->markSent()->save(); return $invoice->service()->markSent()->save();
} }
public function getInvitationByKey($key)
{
return InvoiceInvitation::whereRaw("BINARY `key`= ?", [$key])->first();
}
} }

View File

@ -0,0 +1,52 @@
<?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\Services\Invoice;
use App\Jobs\Invoice\CreateInvoicePdf;
use Illuminate\Support\Facades\Storage;
class GetInvoicePdf
{
public function __construct()
{
}
public function __invoke($invoice, $contact = null)
{
$path = $invoice->client->client_hash . '/invoices/';
$file_path = $path . $invoice->number . '.pdf';
$disk = config('filesystems.default');
$file = Storage::disk($disk)->exists($file_path);
if(!$contact)
$contact = $invoice->client->primary_contact()->first();
if(!$file)
{
$file_path = CreateInvoicePdf::dispatchNow($invoice, $invoice->company, $contact);
}
//return $file_path;
return Storage::disk($disk)->path($file_path);
}
}

View File

@ -17,6 +17,7 @@ use App\Services\Client\ClientService;
use App\Services\Invoice\ApplyNumber; use App\Services\Invoice\ApplyNumber;
use App\Services\Invoice\ApplyPayment; use App\Services\Invoice\ApplyPayment;
use App\Services\Invoice\CreateInvitations; use App\Services\Invoice\CreateInvitations;
use App\Services\Invoice\GetInvoicePdf;
use App\Services\Invoice\MarkInvoicePaid; use App\Services\Invoice\MarkInvoicePaid;
use App\Services\Invoice\MarkSent; use App\Services\Invoice\MarkSent;
use App\Services\Invoice\UpdateBalance; use App\Services\Invoice\UpdateBalance;
@ -112,6 +113,22 @@ class InvoiceService
return $this; return $this;
} }
public function getInvoicePdf()
{
$get_invoice_pdf = new GetInvoicePdf();
return $get_invoice_pdf($this->invoice);
}
public function markViewed() public function markViewed()
{ {
$this->invoice->last_viewed = Carbon::now()->format('Y-m-d H:i'); $this->invoice->last_viewed = Carbon::now()->format('Y-m-d H:i');
@ -159,7 +176,6 @@ class InvoiceService
/** /**
* Saves the invoice * Saves the invoice
* @return Invoice object * @return Invoice object

View File

@ -11,6 +11,7 @@
namespace App\Utils\Traits; namespace App\Utils\Traits;
use App\Exceptions\ModelNotFoundException;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use Hashids\Hashids; use Hashids\Hashids;
@ -69,6 +70,9 @@ trait MakesHash
// \Log::error($decoded_array); // \Log::error($decoded_array);
if(!is_array($decoded_array))
throw new ModelNotFoundException("Resource not found", 1);
return $decoded_array[0]; return $decoded_array[0];
} catch (\Exception $e) { } catch (\Exception $e) {
return response()->json(['error'=>'Invalid primary key'], 400); return response()->json(['error'=>'Invalid primary key'], 400);

View File

@ -13,7 +13,7 @@ return [
| |
*/ */
'default' => env('FILESYSTEM_DRIVER', 'local'), 'default' => env('FILESYSTEM_DRIVER', 'public'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -1353,6 +1353,20 @@ class CreateUsersTable extends Migration
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade')->onUpdate('cascade'); $table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade')->onUpdate('cascade');
}); });
Schema::create('designs', function ($table) {
$table->increments('id');
$table->unsignedInteger('user_id')->nullable();
$table->unsignedInteger('company_id')->nullable()->index();
$table->string('name');
$table->boolean('is_custom')->default(true);
$table->boolean('is_active')->default(true);
$table->mediumText('design')->nullable();
$table->timestamps(6);
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
});
} }
/** /**

View File

@ -31,6 +31,7 @@ class DatabaseSeeder extends Seeder
$this->call('PaymentTypesSeeder'); $this->call('PaymentTypesSeeder');
$this->call('GatewayTypesSeeder'); $this->call('GatewayTypesSeeder');
$this->call('DateFormatsSeeder'); $this->call('DateFormatsSeeder');
$this->call('DesignSeeder');
} }
} }

View File

@ -0,0 +1,40 @@
<?php
use App\Models\Bank;
use App\Models\Design;
use Illuminate\Database\Seeder;
class DesignSeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
$this->createDesigns();
}
private function createDesigns()
{
$designs = [
['id' => 1, 'name' => 'Plain', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 2, 'name' => 'Clean', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 3, 'name' => 'Bold', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 4, 'name' => 'Modern', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 5, 'name' => 'Business', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 6, 'name' => 'Creative', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 7, 'name' => 'Elegant', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 8, 'name' => 'Hipster', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 9, 'name' => 'Playful', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 10, 'name' => 'Photo', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
];
foreach($designs as $design) {
$d = Design::find($design['id']);
if(!$d)
Design::create($design);
}
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Tests\Integration;
use App\Jobs\Invoice\CreateInvoicePdf;
use Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
* @covers App\Services\Invoice\GetInvoicePdf
*/
class InvoiceUploadTest extends TestCase
{
use MockAccountData;
use DatabaseTransactions;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
}
public function testInvoiceUploadWorks()
{
CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company, $this->invoice->client->primary_contact()->first());
$this->assertNotNull($this->invoice->service()->getInvoicePdf());
}
}

View File

@ -19,6 +19,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;