1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 08:21:34 +02:00

Fixes and Refactors for Invoice Emails. (#3339)

* Working on emailing invoices

* Working on emailing and displaying email

* Working on emailing and displaying email

* Email invoices

* Fixes for html emails

* Ensure valid client prior to store

* Ensure client exists when storing an entity

* Update variable name send -> send_email for client_contacts

* Mailable download files

* Extend timeouts of password protected routes when a protected route is hit

* Add default portal design to company settings

* Minor fixes

* Fixes for Tests

* Fixes for invoicing emails

* Refactors for InvoiceEmail

* Implement abstractservice

* Refactors for services

* Refactors for emails

* Fixes for Invoice Emails
This commit is contained in:
David Bomba 2020-02-17 20:37:44 +11:00 committed by GitHub
parent c148157bac
commit f57339f185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 379 additions and 180 deletions

View File

@ -2,10 +2,13 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\DataMapper\DefaultSettings;
use App\Factory\ClientFactory; use App\Factory\ClientFactory;
use App\Factory\CompanyUserFactory;
use App\Factory\InvoiceFactory; use App\Factory\InvoiceFactory;
use App\Factory\InvoiceInvitationFactory; use App\Factory\InvoiceInvitationFactory;
use App\Helpers\Email\InvoiceEmail; use App\Helpers\Email\InvoiceEmail;
use App\Jobs\Invoice\CreateInvoicePdf;
use App\Mail\TemplateEmail; use App\Mail\TemplateEmail;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientContact; use App\Models\ClientContact;
@ -49,9 +52,7 @@ class SendTestEmails extends Command
public function handle() public function handle()
{ {
$this->sendTemplateEmails('plain'); $this->sendTemplateEmails('plain');
sleep(5);
$this->sendTemplateEmails('light'); $this->sendTemplateEmails('light');
sleep(5);
$this->sendTemplateEmails('dark'); $this->sendTemplateEmails('dark');
} }
@ -68,24 +69,46 @@ class SendTestEmails extends Command
$user = User::whereEmail('user@example.com')->first(); $user = User::whereEmail('user@example.com')->first();
$account = factory(\App\Models\Account::class)->create();
$company = factory(\App\Models\Company::class)->create([
'account_id' => $account->id,
]);
$client = Client::all()->first();
if (!$user) { if (!$user) {
$user = factory(\App\Models\User::class)->create([ $user = factory(\App\Models\User::class)->create([
'confirmation_code' => '123', 'confirmation_code' => '123',
'email' => $faker->safeEmail, 'email' => $faker->safeEmail,
'first_name' => 'John', 'first_name' => 'John',
'last_name' => 'Doe', 'last_name' => 'Doe',
]); ]);
$account = factory(\App\Models\Account::class)->create();
$company = factory(\App\Models\Company::class)->create([
'account_id' => $account->id,
]);
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'permissions' => '',
'settings' => DefaultSettings::userSettings(),
]);
}
else
{
$company = $user->company_users->first()->company;
$account = $company->account;
} }
$client = Client::all()->first();
if (!$client) { if (!$client) {
$client = ClientFactory::create($company->id, $user->id); $client = ClientFactory::create($company->id, $user->id);
$client->save(); $client->save();
@ -118,13 +141,18 @@ class SendTestEmails extends Command
$ii->save(); $ii->save();
$invoice->setRelation('invitations', $ii); $invoice->setRelation('invitations', $ii);
$invoice->service()->markSent()->save();
$invoice->save(); CreateInvoicePdf::dispatch($invoice, $company, $client->primary_contact()->first());
$cc_emails = [config('ninja.testvars.test_email')]; $cc_emails = [config('ninja.testvars.test_email')];
$bcc_emails = [config('ninja.testvars.test_email')]; $bcc_emails = [config('ninja.testvars.test_email')];
$email_builder = (new InvoiceEmail())->build($invoice, null, $client->primary_contact()->first()); $email_builder = (new InvoiceEmail())->build($ii, 'invoice');
$email_builder->setFooter($message['footer'])
->setSubject($message['subject'])
->setBody($message['body']);
Mail::to(config('ninja.testvars.test_email'), 'Mr Test') Mail::to(config('ninja.testvars.test_email'), 'Mr Test')
->cc($cc_emails) ->cc($cc_emails)

View File

@ -33,6 +33,8 @@ class CompanySettings extends BaseSettings {
public $document_email_attachment = false; public $document_email_attachment = false;
public $send_portal_password = false; public $send_portal_password = false;
public $portal_design_id = '1';
public $timezone_id = ''; public $timezone_id = '';
public $date_format_id = ''; public $date_format_id = '';
public $military_time = false; public $military_time = false;
@ -218,6 +220,7 @@ class CompanySettings extends BaseSettings {
public $invoice_variables = []; public $invoice_variables = [];
public static $casts = [ public static $casts = [
'portal_design_id' => 'string',
'late_fee_endless_percent' => 'float', 'late_fee_endless_percent' => 'float',
'late_fee_endless_amount' => 'float', 'late_fee_endless_amount' => 'float',
'auto_email_invoice' => 'bool', 'auto_email_invoice' => 'bool',

View File

@ -1,15 +1,13 @@
<?php <?php
namespace App\Helpers\Email; namespace App\Helpers\Email;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\Quote; use App\Models\Quote;
use League\CommonMark\CommonMarkConverter; use League\CommonMark\CommonMarkConverter;
abstract class EmailBuilder class EmailBuilder
{ {
protected $subject; protected $subject;
protected $body; protected $body;
@ -84,7 +82,7 @@ abstract class EmailBuilder
*/ */
public function setBody($body) public function setBody($body)
{ {
$this->parseTemplate($body, true); $this->body = $this->parseTemplate($body, true);
return $this; return $this;
} }

View File

@ -10,23 +10,27 @@ namespace App\Helpers\Email;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation;
class InvoiceEmail extends EmailBuilder class InvoiceEmail extends EmailBuilder
{ {
public function build(Invoice $invoice, $reminder_template, $contact = null) public function build(InvoiceInvitation $invitation, $reminder_template)
{ {
$client = $invoice->client; $client = $invitation->contact->client;
$invoice = $invitation->invoice;
$contact = $invitation->contact;
if(!$reminder_template) if(!$reminder_template)
$reminder_template = $invoice->calculateTemplate(); $reminder_template = $invoice->calculateTemplate();
$body_template = $client->getSetting('email_template_' . $reminder_template); $body_template = $client->getSetting('email_template_' . $reminder_template);
/* Use default translations if a custom message has not been set*/ /* Use default translations if a custom message has not been set*/
if (iconv_strlen($body_template) == 0) { if (iconv_strlen($body_template) == 0) {
$body_template = trans('texts.invoice_message', $body_template = trans('texts.invoice_message',
['amount' => $invoice->present()->amount(), 'company' => $invoice->company->present()->name()], null, ['invoice' => $invoice->number, 'company' => $invoice->company->present()->name()], null,
$invoice->client->locale()); $invoice->client->locale());
} }
@ -36,15 +40,15 @@ class InvoiceEmail extends EmailBuilder
if ($reminder_template == 'quote') { if ($reminder_template == 'quote') {
$subject_template = trans('texts.invoice_subject', $subject_template = trans('texts.invoice_subject',
[ [
'number' => $invoice->present()->invoice_number(), 'invoice' => $invoice->present()->invoice_number(),
'company' => $invoice->company->present()->name() 'account' => $invoice->company->present()->name()
], ],
null, $invoice->client->locale()); null, $invoice->client->locale());
} else { } else {
$subject_template = trans('texts.reminder_subject', $subject_template = trans('texts.reminder_subject',
[ [
'number' => $invoice->present()->invoice_number(), 'invoice' => $invoice->present()->invoice_number(),
'company' => $invoice->company->present()->name() 'account' => $invoice->company->present()->name()
], ],
null, $invoice->client->locale()); null, $invoice->client->locale());
} }
@ -55,7 +59,7 @@ class InvoiceEmail extends EmailBuilder
->setVariables($invoice->makeValues($contact)) ->setVariables($invoice->makeValues($contact))
->setSubject($subject_template) ->setSubject($subject_template)
->setBody($body_template) ->setBody($body_template)
->setFooter("<a href='{$invoice->invitations->first()->getLink()}'>Invoice Link</a>"); ->setFooter("<a href='{$invitation->getLink()}'>{$invitation->getLink()}</a>");
if ($client->getSetting('pdf_email_attachment') !== false) { if ($client->getSetting('pdf_email_attachment') !== false) {
$this->setAttachments($invoice->pdf_file_path()); $this->setAttachments($invoice->pdf_file_path());

View File

@ -9,15 +9,19 @@
namespace App\Helpers\Email; namespace App\Helpers\Email;
use App\Models\Quote; use App\Models\Quote;
use App\Models\QuoteInvitation;
class QuoteEmail extends EmailBuilder class QuoteEmail extends EmailBuilder
{ {
public function build(Quote $quote, $reminder_template, $contact = null) public function build(QuoteInvitation $invitation, $reminder_template)
{ {
$client = $quote->client; $client = $invitation->client;
$this->template_style = $quote->client->getSetting('email_style'); $quote = $invitation->quote;
$contact = $invitation->contact;
$this->template_style = $client->getSetting('email_style');
$body_template = $client->getSetting('email_template_' . $reminder_template); $body_template = $client->getSetting('email_template_' . $reminder_template);
@ -44,7 +48,7 @@ class QuoteEmail extends EmailBuilder
$this->setTemplate($quote->client->getSetting('email_style')) $this->setTemplate($quote->client->getSetting('email_style'))
->setContact($contact) ->setContact($contact)
->setFooter("<a href='{$quote->invitations->first()->getLink()}'>Invoice Link</a>") ->setFooter("<a href='{$invitation->getLink()}'>Quote Link</a>")
->setVariables($quote->makeValues($contact)) ->setVariables($quote->makeValues($contact))
->setSubject($subject_template) ->setSubject($subject_template)
->setBody($body_template); ->setBody($body_template);

View File

@ -666,7 +666,9 @@ class InvoiceController extends BaseController {
case 'email': case 'email':
$invoice->invitations->each(function ($invitation) use($invoice){ $invoice->invitations->each(function ($invitation) use($invoice){
EmailInvoice::dispatch((new InvoiceEmail)->build($invoice, null, null), $invitation, $invoice->company); $email_builder = (new InvoiceEmail())->build($invitation, $this->reminder_template);
EmailInvoice::dispatch($email_builder, $invitation, $invoice->company);
}); });

View File

@ -6,7 +6,6 @@
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"), * @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"), * @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"), * @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property( * @OA\Property(
* property="contacts", * property="contacts",
* type="array", * type="array",

View File

@ -36,7 +36,7 @@ class Cors
$response->headers->set('Access-Control-Allow-Origin', '*'); $response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->headers->set('Access-Control-Allow-Headers', 'X-API-SECRET,X-API-TOKEN,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'); $response->headers->set('Access-Control-Allow-Headers', 'X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range');
return $response; return $response;
} }

View File

@ -40,6 +40,10 @@ class PasswordProtection
return response()->json($error, 403); return response()->json($error, 403);
} }
} elseif (Cache::get(auth()->user()->email."_logged_in")) { } elseif (Cache::get(auth()->user()->email."_logged_in")) {
Cache::pull(auth()->user()->email."_logged_in");
Cache::add(auth()->user()->email."_logged_in", Str::random(64), now()->addMinutes(10));
return $next($request); return $next($request);
} else { } else {
$error = [ $error = [

View File

@ -40,7 +40,9 @@ class StoreCreditRequest extends FormRequest
{ {
$input = $this->all(); $input = $this->all();
$input['client_id'] = $this->decodePrimaryKey($input['client_id']); if($input['client_id'])
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']); //$input['line_items'] = json_encode($input['line_items']);
$this->replace($input); $this->replace($input);

View File

@ -46,7 +46,9 @@ class StoreInvoiceRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
$input['client_id'] = $this->decodePrimaryKey($input['client_id']); if($input['client_id'])
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']); //$input['line_items'] = json_encode($input['line_items']);
$this->replace($input); $this->replace($input);

View File

@ -45,7 +45,9 @@ class StoreRecurringInvoiceRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
$input['client_id'] = $this->decodePrimaryKey($input['client_id']); if($input['client_id'])
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']); //$input['line_items'] = json_encode($input['line_items']);
$this->replace($input); $this->replace($input);

View File

@ -45,7 +45,9 @@ class StoreRecurringQuoteRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
$input['client_id'] = $this->decodePrimaryKey($input['client_id']); if($input['client_id'])
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']); //$input['line_items'] = json_encode($input['line_items']);
$this->replace($input); $this->replace($input);

View File

@ -12,6 +12,7 @@
namespace App\Jobs\Invoice; namespace App\Jobs\Invoice;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\DownloadInvoices;
use App\Models\Company; use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -19,15 +20,16 @@ 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\Facades\Storage;
use ZipStream\Option\Archive; use ZipStream\Option\Archive;
use ZipStream\ZipStream; use ZipStream\ZipStream;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Mail;
class ZipInvoices implements ShouldQueue class ZipInvoices implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $invoice; public $invoices;
private $company; private $company;
@ -66,7 +68,7 @@ class ZipInvoices implements ShouldQueue
$zip = new ZipStream($file_name, $options); $zip = new ZipStream($file_name, $options);
foreach ($invoices as $invoice) { foreach ($this->invoices as $invoice) {
$zip->addFileFromPath(basename($invoice->pdf_file_path()), public_path($invoice->pdf_file_path())); $zip->addFileFromPath(basename($invoice->pdf_file_path()), public_path($invoice->pdf_file_path()));
} }
@ -76,9 +78,9 @@ class ZipInvoices implements ShouldQueue
fclose($tempStream); fclose($tempStream);
//fire email here //fire email here
return Storage::disk(config('filesystems.default'))->url($path . $file_name); Mail::to(config('ninja.contact.ninja_official_contact'))
->send(new DownloadInvoices(Storage::disk(config('filesystems.default'))->url($path . $file_name)));
} }
} }

View File

@ -38,6 +38,7 @@ class CreateInvoiceHtmlBackup implements ShouldQueue
*/ */
public function handle($event) public function handle($event)
{ {
MultiDB::setDB($event->company->db); MultiDB::setDB($event->company->db);
$fields = new \stdClass; $fields = new \stdClass;

View File

@ -0,0 +1,36 @@
<?php
namespace App\Mail;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class DownloadInvoices extends Mailable
{
use Queueable, SerializesModels;
public $file_path;
public function __construct($file_path)
{
$this->file_path = $file_path;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->from(config('mail.from.address')) //todo this needs to be fixed to handle the hosted version
->subject(ctrans('texts.download_documents',['size'=>'']))
->markdown('email.admin.download_files', [
'file_path' => $this->file_path
]);
}
}

View File

@ -56,7 +56,7 @@ class Client extends BaseModel implements HasLocalePreference
'user_id', 'user_id',
'company_id', 'company_id',
'backup', 'backup',
'settings', // 'settings',
'last_login', 'last_login',
'private_notes' 'private_notes'
]; ];
@ -245,6 +245,7 @@ class Client extends BaseModel implements HasLocalePreference
*/ */
public function getMergedSettings() :object public function getMergedSettings() :object
{ {
if ($this->group_settings !== null) { if ($this->group_settings !== null) {
$group_settings = ClientSettings::buildClientSettings($this->group_settings->settings, $this->settings); $group_settings = ClientSettings::buildClientSettings($this->group_settings->settings, $this->settings);
@ -431,7 +432,7 @@ class Client extends BaseModel implements HasLocalePreference
$languages = Cache::get('languages'); $languages = Cache::get('languages');
return $languages->filter(function ($item) { return $languages->filter(function ($item) {
return $item->id == $this->client->getSetting('language_id'); return $item->id == $this->getSetting('language_id');
})->first()->locale; })->first()->locale;
//$lang = Language::find($this->client->getSetting('language_id')); //$lang = Language::find($this->client->getSetting('language_id'));

View File

@ -0,0 +1,19 @@
<?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;
abstract class AbstractService
{
abstract public function run();
}

View File

@ -5,34 +5,36 @@ namespace App\Services\Credit;
use App\Credit; use App\Credit;
use App\Events\Payment\PaymentWasCreated; use App\Events\Payment\PaymentWasCreated;
use App\Factory\PaymentFactory; use App\Factory\PaymentFactory;
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
use App\Jobs\Customer\UpdateCustomerBalance; use App\Jobs\Customer\UpdateCustomerBalance;
use App\Jobs\Customer\UpdateCustomerPaidToDate; use App\Jobs\Customer\UpdateCustomerPaidToDate;
use App\Jobs\Company\UpdateCompanyLedgerWithPayment; use App\Models\Client;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Services\AbstractService;
use App\Services\Customer\CustomerService; use App\Services\Customer\CustomerService;
use App\Services\Payment\PaymentService; use App\Services\Payment\PaymentService;
use App\Traits\GeneratesCounter; use App\Traits\GeneratesCounter;
class ApplyNumber class ApplyNumber extends AbstractService
{ {
use GeneratesCounter; use GeneratesCounter;
private $customer; private $customer;
public function __construct($customer) public function __construct(Client $client)
{ {
$this->customer = $customer; $this->client = $client;
} }
public function __invoke($credit) public function run($credit)
{ {
if ($credit->number != '') { if ($credit->number != '') {
return $credit; return $credit;
} }
$credit->number = $this->getNextCreditNumber($this->customer); $credit->number = $this->getNextCreditNumber($this->client);
return $credit; return $credit;

View File

@ -3,18 +3,19 @@ namespace App\Services\Credit;
use App\Factory\CreditInvitationFactory; use App\Factory\CreditInvitationFactory;
use App\Models\CreditInvitation; use App\Models\CreditInvitation;
use App\Services\AbstractService;
class CreateInvitations class CreateInvitations extends AbstractService
{ {
public function __construct() public function __construct()
{ {
} }
public function __invoke($credit) public function run($credit)
{ {
$contacts = $credit->customer->contacts; $contacts = $credit->client->contacts;
$contacts->each(function ($contact) use($credit){ $contacts->each(function ($contact) use($credit){
$invitation = CreditInvitation::whereCompanyId($credit->account_id) $invitation = CreditInvitation::whereCompanyId($credit->account_id)
@ -23,11 +24,11 @@ class CreateInvitations
->first(); ->first();
if (!$invitation) { if (!$invitation) {
$ii = CreditInvitationFactory::create($credit->account_id, $credit->user_id); $ii = CreditInvitationFactory::create($credit->company_id, $credit->user_id);
$ii->credit_id = $credit->id; $ii->credit_id = $credit->id;
$ii->client_contact_id = $contact->id; $ii->client_contact_id = $contact->id;
$ii->save(); $ii->save();
} elseif ($invitation && !$contact->send_credit) { } elseif ($invitation && !$contact->send_email) {
$invitation->delete(); $invitation->delete();
} }
}); });

View File

@ -3,9 +3,10 @@
namespace App\Services\Credit; namespace App\Services\Credit;
use App\Jobs\Invoice\CreateInvoicePdf; use App\Jobs\Invoice\CreateInvoicePdf;
use App\Services\AbstractService;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class GetCreditPdf class GetCreditPdf extends AbstractService
{ {
public function __construct() public function __construct()
@ -15,16 +16,16 @@ class GetCreditPdf
public function __invoke($credit, $contact = null) public function __invoke($credit, $contact = null)
{ {
if (!$contact) { if (!$contact) {
$contact = $credit->customer->primary_contact()->first(); $contact = $credit->client->primary_contact()->first();
} }
$path = 'public/' . $credit->customer->id . '/credits/'; $path = 'public/' . $credit->client->id . '/credits/';
$file_path = $path . $credit->number . '.pdf'; $file_path = $path . $credit->number . '.pdf';
$disk = config('filesystems.default'); $disk = config('filesystems.default');
$file = Storage::disk($disk)->exists($file_path); $file = Storage::disk($disk)->exists($file_path);
if (!$file) { if (!$file) {
$file_path = CreateInvoicePdf::dispatchNow($this, $credit->account, $contact); $file_path = CreateInvoicePdf::dispatchNow($this, $credit->company, $contact);
} }
return Storage::disk($disk)->url($file_path); return Storage::disk($disk)->url($file_path);

View File

@ -14,35 +14,41 @@ namespace App\Services\Invoice;
use App\Events\Payment\PaymentWasCreated; use App\Events\Payment\PaymentWasCreated;
use App\Factory\PaymentFactory; use App\Factory\PaymentFactory;
use App\Jobs\Company\UpdateCompanyLedgerWithPayment; use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
use App\Models\Client;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Services\AbstractService;
use App\Services\Client\ClientService; use App\Services\Client\ClientService;
use App\Services\Payment\PaymentService; use App\Services\Payment\PaymentService;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
class ApplyNumber class ApplyNumber extends AbstractService
{ {
use GeneratesCounter; use GeneratesCounter;
private $client; private $client;
public function __construct($client) private $invoice;
public function __construct(Client $client, Invoice $invoice)
{ {
$this->client = $client; $this->client = $client;
$this->invoice = $invoice;
} }
public function run($invoice) public function run()
{ {
if ($invoice->number != '') if ($this->invoice->number != '')
return $invoice; return $this->invoice;
switch ($this->client->getSetting('counter_number_applied')) { switch ($this->client->getSetting('counter_number_applied')) {
case 'when_saved': case 'when_saved':
$invoice->number = $this->getNextInvoiceNumber($this->client); $this->invoice->number = $this->getNextInvoiceNumber($this->client);
break; break;
case 'when_sent': case 'when_sent':
if ($invoice->status_id == Invoice::STATUS_SENT) { if ($this->invoice->status_id == Invoice::STATUS_SENT) {
$invoice->number = $this->getNextInvoiceNumber($this->client); $this->invoice->number = $this->getNextInvoiceNumber($this->client);
} }
break; break;
@ -51,6 +57,6 @@ class ApplyNumber
break; break;
} }
return $invoice; return $this->invoice;
} }
} }

View File

@ -14,46 +14,53 @@ namespace App\Services\Invoice;
use App\Jobs\Company\UpdateCompanyLedgerWithPayment; use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Services\AbstractService;
use App\Services\Client\ClientService; use App\Services\Client\ClientService;
class ApplyPayment class ApplyPayment extends AbstractService
{ {
private $invoice; private $invoice;
public function __construct($invoice) private $payment;
private $payment_amount;
public function __construct($invoice, $payment, $payment_amount)
{ {
$this->invoice = $invoice; $this->invoice = $invoice;
$this->payment = $payment;
$this->payment_amount = $payment_amount;
} }
public function run($payment, $payment_amount) public function run()
{ {
UpdateCompanyLedgerWithPayment::dispatchNow($payment, ($payment_amount*-1), $payment->company); UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($this->payment_amount*-1), $this->payment->company);
$payment->client->service()->updateBalance($payment_amount*-1)->save(); $this->payment->client->service()->updateBalance($this->payment_amount*-1)->save();
/* Update Pivot Record amount */ /* Update Pivot Record amount */
$payment->invoices->each(function ($inv) use($payment_amount){ $this->payment->invoices->each(function ($inv){
if ($inv->id == $this->invoice->id) { if ($inv->id == $this->invoice->id) {
$inv->pivot->amount = $payment_amount; $inv->pivot->amount = $this->payment_amount;
$inv->pivot->save(); $inv->pivot->save();
} }
}); });
if ($this->invoice->hasPartial()) { if ($this->invoice->hasPartial()) {
//is partial and amount is exactly the partial amount //is partial and amount is exactly the partial amount
if ($this->invoice->partial == $payment_amount) { if ($this->invoice->partial == $this->payment_amount) {
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($payment_amount*-1); $this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($this->payment_amount*-1);
} elseif ($this->invoice->partial > 0 && $this->invoice->partial > $payment_amount) { //partial amount exists, but the amount is less than the partial amount } elseif ($this->invoice->partial > 0 && $this->invoice->partial > $this->payment_amount) { //partial amount exists, but the amount is less than the partial amount
$this->invoice->service()->updatePartial($payment_amount*-1)->updateBalance($payment_amount*-1); $this->invoice->service()->updatePartial($this->payment_amount*-1)->updateBalance($this->payment_amount*-1);
} elseif ($this->invoice->partial > 0 && $this->invoice->partial < $payment_amount) { //partial exists and the amount paid is GREATER than the partial amount } elseif ($this->invoice->partial > 0 && $this->invoice->partial < $this->payment_amount) { //partial exists and the amount paid is GREATER than the partial amount
$this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($payment_amount*-1); $this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($this->payment_amount*-1);
} }
} elseif ($payment_amount == $this->invoice->balance) { //total invoice paid. } elseif ($this->payment_amount == $this->invoice->balance) { //total invoice paid.
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($payment_amount*-1); $this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PAID)->updateBalance($this->payment_amount*-1);
} elseif($payment_amount < $this->invoice->balance) { //partial invoice payment made } elseif($this->payment_amount < $this->invoice->balance) { //partial invoice payment made
$this->invoice->service()->clearPartial()->updateBalance($payment_amount*-1); $this->invoice->service()->clearPartial()->updateBalance($this->payment_amount*-1);
} }
return $this->invoice; return $this->invoice;

View File

@ -15,29 +15,31 @@ namespace App\Services\Invoice;
use App\Factory\InvoiceInvitationFactory; use App\Factory\InvoiceInvitationFactory;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Services\AbstractService;
class CreateInvitations class CreateInvitations extends AbstractService
{ {
public function __construct() private $invoice;
public function __construct(Invoice $invoice)
{ {
// .. $this->invoice = $invoice;
} }
public function run($invoice) public function run()
{ {
$contacts = $invoice->client->contacts; $this->invoice->client->contacts->each(function ($contact){
$contacts->each(function ($contact) use($invoice){ $invitation = InvoiceInvitation::whereCompanyId($this->invoice->company_id)
$invitation = InvoiceInvitation::whereCompanyId($invoice->company_id)
->whereClientContactId($contact->id) ->whereClientContactId($contact->id)
->whereInvoiceId($invoice->id) ->whereInvoiceId($this->invoice->id)
->first(); ->first();
if (!$invitation && $contact->send) { if (!$invitation && $contact->send) {
$ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); $ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id);
$ii->invoice_id = $invoice->id; $ii->invoice_id = $this->invoice->id;
$ii->client_contact_id = $contact->id; $ii->client_contact_id = $contact->id;
$ii->save(); $ii->save();
} elseif ($invitation && !$contact->send) { } elseif ($invitation && !$contact->send) {
@ -45,6 +47,6 @@ class CreateInvitations
} }
}); });
return $invoice; return $this->invoice;
} }
} }

View File

@ -12,20 +12,32 @@
namespace App\Services\Invoice; namespace App\Services\Invoice;
use App\Jobs\Invoice\CreateInvoicePdf; use App\Jobs\Invoice\CreateInvoicePdf;
use App\Models\ClientContact;
use App\Models\Invoice;
use App\Services\AbstractService;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class GetInvoicePdf class GetInvoicePdf extends AbstractService
{ {
public function run($invoice, $contact = null) public function __construct(Invoice $invoice, ClientContact $contact = null)
{
$this->invoice = $invoice;
$this->contact = $contact;
}
public function run()
{ {
if(!$contact) if(!$this->contact)
$contact = $invoice->client->primary_contact()->first(); $this->contact = $this->invoice->client->primary_contact()->first();
$path = $invoice->client->invoice_filepath(); $path = $this->invoice->client->invoice_filepath();
$file_path = $path . $invoice->number . '.pdf'; $file_path = $path . $this->invoice->number . '.pdf';
$disk = config('filesystems.default'); $disk = config('filesystems.default');
@ -34,7 +46,7 @@ class GetInvoicePdf
if(!$file) if(!$file)
{ {
$file_path = CreateInvoicePdf::dispatchNow($invoice, $invoice->company, $contact); $file_path = CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company, $this->contact);
} }

View File

@ -44,9 +44,9 @@ class InvoiceService
*/ */
public function markPaid() public function markPaid()
{ {
$mark_invoice_paid = new MarkPaid($this->client_service); $mark_invoice_paid = new MarkPaid($this->client_service, $this->invoice);
$this->invoice = $mark_invoice_paid->run($this->invoice); $this->invoice = $mark_invoice_paid->run();
return $this; return $this;
} }
@ -57,9 +57,9 @@ class InvoiceService
*/ */
public function applyNumber() public function applyNumber()
{ {
$apply_number = new ApplyNumber($this->invoice->client); $apply_number = new ApplyNumber($this->invoice->client, $this->invoice);
$this->invoice = $apply_number->run($this->invoice); $this->invoice = $apply_number->run();
return $this; return $this;
} }
@ -72,9 +72,9 @@ class InvoiceService
*/ */
public function applyPayment(Payment $payment, float $payment_amount) public function applyPayment(Payment $payment, float $payment_amount)
{ {
$apply_payment = new ApplyPayment($this->invoice); $apply_payment = new ApplyPayment($this->invoice, $payment, $payment_amount);
$this->invoice = $apply_payment->run($payment, $payment_amount); $this->invoice = $apply_payment->run();
return $this; return $this;
} }
@ -88,27 +88,27 @@ class InvoiceService
*/ */
public function updateBalance($balance_adjustment) public function updateBalance($balance_adjustment)
{ {
$update_balance = new UpdateBalance($this->invoice); $update_balance = new UpdateBalance($this->invoice, $balance_adjustment);
$this->invoice = $update_balance->run($balance_adjustment); $this->invoice = $update_balance->run();
return $this; return $this;
} }
public function createInvitations() public function createInvitations()
{ {
$create_invitation = new CreateInvitations(); $create_invitation = new CreateInvitations($this->invoice);
$this->invoice = $create_invitation->run($this->invoice); $this->invoice = $create_invitation->run();
return $this; return $this;
} }
public function markSent() public function markSent()
{ {
$mark_sent = new MarkSent($this->invoice->client); $mark_sent = new MarkSent($this->invoice->client, $this->invoice);
$this->invoice = $mark_sent->run($this->invoice); $this->invoice = $mark_sent->run();
return $this; return $this;
} }
@ -116,16 +116,16 @@ class InvoiceService
public function getInvoicePdf($contact) public function getInvoicePdf($contact)
{ {
$get_invoice_pdf = new GetInvoicePdf(); $get_invoice_pdf = new GetInvoicePdf($this->invoice, $contact);
return $get_invoice_pdf->run($this->invoice, $contact); return $get_invoice_pdf->run();
} }
public function sendEmail($contact) public function sendEmail($contact)
{ {
$send_email = new SendEmail($this->invoice); $send_email = new SendEmail($this->invoice, null, $contact);
return $send_email->run(null, $contact); return $send_email->run();
} }
public function markViewed() public function markViewed()

View File

@ -16,39 +16,44 @@ use App\Factory\PaymentFactory;
use App\Jobs\Company\UpdateCompanyLedgerWithPayment; use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Services\AbstractService;
use App\Services\Client\ClientService; use App\Services\Client\ClientService;
use App\Services\Payment\PaymentService; use App\Services\Payment\PaymentService;
class MarkPaid class MarkPaid extends AbstractService
{ {
private $client_service; private $client_service;
public function __construct($client_service) private $invoice;
public function __construct(ClientService $client_service, Invoice $invoice)
{ {
$this->client_service = $client_service; $this->client_service = $client_service;
$this->invoice = $invoice;
} }
public function run($invoice) public function run()
{ {
if($invoice->status_id == Invoice::STATUS_DRAFT) if($this->invoice->status_id == Invoice::STATUS_DRAFT)
$invoice->service()->markSent(); $this->invoice->service()->markSent();
/* Create Payment */ /* Create Payment */
$payment = PaymentFactory::create($invoice->company_id, $invoice->user_id); $payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
$payment->amount = $invoice->balance; $payment->amount = $this->invoice->balance;
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;
$payment->client_id = $invoice->client_id; $payment->client_id = $this->invoice->client_id;
$payment->transaction_reference = ctrans('texts.manual_entry'); $payment->transaction_reference = ctrans('texts.manual_entry');
/* Create a payment relationship to the invoice entity */ /* Create a payment relationship to the invoice entity */
$payment->save(); $payment->save();
$payment->invoices()->attach($invoice->id, [ $payment->invoices()->attach($this->invoice->id, [
'amount' => $payment->amount 'amount' => $payment->amount
]); ]);
$invoice->service() $this->invoice->service()
->updateBalance($payment->amount*-1) ->updateBalance($payment->amount*-1)
->setStatus(Invoice::STATUS_PAID) ->setStatus(Invoice::STATUS_PAID)
->save(); ->save();
@ -63,7 +68,7 @@ class MarkPaid
->updatePaidToDate($payment->amount) ->updatePaidToDate($payment->amount)
->save(); ->save();
return $invoice; return $this->invoice;
} }
} }

View File

@ -14,38 +14,42 @@ namespace App\Services\Invoice;
use App\Events\Invoice\InvoiceWasMarkedSent; use App\Events\Invoice\InvoiceWasMarkedSent;
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
use App\Models\Invoice; use App\Models\Invoice;
use App\Services\AbstractService;
class MarkSent class MarkSent extends AbstractService
{ {
private $client; private $client;
public function __construct($client) private $invoice;
public function __construct($client, $invoice)
{ {
$this->client = $client; $this->client = $client;
$this->invoice = $invoice;
} }
public function run($invoice) public function run()
{ {
/* Return immediately if status is not draft */ /* Return immediately if status is not draft */
if ($invoice->status_id != Invoice::STATUS_DRAFT) { if ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
return $invoice; return $this->invoice;
} }
$invoice->markInvitationsSent(); $this->invoice->markInvitationsSent();
$invoice->setReminder(); $this->invoice->setReminder();
event(new InvoiceWasMarkedSent($invoice, $invoice->company)); event(new InvoiceWasMarkedSent($this->invoice, $this->invoice->company));
$this->client->service()->updateBalance($invoice->balance)->save(); $this->client->service()->updateBalance($this->invoice->balance)->save();
$invoice->service()->setStatus(Invoice::STATUS_SENT)->applyNumber()->save(); $this->invoice->service()->setStatus(Invoice::STATUS_SENT)->applyNumber()->save();
UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, $invoice->balance, $invoice->company); UpdateCompanyLedgerWithInvoice::dispatchNow($this->invoice, $this->invoice->balance, $this->invoice->company);
return $invoice; return $this->invoice;
} }
} }

View File

@ -13,17 +13,23 @@ namespace App\Services\Invoice;
use App\Helpers\Email\InvoiceEmail; use App\Helpers\Email\InvoiceEmail;
use App\Jobs\Invoice\EmailInvoice; use App\Jobs\Invoice\EmailInvoice;
use App\Models\ClientContact;
use App\Models\Invoice; use App\Models\Invoice;
use App\Services\AbstractService;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
class SendEmail class SendEmail extends AbstractService
{ {
protected $invoice; protected $invoice;
public function __construct(Invoice $invoice) public function __construct(Invoice $invoice, $reminder_template = null, ClientContact $contact = null)
{ {
$this->invoice = $invoice; $this->invoice = $invoice;
$this->reminder_template = $reminder_template;
$this->contact = $contact;
} }
@ -32,15 +38,17 @@ class SendEmail
* @param string $reminder_template The template name ie reminder1 * @param string $reminder_template The template name ie reminder1
* @return array * @return array
*/ */
public function run($reminder_template = null, $contact = null): array public function run()
{ {
if (!$reminder_template) {
$reminder_template = $this->invoice->status_id == Invoice::STATUS_DRAFT || Carbon::parse($this->invoice->due_date) > now() ? 'invoice' : $this->invoice->calculateTemplate(); if (!$this->reminder_template) {
$this->reminder_template = $this->invoice->calculateTemplate();
} }
$email_builder = (new InvoiceEmail())->build($this->invoice, $reminder_template, $contact); $this->invoice->invitations->each(function ($invitation){
$email_builder = (new InvoiceEmail())->build($invitation, $this->reminder_template);
$this->invoice->invitations->each(function ($invitation) use ($email_builder) {
if ($invitation->contact->send && $invitation->contact->email) { if ($invitation->contact->send && $invitation->contact->email) {
EmailInvoice::dispatch($email_builder, $invitation, $invitation->company); EmailInvoice::dispatch($email_builder, $invitation, $invitation->company);
} }

View File

@ -12,31 +12,33 @@
namespace App\Services\Invoice; namespace App\Services\Invoice;
use App\Models\Invoice; use App\Models\Invoice;
use App\Services\AbstractService;
class UpdateBalance class UpdateBalance extends AbstractService
{ {
private $invoice; private $invoice;
public function __construct($invoice) private $balance_adjustment;
public function __construct($invoice, $balance_adjustment)
{ {
$this->invoice = $invoice; $this->invoice = $invoice;
$this->balance_adjustment = $balance_adjustment;
} }
public function run($balance_adjustment) public function run()
{ {
if ($this->invoice->is_deleted) { if ($this->invoice->is_deleted) {
return; return;
} }
$balance_adjustment = floatval($balance_adjustment); $this->invoice->balance += floatval($this->balance_adjustment);
$this->invoice->balance += $balance_adjustment;
if ($this->invoice->balance == 0) { if ($this->invoice->balance == 0) {
$this->status_id = Invoice::STATUS_PAID; $this->invoice->status_id = Invoice::STATUS_PAID;
// $this->save(); // $this->save();
// event(new InvoiceWasPaid($this, $this->company)); // event(new InvoiceWasPaid($this, $this->company));

View File

@ -27,10 +27,13 @@ class SendEmail
$reminder_template = $this->quote->status_id == Quote::STATUS_DRAFT || Carbon::parse($this->quote->due_date) > now() ? 'invoice' : $this->quote->calculateTemplate(); $reminder_template = $this->quote->status_id == Quote::STATUS_DRAFT || Carbon::parse($this->quote->due_date) > now() ? 'invoice' : $this->quote->calculateTemplate();
} }
$email_builder = (new QuoteEmail())->build($this->quote, $reminder_template, $contact); $this->quote->invitations->each(function ($invitation){
if ($invitation->contact->send && $invitation->contact->email)
{
$email_builder = (new QuoteEmail())->build($invitation, $reminder_template);
$this->quote->invitations->each(function ($invitation) use ($email_builder) {
if ($invitation->contact->send && $invitation->contact->email) {
EmailQuote::dispatchNow($email_builder, $invitation); EmailQuote::dispatchNow($email_builder, $invitation);
} }
}); });

View File

@ -34,10 +34,11 @@ trait MakesInvoiceHtml
{ {
//$variables = array_merge($invoice->makeLabels(), $invoice->makeValues()); //$variables = array_merge($invoice->makeLabels(), $invoice->makeValues());
//$design = str_replace(array_keys($variables), array_values($variables), $design); //$design = str_replace(array_keys($variables), array_values($variables), $design);
if(!$contact) $invoice->load('client');
$contact = $invoice->client->primary_contact()->first();
App::setLocale($contact->preferredLocale()); $client = $invoice->client;
App::setLocale($client->preferredLocale());
$labels = $invoice->makeLabels(); $labels = $invoice->makeLabels();
$values = $invoice->makeValues($contact); $values = $invoice->makeValues($contact);

View File

@ -576,8 +576,11 @@ trait MakesInvoiceValues
* @param array $items The array of invoice items * @param array $items The array of invoice items
* @return array The formatted array of invoice items * @return array The formatted array of invoice items
*/ */
private function transformLineItems(array $items) :array private function transformLineItems($items) :array
{ {
if(!is_array($items))
return [];
foreach ($items as $item) { foreach ($items as $item) {
$item->cost = Number::formatMoney($item->cost, $this->client); $item->cost = Number::formatMoney($item->cost, $this->client);
$item->line_total = Number::formatMoney($item->line_total, $this->client); $item->line_total = Number::formatMoney($item->line_total, $this->client);

View File

@ -378,7 +378,7 @@ class CreateUsersTable extends Migration
$table->string('password'); $table->string('password');
$table->string('token')->nullable(); $table->string('token')->nullable();
$table->boolean('is_locked')->default(false); $table->boolean('is_locked')->default(false);
$table->boolean('send')->default(true); $table->boolean('send_email')->default(true);
$table->string('contact_key')->nullable(); $table->string('contact_key')->nullable();
$table->rememberToken(); $table->rememberToken();
$table->timestamps(6); $table->timestamps(6);

View File

@ -0,0 +1,20 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
Download
@endcomponent
@endslot
{{-- Body --}}
{{ $file_path }}
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('ninja.app_name') }}.
@endcomponent
@endslot
@endcomponent

View File

@ -1,13 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ App::getLocale() }}"> <html lang="{{ App::getLocale() }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>
<body> <body>
{!! $body !!}
{!! $body !!}
{!! $footer !!}
</body> </body>
<footer>
{!! $footer !!}
</footer>
</html> </html>

View File

@ -87,7 +87,7 @@
<div> <div>
<h2>trans('texts.sign_up_now')</h2> <h2>trans('texts.sign_up_now')</h2>
<p>trans('texts.not_a_member_yet')</p> <p>trans('texts.not_a_member_yet')</p>
<a class="btn btn-primary active mt-3" href="{{route('signup') }}">trans('texts.login_create_an_account')</a> <a class="btn btn-primary active mt-3" href=" route('signup') ">trans('texts.login_create_an_account')</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -41,7 +41,7 @@ class InvoiceEmailTest extends TestCase
$this->makeTestData(); $this->makeTestData();
} }
public function test_initial_email_sends() public function test_initial_email_send_emails()
{ {
$this->invoice->date = now(); $this->invoice->date = now();
@ -50,15 +50,14 @@ class InvoiceEmailTest extends TestCase
$this->invoice->client_id = $this->client->id; $this->invoice->client_id = $this->client->id;
$this->invoice->setRelation('client', $this->client); $this->invoice->setRelation('client', $this->client);
$this->invoice->save(); $this->invoice->save();
$invitations = InvoiceInvitation::whereInvoiceId($this->invoice->id)->get(); $this->invoice->invitations->each(function ($invitation) {
$email_builder = (new InvoiceEmail())->build($this->invoice, null, null); if ($invitation->contact->send_email && $invitation->contact->email) {
$invitations->each(function ($invitation) use ($email_builder) { $email_builder = (new InvoiceEmail())->build($invitation, null);
if ($invitation->contact->send && $invitation->contact->email) {
EmailInvoice::dispatch($email_builder, $invitation, $invitation->company); EmailInvoice::dispatch($email_builder, $invitation, $invitation->company);

View File

@ -364,8 +364,18 @@ class PaymentTest extends TestCase
$this->invoice = null; $this->invoice = null;
$client = ClientFactory::create($this->company->id, $this->user->id); $client = ClientFactory::create($this->company->id, $this->user->id);
$client->setRelation('company', $this->company);
$client->save(); $client->save();
$client_contact = factory(\App\Models\ClientContact::class, 1)->create([
'user_id' => $this->user->id,
'client_id' => $client->id,
'company_id' => $this->company->id,
'is_primary' => 1
]);
$client->setRelation('contacts', $client_contact);
$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;
@ -379,6 +389,8 @@ class PaymentTest extends TestCase
$this->invoice_calc->build(); $this->invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $this->invoice_calc->getInvoice();
$this->invoice->company->setRelation('company', $this->company);
$this->invoice->company->setRelation('client', $client);
$this->invoice->save(); $this->invoice->save();
$this->invoice->service()->markSent()->save(); $this->invoice->service()->markSent()->save();
$this->invoice->is_deleted = false; $this->invoice->is_deleted = false;

View File

@ -139,14 +139,14 @@ trait MockAccountData
'client_id' => $this->client->id, 'client_id' => $this->client->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'is_primary' => 1, 'is_primary' => 1,
'send' => true, 'send_email' => true,
]); ]);
factory(\App\Models\ClientContact::class,1)->create([ factory(\App\Models\ClientContact::class,1)->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'client_id' => $this->client->id, 'client_id' => $this->client->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'send' => true 'send_email' => true
]); ]);
@ -176,6 +176,9 @@ trait MockAccountData
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $this->invoice_calc->getInvoice();
$this->invoice->setRelation('client', $this->client);
$this->invoice->setRelation('company', $this->company);
$this->invoice->save(); $this->invoice->save();
$this->invoice->service()->markSent(); $this->invoice->service()->markSent();
@ -200,13 +203,13 @@ trait MockAccountData
->whereInvoiceId($this->invoice->id) ->whereInvoiceId($this->invoice->id)
->first(); ->first();
if(!$invitation && $contact->send) { if(!$invitation && $contact->send_email) {
$ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id); $ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id);
$ii->invoice_id = $this->invoice->id; $ii->invoice_id = $this->invoice->id;
$ii->client_contact_id = $contact->id; $ii->client_contact_id = $contact->id;
$ii->save(); $ii->save();
} }
else if($invitation && !$contact->send) { else if($invitation && !$contact->send_email) {
$invitation->delete(); $invitation->delete();
} }