Fixes for invoice update and invoice designs (#3302)

* BaseController cleanup

* Working on invoice designs

* Working on invoice designs

* working on invoice designs

* working on invoice designs

* invoice designs

* Working on Invoice Designs

* Fixes for user settings object

* Working on invoice designs

* Fixes for encoded user settings

* Working on contact localized invoice pdfs

* working on invoice designs

* Fix for invoice update 500 error
This commit is contained in:
David Bomba 2020-02-10 20:53:02 +11:00 committed by GitHub
parent e8f19f9b63
commit 9a19f7fd4c
23 changed files with 1438 additions and 1204 deletions

View File

@ -483,7 +483,7 @@ class CreateTestData extends Command
UpdateInvoicePayment::dispatchNow($payment, $payment->company); UpdateInvoicePayment::dispatchNow($payment, $payment->company);
} }
//@todo this slow things down, but gives us PDFs of the invoices for inspection whilst debugging. //@todo this slow things down, but gives us PDFs of the invoices for inspection whilst debugging.
//event(new InvoiceWasCreated($invoice, $invoice->company)); event(new InvoiceWasCreated($invoice, $invoice->company));
} }
private function createCredit($client) private function createCredit($client)

View File

@ -217,7 +217,7 @@ class CompanySettings extends BaseSettings
public $embed_documents = false; public $embed_documents = false;
public $all_pages_header = true; public $all_pages_header = true;
public $all_pages_footer = true; public $all_pages_footer = true;
public $invoice_variables = [];
public static $casts = [ public static $casts = [
'auto_email_invoice' => 'bool', 'auto_email_invoice' => 'bool',
@ -369,6 +369,7 @@ class CompanySettings extends BaseSettings
'counter_padding' => 'integer', 'counter_padding' => 'integer',
'design' => 'string', 'design' => 'string',
'website' => 'string', 'website' => 'string',
'invoice_variables' => 'object',
]; ];
/** /**
@ -413,7 +414,8 @@ class CompanySettings extends BaseSettings
$data->date_format_id = (string)config('ninja.i18n.date_format_id'); $data->date_format_id = (string)config('ninja.i18n.date_format_id');
$data->country_id = (string)config('ninja.i18n.country_id'); $data->country_id = (string)config('ninja.i18n.country_id');
$data->translations = (object) []; $data->translations = (object) [];
$data->invoice_variables = (array) self::getInvoiceVariableDefaults();
// $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject(); // $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject();
// $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate(); // $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate();
// $data->email_subject_quote = EmailTemplateDefaults::emailQuoteSubject(); // $data->email_subject_quote = EmailTemplateDefaults::emailQuoteSubject();
@ -453,4 +455,53 @@ class CompanySettings extends BaseSettings
return $settings; return $settings;
} }
private static function getInvoiceVariableDefaults()
{
$variables = [
'client_details' => [
'name',
'id_number',
'vat_number',
'address1',
'address2',
'city_state_postal',
'country',
'email',
],
'company_details' => [
'company_name',
'id_number',
'vat_number',
'website',
'email',
'phone',
],
'company_address' => [
'address1',
'address2',
'city_state_postal',
'country',
],
'invoice_details' => [
'invoice_number',
'po_number',
'date',
'due_date',
'balance_due',
'invoice_total',
],
'table_columns' => [
'product_key',
'notes',
'cost',
'quantity',
'discount',
'tax_name1',
'line_total'
],
];
return $variables;
}
} }

View File

@ -34,7 +34,7 @@ class DefaultSettings extends BaseSettings
public static function userSettings() : \stdClass public static function userSettings() : \stdClass
{ {
return (object)[ return (object)[
class_basename(User::class) => self::userSettingsObject(), // class_basename(User::class) => self::userSettingsObject(),
]; ];
} }
@ -44,7 +44,7 @@ class DefaultSettings extends BaseSettings
private static function userSettingsObject() : \stdClass private static function userSettingsObject() : \stdClass
{ {
return (object)[ return (object)[
'per_page' => self::$per_page, // 'per_page' => self::$per_page,
]; ];
} }
} }

View File

@ -11,6 +11,7 @@
namespace App\Designs; namespace App\Designs;
use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
class Designer class Designer
@ -24,11 +25,34 @@ class Designer
protected $html; protected $html;
public function __construct($design, array $input_variables) private static $custom_fields = [
'invoice1',
'invoice2',
'invoice3',
'invoice4',
'surcharge1',
'surcharge2',
'surcharge3',
'surcharge4',
'client1',
'client2',
'client3',
'client4',
'contact1',
'contact2',
'contact3',
'contact4',
'company1',
'company2',
'company3',
'company4',
];
public function __construct($design, $input_variables)
{ {
$this->design = $design; $this->design = $design;
$this->input_variables = $input_variables; $this->input_variables = (array)$input_variables;
} }
/** /**
@ -39,7 +63,7 @@ class Designer
public function build(Invoice $invoice) :Designer public function build(Invoice $invoice) :Designer
{ {
$this->exportVariables() $this->exportVariables($invoice)
->setDesign($this->getSection('header')) ->setDesign($this->getSection('header'))
->setDesign($this->getSection('body')) ->setDesign($this->getSection('body'))
->setDesign($this->getTable($invoice)) ->setDesign($this->getTable($invoice))
@ -86,14 +110,15 @@ class Designer
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() private function exportVariables($invoice)
{ {
$company = $invoice->company;
$this->exported_variables['$client_details'] = $this->processVariables($this->input_variables['client_details'], $this->clientDetails()); $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->input_variables['company_details'], $this->companyDetails()); $this->exported_variables['$company_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_details']), $this->companyDetails($company));
$this->exported_variables['$company_address'] = $this->processVariables($this->input_variables['company_address'], $this->companyAddress()); $this->exported_variables['$company_address'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_address']), $this->companyAddress($company));
$this->exported_variables['$invoice_details_labels'] = $this->processLabels($this->input_variables['invoice_details'], $this->invoiceDetails()); $this->exported_variables['$invoice_details_labels'] = $this->processLabels($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company));
$this->exported_variables['$invoice_details'] = $this->processVariables($this->input_variables['invoice_details'], $this->invoiceDetails()); $this->exported_variables['$invoice_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company));
return $this; return $this;
} }
@ -153,10 +178,10 @@ class Designer
// $footer = $this->design->footer(); // $footer = $this->design->footer();
// } // }
private function clientDetails() private function clientDetails(Company $company)
{ {
return [ $data = [
'name' => '<p>$client.name</p>', 'name' => '<p>$client.name</p>',
'id_number' => '<p>$client.id_number</p>', 'id_number' => '<p>$client.id_number</p>',
'vat_number' => '<p>$client.vat_number</p>', 'vat_number' => '<p>$client.vat_number</p>',
@ -166,51 +191,59 @@ class Designer
'postal_city_state' => '<p>$client.postal_city_state</p>', 'postal_city_state' => '<p>$client.postal_city_state</p>',
'country' => '<p>$client.country</p>', 'country' => '<p>$client.country</p>',
'email' => '<p>$client.email</p>', 'email' => '<p>$client.email</p>',
'custom_value1' => '<p>$client.custom_value1</p>', 'client1' => '<p>$client1</p>',
'custom_value2' => '<p>$client.custom_value2</p>', 'client2' => '<p>$client2</p>',
'custom_value3' => '<p>$client.custom_value3</p>', 'client3' => '<p>$client3</p>',
'custom_value4' => '<p>$client.custom_value4</p>', 'client4' => '<p>$client4</p>',
'contact1' => '<p>$contact1</p>',
'contact2' => '<p>$contact2</p>',
'contact3' => '<p>$contact3</p>',
'contact4' => '<p>$contact4</p>',
]; ];
return $this->processCustomFields($company, $data);
} }
private function companyDetails() private function companyDetails(Company $company)
{ {
return [ $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>',
'vat_number' => '<span>$company.vat_number</span>', 'vat_number' => '<span>$company.vat_number</span>',
'website' => '<span>$company.website</span>', 'website' => '<span>$company.website</span>',
'email' => '<span>$company.email</span>', 'email' => '<span>$company.email</span>',
'phone' => '<span>$company.phone</span>', 'phone' => '<span>$company.phone</span>',
'custom_value1' => '<span>$company.custom_value1</span>', 'company1' => '<span>$company1</span>',
'custom_value2' => '<span>$company.custom_value2</span>', 'company2' => '<span>$company2</span>',
'custom_value3' => '<span>$company.custom_value3</span>', 'company3' => '<span>$company3</span>',
'custom_value4' => '<span>$company.custom_value4</span>', 'company4' => '<span>$company4</span>',
]; ];
return $this->processCustomFields($company, $data);
} }
private function companyAddress() private function companyAddress(Company $company)
{ {
return [ $data = [
'address1' => '<span>$company.address1</span>', 'address1' => '<span>$company.address1</span>',
'address2' => '<span>$company.address1</span>', 'address2' => '<span>$company.address1</span>',
'city_state_postal' => '<span>$company.city_state_postal</span>', 'city_state_postal' => '<span>$company.city_state_postal</span>',
'postal_city_state' => '<span>$company.postal_city_state</span>', 'postal_city_state' => '<span>$company.postal_city_state</span>',
'country' => '<span>$company.country</span>', 'country' => '<span>$company.country</span>',
'custom_value1' => '<span>$company.custom_value1</span>', 'company1' => '<span>$company1</span>',
'custom_value2' => '<span>$company.custom_value2</span>', 'company2' => '<span>$company2</span>',
'custom_value3' => '<span>$company.custom_value3</span>', 'company3' => '<span>$company3</span>',
'custom_value4' => '<span>$company.custom_value4</span>', 'company4' => '<span>$company4</span>',
]; ];
return $this->processCustomFields($company, $data);
} }
private function invoiceDetails() private function invoiceDetails(Company $company)
{ {
return [ $data = [
'invoice_number' => '<span>$invoice_number</span>', 'invoice_number' => '<span>$invoice_number</span>',
'po_number' => '<span>$po_number</span>', 'po_number' => '<span>$po_number</span>',
'date' => '<span>$date</span>', 'date' => '<span>$date</span>',
@ -218,11 +251,62 @@ class Designer
'balance_due' => '<span>$balance_due</span>', 'balance_due' => '<span>$balance_due</span>',
'invoice_total' => '<span>$invoice_total</span>', 'invoice_total' => '<span>$invoice_total</span>',
'partial_due' => '<span>$partial_due</span>', 'partial_due' => '<span>$partial_due</span>',
'custom_value1' => '<span>$invoice.custom_value1</span>', 'invoice1' => '<span>$invoice1</span>',
'custom_value2' => '<span>$invoice.custom_value2</span>', 'invoice2' => '<span>$invoice2</span>',
'custom_value3' => '<span>$invoice.custom_value3</span>', 'invoice3' => '<span>$invoice3</span>',
'custom_value4' => '<span>$invoice.custom_value4</span>', 'invoice4' => '<span>$invoice4</span>',
'surcharge1' =>'<span>$surcharge1</span>',
'surcharge2' =>'<span>$surcharge2</span>',
'surcharge3' =>'<span>$surcharge3</span>',
'surcharge4' =>'<span>$surcharge4</span>',
]; ];
return $this->processCustomFields($company, $data);
}
private function processCustomFields(Company $company, $data)
{
$custom_fields = $company->custom_fields;
foreach(self::$custom_fields as $cf)
{
if(!property_exists($custom_fields, $cf) || (strlen($custom_fields->{$cf}) == 0))
unset($data[$cf]);
}
return $data;
}
private function processInputVariables($company, $variables)
{
$custom_fields = $company->custom_fields;
$matches = array_intersect(self::$custom_fields, $variables);
foreach($matches as $match)
{
if(!property_exists($custom_fields, $match) || (strlen($custom_fields->{$match}) == 0))
{
foreach($variables as $key => $value)
{
if($value == $match)
{
unset($variables[$key]);
}
}
}
}
return $variables;
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,8 @@ class CompanyFactory
$company->company_key = $this->createHash(); $company->company_key = $this->createHash();
$company->settings = CompanySettings::defaults(); $company->settings = CompanySettings::defaults();
$company->db = config('database.default'); $company->db = config('database.default');
$company->custom_fields = (object) ['custom1' => '1', 'custom2' => '2', 'custom3'=>'3']; //$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'];
$company->custom_fields = (object) [];
$company->subdomain = ''; $company->subdomain = '';
return $company; return $company;

View File

@ -56,32 +56,35 @@ class BaseController extends Controller
public function __construct() public function __construct()
{ {
$this->manager = new Manager(); $this->manager = new Manager();
$this->forced_includes = []; $this->forced_includes = [];
$this->forced_index = 'data'; $this->forced_index = 'data';
} }
private function buildManager() private function buildManager()
{ {
$include = ''; $include = '';
if(request()->has('first_load') && request()->input('first_load') == 'true') { if(request()->has('first_load') && request()->input('first_load') == 'true') {
$include = $this->getRequestIncludes([]);
$include = array_merge($this->forced_includes, $include); $include = implode("," , array_merge($this->forced_includes, $this->getRequestIncludes([])));
$include = implode(",", $include);
} }
else if (request()->input('include') !== null) { else if (request()->input('include') !== null) {
$request_include = explode(",", request()->input('include'));
$include = array_merge($this->forced_includes, $request_include); $include = array_merge($this->forced_includes, explode(",", request()->input('include')));
$include = implode(",", $include); $include = implode(",", $include);
} elseif (count($this->forced_includes) >= 1) { } elseif (count($this->forced_includes) >= 1) {
$include = implode(",", $this->forced_includes); $include = implode(",", $this->forced_includes);
} }
$this->manager->parseIncludes($include); $this->manager->parseIncludes($include);
@ -89,10 +92,15 @@ class BaseController extends Controller
$this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY; $this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY;
if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) { if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) {
$this->manager->setSerializer(new JsonApiSerializer()); $this->manager->setSerializer(new JsonApiSerializer());
} else { } else {
$this->manager->setSerializer(new ArraySerializer()); $this->manager->setSerializer(new ArraySerializer());
} }
} }
/** /**
@ -101,27 +109,36 @@ class BaseController extends Controller
*/ */
public function notFound() public function notFound()
{ {
return response()->json(['message' => '404 | Nothing to see here!'], 404) return response()->json(['message' => '404 | Nothing to see here!'], 404)
->header('X-API-VERSION', config('ninja.api_version')) ->header('X-API-VERSION', config('ninja.api_version'))
->header('X-APP-VERSION', config('ninja.app_version')); ->header('X-APP-VERSION', config('ninja.app_version'));
} }
public function notFoundClient() public function notFoundClient()
{ {
return abort(404); return abort(404);
} }
protected function errorResponse($response, $httpErrorCode = 400) protected function errorResponse($response, $httpErrorCode = 400)
{ {
$error['error'] = $response; $error['error'] = $response;
$error = json_encode($error, JSON_PRETTY_PRINT); $error = json_encode($error, JSON_PRETTY_PRINT);
$headers = self::getApiHeaders(); $headers = self::getApiHeaders();
return response()->make($error, $httpErrorCode, $headers); return response()->make($error, $httpErrorCode, $headers);
} }
protected function listResponse($query) protected function listResponse($query)
{ {
$this->buildManager(); $this->buildManager();
$transformer = new $this->entity_transformer(Input::get('serializer')); $transformer = new $this->entity_transformer(Input::get('serializer'));
@ -145,32 +162,40 @@ class BaseController extends Controller
$data = $this->createCollection($query, $transformer, $this->entity_type); $data = $this->createCollection($query, $transformer, $this->entity_type);
return $this->response($data); return $this->response($data);
} }
protected function createCollection($query, $transformer, $entity_type) protected function createCollection($query, $transformer, $entity_type)
{ {
$this->buildManager(); $this->buildManager();
if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON) { if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON)
$entity_type = null; $entity_type = null;
}
if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { if (is_a($query, "Illuminate\Database\Eloquent\Builder")) {
$limit = Input::get('per_page', 20); $limit = Input::get('per_page', 20);
$paginator = $query->paginate($limit); $paginator = $query->paginate($limit);
$query = $paginator->getCollection(); $query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $entity_type); $resource = new Collection($query, $transformer, $entity_type);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
} else { } else {
$resource = new Collection($query, $transformer, $entity_type); $resource = new Collection($query, $transformer, $entity_type);
} }
return $this->manager->createData($resource)->toArray(); return $this->manager->createData($resource)->toArray();
} }
protected function response($response) protected function response($response)
{ {
$index = request()->input('index') ?: $this->forced_index; $index = request()->input('index') ?: $this->forced_index;
if ($index == 'none') { if ($index == 'none') {
@ -194,55 +219,50 @@ class BaseController extends Controller
ksort($response); ksort($response);
$response = json_encode($response, JSON_PRETTY_PRINT); $response = json_encode($response, JSON_PRETTY_PRINT);
$headers = self::getApiHeaders(); $headers = self::getApiHeaders();
return response()->make($response, 200, $headers); return response()->make($response, 200, $headers);
} }
protected function itemResponse($item) protected function itemResponse($item)
{ {
$this->buildManager(); $this->buildManager();
$transformer = new $this->entity_transformer(Input::get('serializer')); $transformer = new $this->entity_transformer(Input::get('serializer'));
$data = $this->createItem($item, $transformer, $this->entity_type); $data = $this->createItem($item, $transformer, $this->entity_type);
if (request()->include_static) { if (request()->include_static)
$data['static'] = Statics::company(auth()->user()->getCompany()->getLocale()); $data['static'] = Statics::company(auth()->user()->getCompany()->getLocale());
}
return $this->response($data); return $this->response($data);
} }
protected function createItem($data, $transformer, $entity_type) protected function createItem($data, $transformer, $entity_type)
{ {
if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON) {
$entity_type = null;
}
if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON)
$entity_type = null;
$resource = new Item($data, $transformer, $entity_type); $resource = new Item($data, $transformer, $entity_type);
return $this->manager->createData($resource)->toArray(); return $this->manager->createData($resource)->toArray();
} }
public static function getApiHeaders($count = 0) public static function getApiHeaders($count = 0)
{ {
return [ return [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
//'Access-Control-Allow-Origin' => '*',
//'Access-Control-Allow-Methods' => 'GET',
//'Access-Control-Allow-Headers' => 'Origin, Content-Type, Accept, Authorization, X-Requested-With',
//'Access-Control-Allow-Credentials' => 'true',
//'X-Total-Count' => $count,
'X-Api-Version' => config('ninja.api_version'), 'X-Api-Version' => config('ninja.api_version'),
'X-App-Version' => config('ninja.app_version'), 'X-App-Version' => config('ninja.app_version'),
//'X-Rate-Limit-Limit' - The number of allowed requests in the current period
//'X-Rate-Limit-Remaining' - The number of remaining requests in the current period
//'X-Rate-Limit-Reset' - The number of seconds left in the current period,
]; ];
} }
@ -279,7 +299,9 @@ class BaseController extends Controller
'company.groups', 'company.groups',
]; ];
/**
* Thresholds for displaying large account on first load
*/
if (request()->has('first_load') && request()->input('first_load') == 'true') if (request()->has('first_load') && request()->input('first_load') == 'true')
{ {
@ -315,4 +337,5 @@ class BaseController extends Controller
return $data; return $data;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,9 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Codedge\Updater\UpdaterManager;
use Illuminate\Foundation\Bus\DispatchesJobs;
class SelfUpdateController extends BaseController class SelfUpdateController extends BaseController
{ {
use DispatchesJobs; use DispatchesJobs;
@ -20,8 +23,10 @@ class SelfUpdateController extends BaseController
} }
public function update() public function update(UpdaterManager $updater)
{ {
$updater->update();
} }
} }

View File

@ -103,8 +103,8 @@ class TemplateController extends BaseController
$entity_obj = $class::whereId(request()->input('entity_id'))->company()->first(); $entity_obj = $class::whereId(request()->input('entity_id'))->company()->first();
} }
$subject = request()->input('subject'); $subject = request()->input('subject') ?: '';
$body = request()->input('body'); $body = request()->input('body') ?: '';
$converter = new CommonMarkConverter([ $converter = new CommonMarkConverter([
'html_input' => 'strip', 'html_input' => 'strip',

View File

@ -14,11 +14,10 @@ namespace App\Jobs\Invoice;
use App\Designs\Designer; use App\Designs\Designer;
use App\Designs\Modern; use App\Designs\Modern;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Models\ClientContact;
use App\Models\Company; use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PaymentTerm;
use App\Repositories\InvoiceRepository;
use App\Utils\Traits\MakesInvoiceHtml; use App\Utils\Traits\MakesInvoiceHtml;
use App\Utils\Traits\NumberFormatter; use App\Utils\Traits\NumberFormatter;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -26,142 +25,78 @@ 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;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Spatie\Browsershot\Browsershot; use Spatie\Browsershot\Browsershot;
use Symfony\Component\Debug\Exception\FatalThrowableError;
class CreateInvoicePdf implements ShouldQueue class CreateInvoicePdf implements ShouldQueue {
{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml;
public $invoice; public $invoice;
public $company; public $company;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Invoice $invoice, Company $company)
{
$this->invoice = $invoice;
$this->company = $company; public $contact;
} /**
* Create a new job instance.
*
* @return void
*/
public function __construct(Invoice $invoice, Company $company, ClientContact $contact) {
$this->invoice = $invoice;
public function handle() $this->company = $company;
{
MultiDB::setDB($this->company->db);
$this->contact = $contact;
}
$input_variables = [ public function handle() {
'client_details' => [ MultiDB::setDB($this->company->db);
'name',
'id_number',
'vat_number',
'address1',
'address2',
'city_state_postal',
'postal_city_state',
'country',
'email',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
],
'company_details' => [
'company_name',
'id_number',
'vat_number',
'website',
'email',
'phone',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
],
'company_address' => [
'address1',
'address2',
'city_state_postal',
'postal_city_state',
'country',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
],
'invoice_details' => [
'invoice_number',
'po_number',
'date',
'due_date',
'balance_due',
'invoice_total',
'partial_due',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
],
'table_columns' => [
'product_key',
'notes',
'cost',
'quantity',
'discount',
'tax_name1',
'line_total'
],
];
App::setLocale($this->contact->preferredLocale());
$this->invoice->load('client');
$path = 'public/'.$this->invoice->client->client_hash.'/invoices/';
$file_path = $path.$this->invoice->number.'.pdf';
$modern = new Modern();
$designer = new Designer($modern, $this->invoice->client->getSetting('invoice_variables'));
//get invoice design
$html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice, $this->contact);
$this->invoice->load('client'); //todo - move this to the client creation stage so we don't keep hitting this unnecessarily
$path = 'public/' . $this->invoice->client->client_hash . '/invoices/'; Storage::makeDirectory($path, 0755);
$file_path = $path . $this->invoice->number . '.pdf';
$modern = new Modern(); //\Log::error($html);
$designer = new Designer($modern, $input_variables); //create pdf
$pdf = $this->makePdf(null, null, $html);
//get invoice design $path = Storage::put($file_path, $pdf);
$html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice);
//todo - move this to the client creation stage so we don't keep hitting this unnecessarily return $path;
Storage::makeDirectory($path, 0755); }
\Log::error($html); /**
//create pdf * Returns a PDF stream
$pdf = $this->makePdf(null, null, $html); *
* @param string $header Header to be included in PDF
$path = Storage::put($file_path, $pdf); * @param string $footer Footer to be included in PDF
} * @param string $html The HTML object to be converted into PDF
*
/** * @return string The PDF string
* Returns a PDF stream */
* private function makePdf($header, $footer, $html) {
* @param string $header Header to be included in PDF return Browsershot::html($html)
* @param string $footer Footer to be included in PDF //->showBrowserHeaderAndFooter()
* @param string $html The HTML object to be converted into PDF //->headerHtml($header)
* //->footerHtml($footer)
* @return string The PDF string ->deviceScaleFactor(1)
*/ ->showBackground()
private function makePdf($header, $footer, $html) ->waitUntilNetworkIdle(true) ->pdf();
{ //->margins(10,10,10,10)
return Browsershot::html($html) //->savePdf('test.pdf');
//->showBrowserHeaderAndFooter() }
//->headerHtml($header)
//->footerHtml($footer)
->deviceScaleFactor(1)
->showBackground()
->waitUntilNetworkIdle(false)->pdf();
//->margins(10,10,10,10)
//->savePdf('test.pdf');
}
} }

View File

@ -35,6 +35,6 @@ class CreateInvoicePdf implements ShouldQueue
*/ */
public function handle($event) public function handle($event)
{ {
PdfCreator::dispatch($event->invoice, $event->company); PdfCreator::dispatch($event->invoice, $event->company, $event->invoice->client->primary_contact()->first());
} }
} }

View File

@ -32,13 +32,14 @@ use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Hashids\Hashids; use Hashids\Hashids;
use Illuminate\Contracts\Translation\HasLocalePreference;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\URL;
use Laracasts\Presenter\PresentableTrait; use Laracasts\Presenter\PresentableTrait;
class Client extends BaseModel class Client extends BaseModel implements HasLocalePreference
{ {
use PresentableTrait; use PresentableTrait;
use MakesHash; use MakesHash;
@ -424,4 +425,18 @@ class Client extends BaseModel
return $payment_urls; return $payment_urls;
} }
public function preferredLocale()
{
$languages = Cache::get('languages');
return $languages->filter(function ($item) {
return $item->id == $this->client->getSetting('language_id');
})->first()->locale;
//$lang = Language::find($this->client->getSetting('language_id'));
//return $lang->locale;
}
} }

View File

@ -33,7 +33,6 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Laracasts\Presenter\PresentableTrait; use Laracasts\Presenter\PresentableTrait;
@ -349,7 +348,7 @@ class Invoice extends BaseModel
if (!Storage::exists($storage_path)) { if (!Storage::exists($storage_path)) {
event(new InvoiceWasUpdated($this, $this->company)); event(new InvoiceWasUpdated($this, $this->company));
CreateInvoicePdf::dispatch($this, $this->company); CreateInvoicePdf::dispatch($this, $this->company, $this->client->primary_contact()->first());
} }
return $public_path; return $public_path;
@ -360,7 +359,7 @@ class Invoice extends BaseModel
$storage_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf'; $storage_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
if (!Storage::exists($storage_path)) { if (!Storage::exists($storage_path)) {
CreateInvoicePdf::dispatchNow($this, $this->company); CreateInvoicePdf::dispatchNow($this, $this->company, $this->client->primary_contact()->first());
} }
return $storage_path; return $storage_path;

View File

@ -14,79 +14,70 @@ namespace App\Models;
use App\Models\Invoice; use App\Models\Invoice;
use App\Utils\Traits\Inviteable; use App\Utils\Traits\Inviteable;
use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesDates;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
class InvoiceInvitation extends BaseModel class InvoiceInvitation extends BaseModel {
{ use MakesDates;
use MakesDates; use SoftDeletes;
use SoftDeletes; use Inviteable;
use Inviteable;
protected $fillable = [ protected $fillable = [
'id', //'id',
'client_contact_id', //'client_contact_id',
]; ];
protected $with = [ protected $with = [
// 'company', // 'company',
]; ];
public function entityType() public function entityType() {
{ return Invoice::class ;
return Invoice::class; }
}
/** /**
* @return mixed * @return mixed
*/ */
public function invoice() public function invoice() {
{ return $this->belongsTo(Invoice::class )->withTrashed();
return $this->belongsTo(Invoice::class)->withTrashed(); }
}
/** /**
* @return mixed * @return mixed
*/ */
public function contact() public function contact() {
{ return $this->belongsTo(ClientContact::class , 'client_contact_id', 'id')->withTrashed();
return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed(); }
}
/** /**
* @return mixed * @return mixed
*/ */
public function user() public function user() {
{ return $this->belongsTo(User::class )->withTrashed();
return $this->belongsTo(User::class)->withTrashed(); }
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/ */
public function company() public function company() {
{ return $this->belongsTo(Company::class );
return $this->belongsTo(Company::class); }
}
public function signatureDiv() public function signatureDiv() {
{ if (!$this->signature_base64) {
if (! $this->signature_base64) { return false;
return false; }
}
return sprintf('<img src="data:image/svg+xml;base64,%s"></img><p/>%s: %s', $this->signature_base64, ctrans('texts.signed'), $this->createClientDate($this->signature_date, $this->contact->client->timezone()->name)); return sprintf('<img src="data:image/svg+xml;base64,%s"></img><p/>%s: %s', $this->signature_base64, ctrans('texts.signed'), $this->createClientDate($this->signature_date, $this->contact->client->timezone()->name));
} }
public function getName() public function getName() {
{ return $this->key;
return $this->key; }
}
public function markViewed() public function markViewed() {
{ $this->viewed_date = Carbon::now();
$this->viewed_date = Carbon::now(); $this->save();
$this->save(); }
}
} }

View File

@ -11,126 +11,119 @@
namespace App\Repositories; namespace App\Repositories;
use App\Events\Invoice\InvoiceWasCreated;
use App\Events\Invoice\InvoiceWasUpdated;
use App\Factory\InvoiceInvitationFactory; use App\Factory\InvoiceInvitationFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
use App\Jobs\Product\UpdateOrCreateProduct; use App\Jobs\Product\UpdateOrCreateProduct;
use App\Listeners\Invoice\CreateInvoiceInvitation;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
/** /**
* InvoiceRepository * InvoiceRepository
*/ */
class InvoiceRepository extends BaseRepository
{
use MakesHash;
/**
* Gets the class name.
*
* @return string The class name.
*/
public function getClassName()
{
return Invoice::class;
}
/**
* Saves the invoices
*
* @param array. $data The invoice data
* @param InvoiceSum|\App\Models\Invoice $invoice The invoice
*
* @return Invoice|InvoiceSum|\App\Models\Invoice|null Returns the invoice object
*/
public function save($data, Invoice $invoice) : ?Invoice
{
/* Always carry forward the initial invoice amount this is important for tracking client balance changes later......*/ class InvoiceRepository extends BaseRepository {
$starting_amount = $invoice->amount; use MakesHash;
$invoice->fill($data); /**
* Gets the class name.
*
* @return string The class name.
*/
public function getClassName() {
return Invoice::class ;
}
$invoice->save(); /**
* Saves the invoices
*
* @param array. $data The invoice data
* @param InvoiceSum|\App\Models\Invoice $invoice The invoice
*
* @return Invoice|InvoiceSum|\App\Models\Invoice|null Returns the invoice object
*/
public function save($data, Invoice $invoice):?Invoice {
if (isset($data['client_contacts'])) { /* Always carry forward the initial invoice amount this is important for tracking client balance changes later......*/
foreach ($data['client_contacts'] as $contact) { $starting_amount = $invoice->amount;
if ($contact['send_invoice'] == 1) {
$client_contact = ClientContact::find($this->decodePrimaryKey($contact['id']));
$client_contact->send_invoice = true;
$client_contact->save();
}
}
}
if (isset($data['invitations'])) { $invoice->fill($data);
$invitations = collect($data['invitations']);
/* Get array of Keyss which have been removed from the invitations array and soft delete each invitation */ $invoice->save();
collect($invoice->invitations->pluck('key'))->diff($invitations->pluck('key'))->each(function ($invitation) {
InvoiceInvitation::destroy($invitation);
});
foreach ($data['invitations'] as $invitation) { if (isset($data['client_contacts'])) {
$inv = false; foreach ($data['client_contacts'] as $contact) {
if ($contact['send_invoice'] == 1) {
$client_contact = ClientContact::find($this->decodePrimaryKey($contact['id']));
$client_contact->send_invoice = true;
$client_contact->save();
}
}
}
if (array_key_exists('key', $invitation)) { if (isset($data['invitations'])) {
$inv = InvoiceInvitation::whereKey($invitation['key'])->first(); $invitations = collect($data['invitations']);
}
if (!$inv) { /* Get array of Keyss which have been removed from the invitations array and soft delete each invitation */
collect($invoice->invitations->pluck('key'))->diff($invitations->pluck('key'))->each(function ($invitation) {
InvoiceInvitation::destroy($invitation);
});
$new_invitation = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); foreach ($data['invitations'] as $invitation) {
$new_invitation->fill($invitation); $inv = false;
$new_invitation->invoice_id = $invoice->id;
$new_invitation->client_contact_id = $this->decodePrimaryKey($invitation['client_contact_id']);
$new_invitation->save();
}
}
}
/* If no invitations have been created, this is our fail safe to maintain state*/ if (array_key_exists('key', $invitation)) {
if ($invoice->invitations->count() == 0) { $inv = InvoiceInvitation::whereKey($invitation['key'])->first();
$invoice->service()->createInvitations(); }
}
$invoice = $invoice->calc()->getInvoice(); if (!$inv) {
$invoice->save();
$finished_amount = $invoice->amount; $new_invitation = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id);
$new_invitation->fill($invitation);
$new_invitation->invoice_id = $invoice->id;
$new_invitation->client_contact_id = $this->decodePrimaryKey($invitation['client_contact_id']);
$new_invitation->save();
/**/ }
if (($finished_amount != $starting_amount) && ($invoice->status_id != Invoice::STATUS_DRAFT)) { }
UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, ($finished_amount - $starting_amount), $invoice->company); }
}
$invoice = $invoice->service()->applyNumber()->save(); /* If no invitations have been created, this is our fail safe to maintain state*/
if ($invoice->invitations->count() == 0) {
$invoice->service()->createInvitations();
}
if ($invoice->company->update_products !== false) { $invoice = $invoice->calc()->getInvoice();
UpdateOrCreateProduct::dispatch($invoice->line_items, $invoice, $invoice->company);
}
return $invoice->fresh(); $invoice->save();
}
/** $finished_amount = $invoice->amount;
* Mark the invoice as sent.
* /**/
* @param \App\Models\Invoice $invoice The invoice if (($finished_amount != $starting_amount) && ($invoice->status_id != Invoice::STATUS_DRAFT)) {
* UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, ($finished_amount-$starting_amount), $invoice->company);
* @return Invoice|\App\Models\Invoice|null Return the invoice object }
*/
public function markSent(Invoice $invoice) : ?Invoice $invoice = $invoice->service()->applyNumber()->save();
{
return $invoice->service()->markSent()->save(); if ($invoice->company->update_products !== false) {
} UpdateOrCreateProduct::dispatch($invoice->line_items, $invoice, $invoice->company);
}
return $invoice->fresh();
}
/**
* Mark the invoice as sent.
*
* @param \App\Models\Invoice $invoice The invoice
*
* @return Invoice|\App\Models\Invoice|null Return the invoice object
*/
public function markSent(Invoice $invoice):?Invoice {
return $invoice->service()->markSent()->save();
}
} }

View File

@ -11,6 +11,7 @@
namespace App\Utils\Traits; namespace App\Utils\Traits;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Blade;
use Symfony\Component\Debug\Exception\FatalThrowableError; use Symfony\Component\Debug\Exception\FatalThrowableError;
@ -29,11 +30,20 @@ trait MakesInvoiceHtml
* *
* @return string The invoice string in HTML format * @return string The invoice string in HTML format
*/ */
public function generateInvoiceHtml($design, $invoice) :string public function generateInvoiceHtml($design, $invoice, $contact = null) :string
{ {
$variables = array_merge($invoice->makeLabels(), $invoice->makeValues()); //$variables = array_merge($invoice->makeLabels(), $invoice->makeValues());
//$design = str_replace(array_keys($variables), array_values($variables), $design);
if(!$contact)
$contact = $invoice->client->primary_contact()->first();
$design = str_replace(array_keys($variables), array_values($variables), $design); App::setLocale($contact->preferredLocale());
$labels = $invoice->makeLabels();
$values = $invoice->makeValues($contact);
$design = str_replace(array_keys($labels), array_values($labels), $design);
$design = str_replace(array_keys($values), array_values($values), $design);
$data['invoice'] = $invoice; $data['invoice'] = $invoice;

View File

@ -119,10 +119,10 @@ trait MakesInvoiceValues
'service', 'service',
'product_key', 'product_key',
'unit_cost', 'unit_cost',
'custom_value1', // 'custom_value1',
'custom_value2', // 'custom_value2',
'custom_value3', // 'custom_value3',
'custom_value4', // 'custom_value4',
'delivery_note', 'delivery_note',
'date', 'date',
'method', 'method',
@ -130,6 +130,49 @@ trait MakesInvoiceValues
'reference', 'reference',
'amount', 'amount',
'amount_paid', 'amount_paid',
'invoice1',
'invoice2',
'invoice3',
'invoice4',
'surcharge1',
'surcharge2',
'surcharge3',
'surcharge4',
'client1',
'client2',
'client3',
'client4',
'contact1',
'contact2',
'contact3',
'contact4',
'company1',
'company2',
'company3',
'company4',
];
private static $custom_label_fields = [
'invoice1',
'invoice2',
'invoice3',
'invoice4',
'surcharge1',
'surcharge2',
'surcharge3',
'surcharge4',
'client1',
'client2',
'client3',
'client4',
'contact1',
'contact2',
'contact3',
'contact4',
'company1',
'company2',
'company3',
'company4',
]; ];
/** /**
@ -150,8 +193,49 @@ trait MakesInvoiceValues
$data['$'.$label . '_label'] = ctrans('texts.'.$label); $data['$'.$label . '_label'] = ctrans('texts.'.$label);
} }
if($custom_fields && property_exists($custom_fields,'invoice_text1')) if($custom_fields)
$data['$invoice_text1'] = $custom_fields->invoice_text1; {
foreach($custom_fields as $key => $value)
{
if(strpos($value, '|') !== false)
{
$value = explode("|", $value);
$value = $value[0];
}
$data['$'.$key.'_label'] = $value;
}
}
/*
Don't forget pipe | strings for dropdowns needs to be filtered
*/
/*
invoice1
invoice2
invoice3
invoice4
surcharge1
surcharge2
surcharge3
surcharge4
client1
client2
client3
client4
contact1
contact2
contact3
contact4
*/
$arrKeysLength = array_map('strlen', array_keys($data));
array_multisort($arrKeysLength, SORT_DESC, $data);
return $data; return $data;
} }
@ -179,18 +263,18 @@ trait MakesInvoiceValues
$data['$line_tax_labels'] = $this->lineTaxLabels(); $data['$line_tax_labels'] = $this->lineTaxLabels();
$data['$line_tax_values'] = $this->lineTaxValues(); $data['$line_tax_values'] = $this->lineTaxValues();
$data['$date'] = $this->date; $data['$date'] = $this->date ?: '&nbsp;';
$data['$invoice.date'] = &$data['$date']; $data['$invoice.date'] = &$data['$date'];
$data['$due_date'] = $this->due_date; $data['$due_date'] = $this->due_date ?: '&nbsp;';
$data['$invoice.due_date'] = &$data['$due_date']; $data['$invoice.due_date'] = &$data['$due_date'];
$data['$number'] = $this->number; $data['$number'] = $this->number ?: '&nbsp;';
$data['$invoice.number'] = &$data['$number']; $data['$invoice.number'] = &$data['$number'];
$data['$invoice_number'] = &$data['$number']; $data['$invoice_number'] = &$data['$number'];
$data['$po_number'] = $this->po_number; $data['$po_number'] = $this->po_number ?: '&nbsp;';
$data['$invoice.po_number'] = &$data['$po_number']; $data['$invoice.po_number'] = &$data['$po_number'];
$data['$line_taxes'] = $this->makeLineTaxes(); $data['$line_taxes'] = $this->makeLineTaxes() ?: '&nbsp;';
$data['$invoice.line_taxes'] = &$data['$line_taxes']; $data['$invoice.line_taxes'] = &$data['$line_taxes'];
$data['$total_taxes'] = $this->makeTotalTaxes(); $data['$total_taxes'] = $this->makeTotalTaxes() ?: '&nbsp;';
$data['$invoice.total_taxes'] = &$data['$total_taxes']; $data['$invoice.total_taxes'] = &$data['$total_taxes'];
// $data['$tax'] = ; // $data['$tax'] = ;
// $data['$item'] = ; // $data['$item'] = ;
@ -199,31 +283,31 @@ trait MakesInvoiceValues
// $data['$quantity'] = ; // $data['$quantity'] = ;
// $data['$line_total'] = ; // $data['$line_total'] = ;
// $data['$paid_to_date'] = ; // $data['$paid_to_date'] = ;
$data['$discount'] = Number::formatMoney($this->calc()->getTotalDiscount(), $this->client); $data['$discount'] = Number::formatMoney($this->calc()->getTotalDiscount(), $this->client) ?: '&nbsp;';
$data['$invoice.discount'] = &$data['$discount']; $data['$invoice.discount'] = &$data['$discount'];
$data['$subtotal'] = Number::formatMoney($this->calc()->getSubTotal(), $this->client); $data['$subtotal'] = Number::formatMoney($this->calc()->getSubTotal(), $this->client) ?: '&nbsp;';
$data['$invoice.subtotal'] = &$data['$subtotal']; $data['$invoice.subtotal'] = &$data['$subtotal'];
$data['$balance_due'] = Number::formatMoney($this->balance, $this->client); $data['$balance_due'] = Number::formatMoney($this->balance, $this->client) ?: '&nbsp;';
$data['$invoice.balance_due'] = &$data['$balance_due']; $data['$invoice.balance_due'] = &$data['$balance_due'];
$data['$partial_due'] = Number::formatMoney($this->partial, $this->client); $data['$partial_due'] = Number::formatMoney($this->partial, $this->client) ?: '&nbsp;';
$data['$invoice.partial_due'] = &$data['$partial_due']; $data['$invoice.partial_due'] = &$data['$partial_due'];
$data['$total'] = Number::formatMoney($this->calc()->getTotal(), $this->client); $data['$total'] = Number::formatMoney($this->calc()->getTotal(), $this->client) ?: '&nbsp;';
$data['$invoice.total'] = &$data['$total']; $data['$invoice.total'] = &$data['$total'];
$data['$amount'] = &$data['$total']; $data['$amount'] = &$data['$total'];
$data['$invoice_total'] = &$data['$total']; $data['$invoice_total'] = &$data['$total'];
$data['$invoice.amount'] = &$data['$total']; $data['$invoice.amount'] = &$data['$total'];
$data['$balance'] = Number::formatMoney($this->calc()->getBalance(), $this->client); $data['$balance'] = Number::formatMoney($this->calc()->getBalance(), $this->client) ?: '&nbsp;';
$data['$invoice.balance'] = &$data['$balance']; $data['$invoice.balance'] = &$data['$balance'];
$data['$taxes'] = Number::formatMoney($this->calc()->getItemTotalTaxes(), $this->client); $data['$taxes'] = Number::formatMoney($this->calc()->getItemTotalTaxes(), $this->client) ?: '&nbsp;';
$data['$invoice.taxes'] = &$data['$taxes']; $data['$invoice.taxes'] = &$data['$taxes'];
$data['$terms'] = $this->terms; $data['$terms'] = $this->terms ?: '&nbsp;';
$data['$invoice.terms'] = &$data['$terms']; $data['$invoice.terms'] = &$data['$terms'];
$data['$invoice.custom_value1'] = $this->custom_value1; $data['$invoice1'] = $this->custom_value1 ?: '&nbsp;';
$data['$invoice.custom_value2'] = $this->custom_value2; $data['$invoice2'] = $this->custom_value2 ?: '&nbsp;';
$data['$invoice.custom_value3'] = $this->custom_value3; $data['$invoice3'] = $this->custom_value3 ?: '&nbsp;';
$data['$invoice.custom_value4'] = $this->custom_value4; $data['$invoice4'] = $this->custom_value4 ?: '&nbsp;';
$data['$invoice.public_notes'] = $this->public_notes; $data['$invoice.public_notes'] = $this->public_notes ?: '&nbsp;';
// $data['$your_invoice'] = ; // $data['$your_invoice'] = ;
// $data['$quote'] = ; // $data['$quote'] = ;
// $data['$your_quote'] = ; // $data['$your_quote'] = ;
@ -238,74 +322,74 @@ trait MakesInvoiceValues
// $data['$invoice_to'] = ; // $data['$invoice_to'] = ;
// $data['$quote_to'] = ; // $data['$quote_to'] = ;
// $data['$details'] = ; // $data['$details'] = ;
$data['$invoice_no'] = $this->number; $data['$invoice_no'] = $this->number ?: '&nbsp;';
$data['$invoice.invoice_no'] = &$data['$invoice_no']; $data['$invoice.invoice_no'] = &$data['$invoice_no'];
// $data['$quote_no'] = ; // $data['$quote_no'] = ;
// $data['$valid_until'] = ; // $data['$valid_until'] = ;
$data['$client_name'] = $this->present()->clientName(); $data['$client1'] = $this->client->custom_value1 ?: '&nbsp;';
$data['$client2'] = $this->client->custom_value2 ?: '&nbsp;';
$data['$client3'] = $this->client->custom_value3 ?: '&nbsp;';
$data['$client4'] = $this->client->custom_value4 ?: '&nbsp;';
$data['$client_name'] = $this->present()->clientName() ?: '&nbsp;';
$data['$client.name'] = &$data['$client_name']; $data['$client.name'] = &$data['$client_name'];
$data['$client_address'] = $this->present()->address(); $data['$address1'] = $this->client->address1 ?: '&nbsp;';
$data['$client.address'] = &$data['$client_address']; $data['$address2'] = $this->client->address2 ?: '&nbsp;';
$data['$address1'] = $this->client->address1;
$data['$client.address1'] = &$data['$address1'];
$data['$address2'] = $this->client->address2;
$data['$client.address2'] = &$data['$address2']; $data['$client.address2'] = &$data['$address2'];
$data['$id_number'] = $this->client->id_number; $data['$client.address1'] = &$data['$address1'];
$data['$client.address'] = &$data['$client_address'];
$data['$client_address'] = $this->present()->address() ?: '&nbsp;';
$data['$id_number'] = $this->client->id_number ?: '&nbsp;';
$data['$client.id_number'] = &$data['$id_number']; $data['$client.id_number'] = &$data['$id_number'];
$data['$vat_number'] = $this->client->vat_number; $data['$vat_number'] = $this->client->vat_number ?: '&nbsp;';
$data['$client.vat_number'] = &$data['$vat_number']; $data['$client.vat_number'] = &$data['$vat_number'];
$data['$website'] = $this->client->present()->website(); $data['$website'] = $this->client->present()->website() ?: '&nbsp;';
$data['$client.website'] = &$data['$website']; $data['$client.website'] = &$data['$website'];
$data['$phone'] = $this->client->present()->phone(); $data['$phone'] = $this->client->present()->phone() ?: '&nbsp;';
$data['$client.phone'] = &$data['$phone']; $data['$client.phone'] = &$data['$phone'];
$data['$city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false); $data['$city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false) ?: '&nbsp;';
$data['$client.city_state_postal'] = &$data['$city_state_postal']; $data['$client.city_state_postal'] = &$data['$city_state_postal'];
$data['$postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true); $data['$postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true) ?: '&nbsp;';
$data['$client.postal_city_state'] = &$data['$postal_city_state']; $data['$client.postal_city_state'] = &$data['$postal_city_state'];
$data['$country'] = isset($this->client->country->name) ?: 'No Country Set'; $data['$country'] = isset($this->client->country->name) ? $this->client->country->name : 'No Country Set';
$data['$client.country'] = &$data['$country']; $data['$client.country'] = &$data['$country'];
$data['$email'] = isset($this->client->primary_contact()->first()->email) ?: 'no contact email on record'; $data['$email'] = isset($this->client->primary_contact()->first()->email) ? $this->client->primary_contact()->first()->email : 'no contact email on record';
$data['$client.email'] = &$data['$email']; $data['$client.email'] = &$data['$email'];
$data['$client.custom_value1'] = $this->client->custom_value1;
$data['$client.custom_value2'] = $this->client->custom_value2;
$data['$client.custom_value3'] = $this->client->custom_value3;
$data['$client.custom_value4'] = $this->client->custom_value4;
if(!$contact) if(!$contact)
$contact = $this->client->primary_contact()->first(); $contact = $this->client->primary_contact()->first();
$data['$contact_name'] = isset($contact) ? $contact->present()->name() : 'no contact name on record'; $data['$contact_name'] = isset($contact) ? $contact->present()->name() : 'no contact name on record';
$data['$contact.name'] = &$data['$contact_name']; $data['$contact.name'] = &$data['$contact_name'];
$data['$contact.custom_value1'] = isset($contact) ? $contact->custom_value1 : ''; $data['$contact1'] = isset($contact) ? $contact->custom_value1 : '&nbsp;';
$data['$contact.custom_value2'] = isset($contact) ? $contact->custom_value2 : ''; $data['$contact2'] = isset($contact) ? $contact->custom_value2 : '&nbsp;';
$data['$contact.custom_value3'] = isset($contact) ? $contact->custom_value3 : ''; $data['$contact3'] = isset($contact) ? $contact->custom_value3 : '&nbsp;';
$data['$contact.custom_value4'] = isset($contact) ? $contact->custom_value4 : ''; $data['$contact4'] = isset($contact) ? $contact->custom_value4 : '&nbsp;';
$data['$company.city_state_postal'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, false); $data['$company.city_state_postal'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, false) ?: '&nbsp;';
$data['$company.postal_city_state'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, true); $data['$company.postal_city_state'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, true) ?: '&nbsp;';
$data['$company.name'] = $this->company->present()->name(); $data['$company.name'] = $this->company->present()->name() ?: '&nbsp;';
$data['$company.company_name'] = &$data['$company.name']; $data['$company.company_name'] = &$data['$company.name'];
$data['$company.address1'] = $settings->address1; $data['$company.address1'] = $settings->address1 ?: '&nbsp;';
$data['$company.address2'] = $settings->address2; $data['$company.address2'] = $settings->address2 ?: '&nbsp;';
$data['$company.city'] = $settings->city; $data['$company.city'] = $settings->city ?: '&nbsp;';
$data['$company.state'] = $settings->state; $data['$company.state'] = $settings->state ?: '&nbsp;';
$data['$company.postal_code'] = $settings->postal_code; $data['$company.postal_code'] = $settings->postal_code ?: '&nbsp;';
$data['$company.country'] = Country::find($settings->country_id)->first()->name; $data['$company.country'] = Country::find($settings->country_id)->first()->name ?: '&nbsp;';
$data['$company.phone'] = $settings->phone; $data['$company.phone'] = $settings->phone ?: '&nbsp;';
$data['$company.email'] = $settings->email; $data['$company.email'] = $settings->email ?: '&nbsp;';
$data['$company.vat_number'] = $settings->vat_number; $data['$company.vat_number'] = $settings->vat_number ?: '&nbsp;';
$data['$company.id_number'] = $settings->id_number; $data['$company.id_number'] = $settings->id_number ?: '&nbsp;';
$data['$company.website'] = $settings->website; $data['$company.website'] = $settings->website ?: '&nbsp;';
$data['$company.address'] = $this->company->present()->address($settings); $data['$company.address'] = $this->company->present()->address($settings) ?: '&nbsp;';
$logo = $this->company->present()->logo($settings); $logo = $this->company->present()->logo($settings);
$data['$company.logo'] = "<img src='{$logo}' class='w-48' alt='logo'>"; $data['$company.logo'] = "<img src='{$logo}' class='w-48' alt='logo'>" ?: '&nbsp;';
$data['$company_logo'] = &$data['$company.logo']; $data['$company_logo'] = &$data['$company.logo'];
$data['$company.custom_value1'] = $this->company->custom_value1; $data['$company1'] = $settings->custom_value1 ?: '&nbsp;';
$data['$company.custom_value2'] = $this->company->custom_value2; $data['$company2'] = $settings->custom_value2 ?: '&nbsp;';
$data['$company.custom_value3'] = $this->company->custom_value3; $data['$company3'] = $settings->custom_value3 ?: '&nbsp;';
$data['$company.custom_value4'] = $this->company->custom_value4; $data['$company4'] = $settings->custom_value4 ?: '&nbsp;';
//$data['$blank'] = ; //$data['$blank'] = ;
//$data['$surcharge'] = ; //$data['$surcharge'] = ;
/* /*
@ -340,6 +424,12 @@ trait MakesInvoiceValues
$data['$amount'] = ; $data['$amount'] = ;
$data['$amount_paid'] =; $data['$amount_paid'] =;
*/ */
$arrKeysLength = array_map('strlen', array_keys($data));
array_multisort($arrKeysLength, SORT_DESC, $data);
// \Log::error('woop');
//\Log::error(print_r($data,1));
return $data; return $data;
} }
@ -499,6 +589,26 @@ trait MakesInvoiceValues
$item->discount = $item->discount . '%'; $item->discount = $item->discount . '%';
} }
} }
else
$item->discount = '';
if(isset($item->tax_rate1) && $item->tax_rate1 > 0)
$item->tax_rate1 = $item->tax_rate1."%";
if(isset($item->tax_rate2) && $item->tax_rate2 > 0)
$item->tax_rate2 = $item->tax_rate2."%";
if(isset($item->tax_rate2) && $item->tax_rate2 > 0)
$item->tax_rate2 = $item->tax_rate2."%";
if(isset($item->tax_rate1) && $item->tax_rate1 == 0)
$item->tax_rate1 = '';
if(isset($item->tax_rate2) && $item->tax_rate2 == 0)
$item->tax_rate2 = '';
if(isset($item->tax_rate2) && $item->tax_rate2 == 0)
$item->tax_rate2 = '';
} }

View File

@ -2,124 +2,126 @@
return [ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Default source repository type | Default source repository type
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The default source repository type you want to pull your updates from. | The default source repository type you want to pull your updates from.
| |
*/ */
'default' => env('SELF_UPDATER_SOURCE', 'github'), 'default' => env('SELF_UPDATER_SOURCE', 'github'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Version installed | Version installed
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Set this to the version of your software installed on your system. | Set this to the version of your software installed on your system.
| |
*/ */
'version_installed' => env('SELF_UPDATER_VERSION_INSTALLED', ''), 'version_installed' => env('SELF_UPDATER_VERSION_INSTALLED', ''),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Repository types | Repository types
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| A repository can be of different types, which can be specified here. | A repository can be of different types, which can be specified here.
| Current options: | Current options:
| - github | - github
| - http | - http
| |
*/ */
'repository_types' => [ 'repository_types' => [
'github' => [ 'github' => [
'type' => 'github', 'type' => 'github',
'repository_vendor' => env('SELF_UPDATER_REPO_VENDOR', ''), 'repository_vendor' => env('SELF_UPDATER_REPO_VENDOR', ''),
'repository_name' => env('SELF_UPDATER_REPO_NAME', ''), 'repository_name' => env('SELF_UPDATER_REPO_NAME', ''),
'repository_url' => '', 'repository_url' => '',
'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
'private_access_token' => env('SELF_UPDATER_GITHUB_PRIVATE_ACCESS_TOKEN', ''), 'private_access_token' => env('SELF_UPDATER_GITHUB_PRIVATE_ACCESS_TOKEN', ''),
], 'use_branch' => env('SELF_UPDATER_BRANCH_NAME', 'v2'),
'http' => [
'type' => 'http',
'repository_url' => env('SELF_UPDATER_REPO_URL', ''),
'pkg_filename_format' => env('SELF_UPDATER_PKG_FILENAME_FORMAT', 'v_VERSION_'),
'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''),
],
],
/* ],
|-------------------------------------------------------------------------- 'http' => [
| Exclude folders from update 'type' => 'http',
|-------------------------------------------------------------------------- 'repository_url' => env('SELF_UPDATER_REPO_URL', ''),
| 'pkg_filename_format' => env('SELF_UPDATER_PKG_FILENAME_FORMAT', 'v_VERSION_'),
| Specifiy folders which should not be updated and will be skipped during the 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
| update process. 'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''),
| ],
| Here's already a list of good examples to skip. You may want to keep those. ],
|
*/
'exclude_folders' => [ /*
'node_modules', |--------------------------------------------------------------------------
'bootstrap/cache', | Exclude folders from update
'bower', |--------------------------------------------------------------------------
'storage/app', |
'storage/framework', | Specifiy folders which should not be updated and will be skipped during the
'storage/logs', | update process.
'storage/self-update', |
'vendor', | Here's already a list of good examples to skip. You may want to keep those.
], |
*/
/* 'exclude_folders' => [
|-------------------------------------------------------------------------- 'node_modules',
| Event Logging 'bootstrap/cache',
|-------------------------------------------------------------------------- 'bower',
| 'storage/app',
| Configure if fired events should be logged 'storage/framework',
| 'storage/logs',
*/ 'storage/self-update',
'vendor',
],
'log_events' => env('SELF_UPDATER_LOG_EVENTS', false), /*
|--------------------------------------------------------------------------
| Event Logging
|--------------------------------------------------------------------------
|
| Configure if fired events should be logged
|
*/
/* 'log_events' => env('SELF_UPDATER_LOG_EVENTS', false),
|--------------------------------------------------------------------------
| Mail To Settings
|--------------------------------------------------------------------------
|
| Configure if fired events should be logged
|
*/
'mail_to' => [ /*
'address' => env('SELF_UPDATER_MAILTO_ADDRESS', ''), |--------------------------------------------------------------------------
'name' => env('SELF_UPDATER_MAILTO_NAME', ''), | Mail To Settings
'subject_update_available' => env('SELF_UPDATER_MAILTO_UPDATE_AVAILABLE_SUBJECT', 'Update available'), |--------------------------------------------------------------------------
'subject_update_succeeded' => env('SELF_UPDATER_MAILTO_UPDATE_SUCCEEDED_SUBJECT', 'Update succeeded'), |
], | Configure if fired events should be logged
|
*/
/* 'mail_to' => [
|--------------------------------------------------------------------------- 'address' => env('SELF_UPDATER_MAILTO_ADDRESS', ''),
| Register custom artisan commands 'name' => env('SELF_UPDATER_MAILTO_NAME', ''),
|--------------------------------------------------------------------------- 'subject_update_available' => env('SELF_UPDATER_MAILTO_UPDATE_AVAILABLE_SUBJECT', 'Update available'),
*/ 'subject_update_succeeded' => env('SELF_UPDATER_MAILTO_UPDATE_SUCCEEDED_SUBJECT', 'Update succeeded'),
],
'artisan_commands' => [ /*
'pre_update' => [ |---------------------------------------------------------------------------
//'command:signature' => [ | Register custom artisan commands
// 'class' => Command class |---------------------------------------------------------------------------
// 'params' => [] */
//]
],
'post_update' => [
], 'artisan_commands' => [
], 'pre_update' => [
//'command:signature' => [
// 'class' => Command class
// 'params' => []
//]
],
'post_update' => [
],
],
]; ];

View File

@ -10,7 +10,7 @@ $factory->define(App\Models\Company::class, function (Faker $faker) {
'ip' => $faker->ipv4, 'ip' => $faker->ipv4,
'db' => config('database.default'), 'db' => config('database.default'),
'settings' => CompanySettings::defaults(), 'settings' => CompanySettings::defaults(),
'custom_fields' => (object) ['custom1' => '1', 'custom2' => '2', 'custom3'=>'3'], 'custom_fields' => (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'],
// 'address1' => $faker->secondaryAddress, // 'address1' => $faker->secondaryAddress,
// 'address2' => $faker->address, // 'address2' => $faker->address,

View File

@ -1,7 +1,5 @@
<?php <?php
use Illuminate\Http\Request;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| API Routes | API Routes
@ -14,120 +12,124 @@ use Illuminate\Http\Request;
Route::middleware('auth:api')->get('/user', function (Request $request) { Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user(); return $request->user();
}); });
*/ */
Route::group(['middleware' => ['api_secret_check']], function () { Route::group(['middleware' => ['api_secret_check']],
function () {
Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit'); Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit');
Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin'); Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin');
}); });
Route::group(['api_secret_check', 'email_db'], function () { Route::group(['api_secret_check', 'email_db'], function () {
Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit'); Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit');
Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset'); Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset');
}); });
Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () { Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () {
Route::resource('activities', 'ActivityController'); // name = (clients. index / create / show / update / destroy / edit Route::resource('activities', 'ActivityController');// name = (clients. index / create / show / update / destroy / edit
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit Route::resource('clients', 'ClientController');// name = (clients. index / create / show / update / destroy / edit
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk'); Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit Route::resource('invoices', 'InvoiceController');// name = (invoices. index / create / show / update / destroy / edit
Route::get('invoices/{invoice}/{action}', 'InvoiceController@action')->name('invoices.action');
Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk');
Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit Route::get('invoices/{invoice}/{action}', 'InvoiceController@action')->name('invoices.action');
Route::get('credits/{credit}/{action}', 'CreditController@action')->name('credits.action'); Route::get('invoice/{invitation_key}/download', 'InvoiceController@downloadPdf')->name('invoices.downloadPdf');
Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk'); Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk');
Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit Route::resource('credits', 'CreditController');// name = (credits. index / create / show / update / destroy / edit
Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); Route::get('credits/{credit}/{action}', 'CreditController@action')->name('credits.action');
Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk');
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk'); Route::resource('products', 'ProductController');// name = (products. index / create / show / update / destroy / edit
Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk');
Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk'); Route::resource('quotes', 'QuoteController');// name = (quotes. index / create / show / update / destroy / edit
Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk'); Route::resource('recurring_invoices', 'RecurringInvoiceController');// name = (recurring_invoices. index / create / show / update / destroy / edit
Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk');
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); Route::resource('recurring_quotes', 'RecurringQuoteController');// name = (recurring_invoices. index / create / show / update / destroy / edit
Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk');
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk'); Route::resource('expenses', 'ExpenseController');// name = (expenses. index / create / show / update / destroy / edit
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit Route::resource('vendors', 'VendorController');// name = (vendors. index / create / show / update / destroy / edit
Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund'); Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); Route::resource('client_statement', 'ClientStatementController@statement');// name = (client_statement. index / create / show / update / destroy / edit
Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start'); Route::resource('payments', 'PaymentController');// name = (payments. index / create / show / update / destroy / edit
// Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund');
Route::get('users', 'UserController@index');
Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
Route::post('users', 'UserController@store')->middleware('password_protected');
Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected'); Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start');
Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected');
// Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit
Route::get('users', 'UserController@index');
Route::put('users/{user}', 'UserController@update')->middleware('password_protected');
Route::post('users', 'UserController@store')->middleware('password_protected');
Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected');
Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected');
Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected');
Route::post('migration/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected');
Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected');
Route::post('migration/start', 'MigrationController@startMigration')->middleware('password_protected');
Route::resource('companies', 'CompanyController');// name = (companies. index / create / show / update / destroy / edit
Route::resource('company_gateways', 'CompanyGatewayController');
Route::resource('group_settings', 'GroupSettingController');
Route::resource('tax_rates', 'TaxRateController');// name = (tasks. index / create / show / update / destroy / edit
Route::post('refresh', 'Auth\LoginController@refresh');
Route::post('templates', 'TemplateController@show')->name('templates.show');
Route::post('self-update', 'SelfUpdateController@update');
/*
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected'); Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit
Route::post('migration/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected'); Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk');
Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected');
Route::post('migration/start', 'MigrationController@startMigration')->middleware('password_protected');
Route::resource('companies', 'CompanyController'); // name = (companies. index / create / show / update / destroy / edit
Route::resource('company_gateways', 'CompanyGatewayController');
Route::resource('group_settings', 'GroupSettingController');
Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('refresh', 'Auth\LoginController@refresh');
Route::post('templates', 'TemplateController@show')->name('templates.show');
/*
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit
Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk');
Route::get('settings', 'SettingsController@index')->name('user.settings'); Route::get('settings', 'SettingsController@index')->name('user.settings');
*/ */
Route::post('support/messages/send', 'Support\Messages\SendingController'); Route::post('support/messages/send', 'Support\Messages\SendingController');
}); });
Route::fallback('BaseController@notFound'); Route::fallback('BaseController@notFound');

View File

@ -281,6 +281,14 @@ class PaymentTest extends TestCase
$client = ClientFactory::create($this->company->id, $this->user->id); $client = ClientFactory::create($this->company->id, $this->user->id);
$client->save(); $client->save();
factory(\App\Models\ClientContact::class)->create([
'user_id' => $this->user->id,
'client_id' => $client->id,
'company_id' =>$this->company->id,
'is_primary' => true,
]);
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id $this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
$this->invoice->client_id = $client->id; $this->invoice->client_id = $client->id;

View File

@ -39,10 +39,14 @@ class InvoiceDesignTest extends TestCase
'postal_city_state', 'postal_city_state',
'country', 'country',
'email', 'email',
'custom_value1', 'client1',
'custom_value2', 'client2',
'custom_value3', 'client3',
'custom_value4', 'client4',
'contact1',
'contact2',
'contact3',
'contact4',
], ],
'company_details' => [ 'company_details' => [
'company_name', 'company_name',
@ -51,10 +55,10 @@ class InvoiceDesignTest extends TestCase
'website', 'website',
'email', 'email',
'phone', 'phone',
'custom_value1', 'company1',
'custom_value2', 'company2',
'custom_value3', 'company3',
'custom_value4', 'company4',
], ],
'company_address' => [ 'company_address' => [
'address1', 'address1',
@ -62,10 +66,10 @@ class InvoiceDesignTest extends TestCase
'city_state_postal', 'city_state_postal',
'postal_city_state', 'postal_city_state',
'country', 'country',
'custom_value1', 'company1',
'custom_value2', 'company2',
'custom_value3', 'company3',
'custom_value4', 'company4',
], ],
'invoice_details' => [ 'invoice_details' => [
'invoice_number', 'invoice_number',
@ -75,10 +79,14 @@ class InvoiceDesignTest extends TestCase
'balance_due', 'balance_due',
'invoice_total', 'invoice_total',
'partial_due', 'partial_due',
'custom_value1', 'invoice1',
'custom_value2', 'invoice2',
'custom_value3', 'invoice3',
'custom_value4', 'invoice4',
'surcharge1',
'surcharge2',
'surcharge3',
'surcharge4',
], ],
'table_columns' => [ 'table_columns' => [
'product_key', 'product_key',
@ -99,7 +107,7 @@ class InvoiceDesignTest extends TestCase
//\Log::error($html); //\Log::error($html);
CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company); CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company, $this->invoice->client->primary_contact()->first());
} }