diff --git a/app/Helpers/Email/EmailBuilder.php b/app/Helpers/Email/EmailBuilder.php new file mode 100644 index 000000000000..b449e9f24905 --- /dev/null +++ b/app/Helpers/Email/EmailBuilder.php @@ -0,0 +1,154 @@ +variables)) { + $data = str_replace(array_keys($this->variables), array_values($this->variables), $data); + } + + //process markdown + if ($is_markdown) { + //$data = Parsedown::instance()->line($data); + + $converter = new CommonMarkConverter([ + 'html_input' => 'allow', + 'allow_unsafe_links' => true, + ]); + + $data = $converter->convertToHtml($data); + } + + return $data; + } + + /** + * @param $footer + * @return $this + */ + public function setFooter($footer) + { + $this->footer = $footer; + return $this; + } + + public function setVariables($variables) + { + $this->variables = $variables; + return $this; + } + + /** + * @param $contact + * @return $this + */ + public function setContact($contact) + { + $this->contact = $contact; + return $this; + } + + /** + * @param $subject + * @return $this + */ + public function setSubject($subject) + { + $this->subject = $this->parseTemplate($subject, false, $this->contact); + return $this; + } + + /** + * @param $body + * @return $this + */ + public function setBody($body) + { + $this->parseTemplate($body, true); + return $this; + } + + /** + * @param $template_style + * @return $this + */ + public function setTemplate($template_style) + { + $this->template_style = $template_style; + return $this; + } + + public function setAttachments($attachments) + { + $this->attachments[] = $attachments; + } + + + /** + * @return mixed + */ + public function getSubject() + { + return $this->subject; + } + + /** + * @return mixed + */ + public function getBody() + { + return $this->body; + } + + /** + * @return mixed + */ + public function getRecipients() + { + return $this->recipients; + } + + /** + * @return mixed + */ + public function getAttachments() + { + return $this->attachments; + } + + /** + * @return mixed + */ + public function getFooter() + { + return $this->footer; + } + + /** + * @return mixed + */ + public function getTemplate() + { + return $this->template_style; + } +} diff --git a/app/Helpers/Email/InvoiceEmail.php b/app/Helpers/Email/InvoiceEmail.php new file mode 100644 index 000000000000..835e24d28346 --- /dev/null +++ b/app/Helpers/Email/InvoiceEmail.php @@ -0,0 +1,61 @@ +client; + + $body_template = $client->getSetting('email_template_' . $reminder_template); + + /* Use default translations if a custom message has not been set*/ + if (iconv_strlen($body_template) == 0) { + $body_template = trans('texts.invoice_message', + ['amount' => $invoice->present()->amount(), 'company' => $invoice->company->present()->name()], null, + $invoice->client->locale()); + } + + $subject_template = $client->getSetting('email_subject_' . $reminder_template); + + if (iconv_strlen($subject_template) == 0) { + if ($reminder_template == 'quote') { + $subject_template = trans('texts.invoice_subject', + [ + 'number' => $this->invoice->present()->invoice_number(), + 'company' => $invoice->company->present()->name() + ], + null, $invoice->client->locale()); + } else { + $subject_template = trans('texts.reminder_subject', + [ + 'number' => $invoice->present()->invoice_number(), + 'company' => $invoice->company->present()->name() + ], + null, $invoice->client->locale()); + } + } + + $this->setTemplate($invoice->client->getSetting('email_style')) + ->setContact($contact) + ->setVariables($invoice->makeValues($contact)) + ->setSubject($subject_template) + ->setBody($body_template) + ->setFooter("Invoice Link"); + + if ($client->getSetting('pdf_email_attachment') !== false) { + $this->setAttachments($invoice->pdf_file_path()); + } + return $this; + } +} diff --git a/app/Helpers/Email/PaymentEmail.php b/app/Helpers/Email/PaymentEmail.php new file mode 100644 index 000000000000..5535a9811e13 --- /dev/null +++ b/app/Helpers/Email/PaymentEmail.php @@ -0,0 +1,43 @@ +client; + + $body_template = $client->getSetting('email_template_payment'); + + /* Use default translations if a custom message has not been set*/ + if (iconv_strlen($body_template) == 0) { + + $body_template = trans('texts.payment_message', + ['amount' => $payment->amount, 'company' => $payment->company->present()->name()], null, + $this->client->locale()); + } + + $subject_template = $client->getSetting('email_subject_payment'); + + if (iconv_strlen($subject_template) == 0) { + $subject_template = trans('texts.payment_subject', + ['number' => $payment->number, 'company' => $payment->company->present()->name()], null, + $payment->client->locale()); + } + + $this->setTemplate($payment->client->getSetting('email_style')) + ->setSubject($subject_template) + ->setBody($body_template); + + return $this; + } +} diff --git a/app/Helpers/Email/QuoteEmail.php b/app/Helpers/Email/QuoteEmail.php new file mode 100644 index 000000000000..a586199acaca --- /dev/null +++ b/app/Helpers/Email/QuoteEmail.php @@ -0,0 +1,58 @@ +client; + $this->template_style = $quote->client->getSetting('email_style'); + + $body_template = $client->getSetting('email_template_' . $reminder_template); + + /* Use default translations if a custom message has not been set*/ + if (iconv_strlen($body_template) == 0) { + $body_template = trans('texts.quote_message', + ['amount' => $quote->amount, 'company' => $quote->company->present()->name()], null, + $quote->client->locale()); + } + + $subject_template = $client->getSetting('email_subject_' . $reminder_template); + + if (iconv_strlen($subject_template) == 0) { + if ($reminder_template == 'quote') { + $subject_template = trans('texts.quote_subject', + ['number' => $quote->number, 'company' => $quote->company->present()->name()], + null, $quote->client->locale()); + } else { + $subject_template = trans('texts.reminder_subject', + ['number' => $quote->number, 'company' => $quote->company->present()->name()], + null, $quote->client->locale()); + } + } + + $this->setTemplate($quote->client->getSetting('email_style')) + ->setContact($contact) + ->setFooter("Invoice Link") + ->setVariables($quote->makeValues($contact)) + ->setSubject($subject_template) + ->setBody($body_template); + + if ($client->getSetting('pdf_email_attachment') !== false) { + $this->attachments = $quote->pdf_file_path(); + } + + return $this; + } +} diff --git a/app/Jobs/Invoice/EmailInvoice.php b/app/Jobs/Invoice/EmailInvoice.php index 791ed7dccdd9..c63450b5dd1f 100644 --- a/app/Jobs/Invoice/EmailInvoice.php +++ b/app/Jobs/Invoice/EmailInvoice.php @@ -1,50 +1,40 @@ invoice = $invoice; - - $this->company = $company; + $this->invoice_invitation = $invoice_invitation; + $this->email_builder = $email_builder; } /** @@ -53,46 +43,22 @@ class EmailInvoice implements ShouldQueue * * @return void */ + public function handle() { - /*Jobs are not multi-db aware, need to set! */ - MultiDB::setDB($this->company->db); + + $email_builder = $this->email_builder; - //todo - change runtime config of mail driver if necessary + Mail::to($this->invoice_invitation->contact->email, $this->invoice_invitation->contact->present()->name()) + ->send(new TemplateEmail($email_builder, + $this->invoice_invitation->contact->user, + $this->invoice_invitation->contact->client + ) + ); - $template_style = $this->invoice->client->getSetting('email_style'); - - $this->invoice->invitations->each(function ($invitation) use ($template_style) { - if ($invitation->contact->send_invoice && $invitation->contact->email) { - - $message_array = $this->invoice->getEmailData('', $invitation->contact); - - $message_array['title'] = &$message_array['subject']; - - //$message_array['footer'] = "Sent to ".$invitation->contact->present()->name(); - $message_array['footer'] = "Invoice Link"; - - //change the runtime config of the mail provider here: - - //send message - Mail::to($invitation->contact->email, $invitation->contact->present()->name()) - ->send(new TemplateEmail($message_array, - $template_style, - $invitation->contact->user, - $invitation->contact->client)); - - if (count(Mail::failures()) > 0) { - event(new InvoiceWasEmailedAndFailed($this->invoice, Mail::failures())); - - return $this->logMailError($errors); - } - - //fire any events - event(new InvoiceWasEmailed($this->invoice)); - - //sleep(5); - } - }); + if (count(Mail::failures()) > 0) { + return $this->logMailError($errors); + } } private function logMailError($errors) diff --git a/app/Jobs/Payment/EmailPayment.php b/app/Jobs/Payment/EmailPayment.php index dccb94d2bb0f..f8d58a3cead3 100644 --- a/app/Jobs/Payment/EmailPayment.php +++ b/app/Jobs/Payment/EmailPayment.php @@ -1,9 +1,13 @@ payment = $payment; + $this->email_builder = $email_builder; + $this->contact = $contact; + } - $this->company = $company; - } /** * Execute the job. @@ -46,37 +52,24 @@ class EmailPayment implements ShouldQueue */ public function handle() { - /*Jobs are not multi-db aware, need to set! */ - MultiDB::setDB($this->company->db); + $email_builder = $this->email_builder; - //todo - change runtime config of mail driver if necessary + if ($this->contact->email) { + Mail::to($this->contact->email, $this->contact->present()->name()) + ->send(new TemplateEmail($email_builder, $this->contact->user, $this->contact->customer)); - $template_style = $this->payment->client->getSetting('email_style'); + if (count(Mail::failures()) > 0) { + event(new PaymentWasEmailedAndFailed($this->payment, Mail::failures())); - $this->payment->client->contacts->each(function ($contact) use ($template_style) { - if ($contact->email) { - $message_array = $this->payment->getEmailData('', $contact); - $message_array['title'] = &$message_array['subject']; - $message_array['footer'] = "Sent to ".$contact->present()->name(); - - //change the runtime config of the mail provider here: - - //send message - Mail::to($contact->email, $contact->present()->name()) - ->send(new TemplateEmail($message_array, $template_style, $contact->user, $contact->client)); - - if (count(Mail::failures()) > 0) { - event(new PaymentWasEmailedAndFailed($this->payment, Mail::failures())); - - return $this->logMailError($errors); - } - - //fire any events - event(new PaymentWasEmailed($this->payment)); - - //sleep(5); + return $this->logMailError($errors); } - }); + + //fire any events + event(new PaymentWasEmailed($this->payment)); + + //sleep(5); + } + } private function logMailError($errors) diff --git a/app/Jobs/Quote/EmailQuote.php b/app/Jobs/Quote/EmailQuote.php index fd7834e59240..845c839d08cf 100644 --- a/app/Jobs/Quote/EmailQuote.php +++ b/app/Jobs/Quote/EmailQuote.php @@ -1,13 +1,17 @@ quote = $quote; - - $this->company = $company; + $this->quote_invitation = $quote_invitation; + $this->email_builder = $email_builder; } /** @@ -46,37 +47,19 @@ class EmailQuote implements ShouldQueue */ public function handle() { - /*Jobs are not multi-db aware, need to set! */ - MultiDB::setDB($this->company->db); + $email_builder = $this->email_builder; - //todo - change runtime config of mail driver if necessary + Mail::to($this->quote_invitation->contact->email, $this->quote_invitation->contact->present()->name()) + ->send(new TemplateEmail($email_builder, + $this->quote_invitation->contact->user, + $this->quote_invitation->contact->client + ) + ); - $template_style = $this->quote->client->getSetting('email_style'); + if (count(Mail::failures()) > 0) { + return $this->logMailError($errors); + } - $this->quote->invitations->each(function ($invitation) use ($template_style) { - if ($invitation->contact->email) { - $message_array = $this->quote->getEmailData('', $invitation->contact); - $message_array['title'] = &$message_array['subject']; - $message_array['footer'] = "Quote Link"; - - //change the runtime config of the mail provider here: - - //send message - Mail::to($invitation->contact->email, $invitation->contact->present()->name()) - ->send(new TemplateEmail($message_array, $template_style, $invitation->contact->user, $invitation->contact->client)); - - if (count(Mail::failures()) > 0) { - event(new QuoteWasEmailedAndFailed($this->quote, Mail::failures())); - - return $this->logMailError($errors); - } - - //fire any events - event(new QuoteWasEmailed($this->quote)); - - //sleep(5); - } - }); } private function logMailError($errors) diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index 47872e14113b..e1b06a5ac22c 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -1,8 +1,6 @@ message = $message; - $this->template = $template; + $this->build_email = $build_email; $this->user = $user; //this is inappropriate here, need to refactor 'user' in this context the 'user' could also be the 'system' - $this->client = $client; + $this->customer = $customer; } /** @@ -37,33 +29,32 @@ class TemplateEmail extends Mailable public function build() { /*Alter Run Time Mailer configuration (driver etc etc) to regenerate the Mailer Singleton*/ - //if using a system level template - $template_name = 'email.template.'.$this->template; + $template_name = 'email.template.' . $this->build_email->getTemplate(); - $settings = $this->client->getMergedSettings(); + $settings = $this->customer->getMergedSettings(); + \Log::error(print_r($settings, 1)); + $company = $this->customer->account; - $company = $this->client->company; - - $message = $this->from($this->user->email, $this->user->present()->name()) //todo this needs to be fixed to handle the hosted version - ->subject($this->message['subject']) - ->text('email.template.plain', ['body' => $this->message['body'], 'footer' => $this->message['footer']]) + $message = $this->from($this->user->email, + $this->user->present()->name())//todo this needs to be fixed to handle the hosted version + ->subject($this->build_email->getSubject()) + ->text('email.template.plain', ['body' => $this->build_email->getBody(), 'footer' => $this->build_email->getFooter()]) ->view($template_name, [ - 'body' => $this->message['body'], - 'footer' => $this->message['footer'], - 'title' => $this->message['title'], + 'body' => $this->build_email->getBody(), + 'footer' => $this->build_email->getFooter(), + 'title' => $this->build_email->getSubject(), 'settings' => $settings, 'company' => $company ]); + //conditionally attach files + if($settings->pdf_email_attachment !== false && !empty($this->build_email->getAttachments())){ - //conditionally attach files - if ($settings->pdf_email_attachment !== false && array_key_exists('files', $this->message)) { - foreach ($this->message['files'] as $file) { - $message->attach($file); - } - } + foreach($this->build_email->getAttachments() as $file) + $message->attach($file); + } - return $message; + return $message; } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 715f736c0fee..f5bec3c94189 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -146,7 +146,7 @@ class Invoice extends BaseModel public function payments() { - return $this->morphToMany(Payment::class, 'paymentable')->withPivot('amount','refunded')->withTimestamps();; + return $this->morphToMany(Payment::class, 'paymentable')->withPivot('amount', 'refunded')->withTimestamps();; } public function company_ledger() @@ -156,13 +156,14 @@ class Invoice extends BaseModel public function credits() { - return $this->belongsToMany(Credit::class)->using(Paymentable::class)->withPivot('amount','refunded')->withTimestamps();; + return $this->belongsToMany(Credit::class)->using(Paymentable::class)->withPivot('amount', + 'refunded')->withTimestamps();; } /** * Service entry points */ - public function service() :InvoiceService + public function service(): InvoiceService { return new InvoiceService($this); } @@ -192,12 +193,12 @@ class Invoice extends BaseModel * * @return boolean isLocked */ - public function isLocked() : bool + public function isLocked(): bool { return $this->client->getSetting('lock_sent_invoices'); } - public function isPayable() : bool + public function isPayable(): bool { if ($this->status_id == Invoice::STATUS_SENT && $this->is_deleted == false) { return true; @@ -212,23 +213,25 @@ class Invoice extends BaseModel } } - public function isRefundable() : bool + public function isRefundable(): bool { - if($this->is_deleted) + if ($this->is_deleted) { return false; + } - if(($this->amount - $this->balance) == 0) + if (($this->amount - $this->balance) == 0) { return false; + } return true; - + } /** * @return bool */ - public function isPartial() : bool + public function isPartial(): bool { return $this->status_id >= self::STATUS_PARTIAL; } @@ -236,7 +239,7 @@ class Invoice extends BaseModel /** * @return bool */ - public function hasPartial() : bool + public function hasPartial(): bool { return ($this->partial && $this->partial > 0) === true; } @@ -245,28 +248,28 @@ class Invoice extends BaseModel { switch ($status) { case Invoice::STATUS_DRAFT: - return '
'.ctrans('texts.draft').'
'; + return '
' . ctrans('texts.draft') . '
'; break; case Invoice::STATUS_SENT: - return '
'.ctrans('texts.sent').'
'; + return '
' . ctrans('texts.sent') . '
'; break; case Invoice::STATUS_PARTIAL: - return '
'.ctrans('texts.partial').'
'; + return '
' . ctrans('texts.partial') . '
'; break; case Invoice::STATUS_PAID: - return '
'.ctrans('texts.paid').'
'; + return '
' . ctrans('texts.paid') . '
'; break; case Invoice::STATUS_CANCELLED: - return '
'.ctrans('texts.cancelled').'
'; + return '
' . ctrans('texts.cancelled') . '
'; break; case Invoice::STATUS_OVERDUE: - return '
'.ctrans('texts.overdue').'
'; + return '
' . ctrans('texts.overdue') . '
'; break; case Invoice::STATUS_UNPAID: - return '
'.ctrans('texts.unpaid').'
'; + return '
' . ctrans('texts.unpaid') . '
'; break; case Invoice::STATUS_REVERSED: - return '
'.ctrans('texts.reversed').'
'; + return '
' . ctrans('texts.reversed') . '
'; break; default: # code... @@ -306,13 +309,14 @@ class Invoice extends BaseModel break; } } + /** * Returns the template for the invoice * * @return string Either the template view, OR the template HTML string * @todo this needs attention, invoice->settings needs clarification */ - public function design() :string + public function design(): string { if ($this->client->getSetting('design')) { return File::exists(resource_path($this->client->getSetting('design'))) ? File::get(resource_path($this->client->getSetting('design'))) : File::get(resource_path('views/pdf/design1.blade.php')); @@ -346,9 +350,10 @@ class Invoice extends BaseModel // $storage_path = 'public/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf'; - $public_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf'; + $public_path = $this->client->client_hash . '/invoices/' . $this->number . '.pdf'; + + $storage_path = $this->client->client_hash . '/invoices/' . $this->number . '.pdf'; - $storage_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf'; $disk = config('filesystems.default'); @@ -362,7 +367,7 @@ class Invoice extends BaseModel public function pdf_file_path() { - $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)) { CreateInvoicePdf::dispatchNow($this, $this->company, $this->client->primary_contact()->first()); @@ -386,8 +391,7 @@ class Invoice extends BaseModel }); } - -/* Graveyard */ + /* Graveyard */ // /** // * Determines if invoice overdue. diff --git a/app/Models/Presenters/QuotePresenter.php b/app/Models/Presenters/QuotePresenter.php new file mode 100644 index 000000000000..b0b13c7c4ffe --- /dev/null +++ b/app/Models/Presenters/QuotePresenter.php @@ -0,0 +1,82 @@ +balance, $this->client); + } + + public function invoice_number() + { + if ($this->number != '') { + return $this->number; + } else { + return ''; + } + } + + public function clientName() + { + return $this->client->present()->name(); + } + + public function address() + { + return $this->client->present()->address(); + } + + public function shippingAddress() + { + return $this->client->present()->shipping_address(); + } + + public function companyLogo() + { + return $this->company->logo; + } + + public function clientLogo() + { + return $this->client->logo; + } + + public function companyName() + { + return $this->company->present()->name(); + } + + public function companyAddress() + { + return $this->company->present()->address(); + } +} diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 645dee4a66f1..1477e1ef5aeb 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -16,6 +16,8 @@ use App\Helpers\Invoice\InvoiceSumInclusive; use App\Models\Filterable; use App\Services\Quote\QuoteService; use App\Utils\Traits\MakesHash; +use App\Utils\Traits\MakesReminders; +use Laracasts\Presenter\PresentableTrait; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -24,6 +26,10 @@ class Quote extends BaseModel use MakesHash; use Filterable; use SoftDeletes; + use MakesReminders; + use PresentableTrait; + + protected $presenter = 'App\Models\Presenters\QuotePresenter'; protected $fillable = [ 'number', diff --git a/app/Models/QuoteInvitation.php b/app/Models/QuoteInvitation.php index 61cc8dcc4ae5..942cd99c1e53 100644 --- a/app/Models/QuoteInvitation.php +++ b/app/Models/QuoteInvitation.php @@ -44,7 +44,7 @@ class QuoteInvitation extends BaseModel */ public function contact() { - return $this->belongsTo(ClientContact::class)->withTrashed(); + return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed(); } /** diff --git a/app/Services/Credit/ApplyNumber.php b/app/Services/Credit/ApplyNumber.php new file mode 100644 index 000000000000..2f23b0999162 --- /dev/null +++ b/app/Services/Credit/ApplyNumber.php @@ -0,0 +1,40 @@ +customer = $customer; + } + + public function __invoke($credit) + { + + if ($credit->number != '') { + return $credit; + } + + $credit->number = $this->getNextCreditNumber($this->customer); + + + return $credit; + } +} diff --git a/app/Services/Credit/CreateInvitations.php b/app/Services/Credit/CreateInvitations.php new file mode 100644 index 000000000000..07ae23cf6f2d --- /dev/null +++ b/app/Services/Credit/CreateInvitations.php @@ -0,0 +1,37 @@ +customer->contacts; + + $contacts->each(function ($contact) use($credit){ + $invitation = CreditInvitation::whereCompanyId($credit->account_id) + ->whereClientContactId($contact->id) + ->whereCreditId($credit->id) + ->first(); + + if (!$invitation) { + $ii = CreditInvitationFactory::create($credit->account_id, $credit->user_id); + $ii->credit_id = $credit->id; + $ii->client_contact_id = $contact->id; + $ii->save(); + } elseif ($invitation && !$contact->send_credit) { + $invitation->delete(); + } + }); + + return $credit; + } +} diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php new file mode 100644 index 000000000000..5f783238bc72 --- /dev/null +++ b/app/Services/Credit/CreditService.php @@ -0,0 +1,55 @@ +credit = $credit; + + } + + public function getCreditPdf($contact) + { + $get_invoice_pdf = new GetCreditPdf(); + + return $get_invoice_pdf($this->credit, $contact); + } + + /** + * Applies the invoice number + * @return $this InvoiceService object + */ + public function applyNumber() + { + $apply_number = new ApplyNumber($this->credit->customer); + + $this->credit = $apply_number($this->credit); + + return $this; + } + + public function createInvitations() + { + $create_invitation = new CreateInvitations(); + + $this->invoice = $create_invitation($this->invoice); + + return $this; + } + + /** + * Saves the credit + * @return Credit object + */ + public function save() : ?Credit + { + $this->credit->save(); + return $this->credit; + } +} diff --git a/app/Services/Credit/GetCreditPdf.php b/app/Services/Credit/GetCreditPdf.php new file mode 100644 index 000000000000..0a8f7619df4d --- /dev/null +++ b/app/Services/Credit/GetCreditPdf.php @@ -0,0 +1,33 @@ +customer->primary_contact()->first(); + } + + $path = 'public/' . $credit->customer->id . '/credits/'; + $file_path = $path . $credit->number . '.pdf'; + $disk = config('filesystems.default'); + $file = Storage::disk($disk)->exists($file_path); + + if (!$file) { + $file_path = CreateInvoicePdf::dispatchNow($this, $credit->account, $contact); + } + + return Storage::disk($disk)->url($file_path); + } + +} diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 517f2e374524..afd5f751e6f6 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -120,14 +120,12 @@ class InvoiceService return $get_invoice_pdf->run($this->invoice, $contact); } + public function sendEmail($contact) + { + $send_email = new SendEmail($this->invoice); - - - - - - - + return $send_email->run(null, $contact); + } public function markViewed() { diff --git a/app/Services/Invoice/SendEmail.php b/app/Services/Invoice/SendEmail.php new file mode 100644 index 000000000000..91d6bc6f2699 --- /dev/null +++ b/app/Services/Invoice/SendEmail.php @@ -0,0 +1,39 @@ +invoice = $invoice; + } + + /** + * Builds the correct template to send + * @param string $reminder_template The template name ie reminder1 + * @return array + */ + public function run($reminder_template = null, $contact = null): array + { + if (!$reminder_template) { + $reminder_template = $this->invoice->status_id == Invoice::STATUS_DRAFT || Carbon::parse($this->invoice->due_date) > now() ? 'invoice' : $this->invoice->calculateTemplate(); + } + + $email_builder = (new InvoiceEmail())->build($this->invoice, $reminder_template, $contact); + + $this->invoice->invitations->each(function ($invitation) use ($email_builder) { + if ($invitation->contact->send_invoice && $invitation->contact->email) { + EmailInvoice::dispatchNow($email_builder, $invitation); + } + }); + } +} diff --git a/app/Services/Payment/PaymentService.php b/app/Services/Payment/PaymentService.php index 097126572379..581262b344e6 100644 --- a/app/Services/Payment/PaymentService.php +++ b/app/Services/Payment/PaymentService.php @@ -42,4 +42,11 @@ class PaymentService return $payment; } + + public function sendEmail($contact = null) + { + $send_email = new SendEmail($this->payment); + + return $send_email->run(null, $contact); + } } diff --git a/app/Services/Payment/SendEmail.php b/app/Services/Payment/SendEmail.php new file mode 100644 index 000000000000..b3114507563e --- /dev/null +++ b/app/Services/Payment/SendEmail.php @@ -0,0 +1,32 @@ +payment = $payment; + } + + /** + * Builds the correct template to send + * @param string $reminder_template The template name ie reminder1 + * @return array + */ + public function run($contact = null): array + { + $email_builder = (new PaymentEmail())->build($this->payment, $contact); + + $this->payment->client->contacts->each(function ($contact) use ($email_builder) { + if ($contact->send_invoice && $contact->email) { + EmailPayment::dispatchNow($this->payment, $email_builder, $contact); + } + }); + } +} diff --git a/app/Services/Quote/QuoteService.php b/app/Services/Quote/QuoteService.php index 28f5ed9634f9..ab0c4d5961a4 100644 --- a/app/Services/Quote/QuoteService.php +++ b/app/Services/Quote/QuoteService.php @@ -35,6 +35,20 @@ class QuoteService return $this; } + public function getQuotePdf($contact) + { + $get_invoice_pdf = new GetQuotePdf(); + + return $get_invoice_pdf($this->quote, $contact); + } + + public function sendEmail($contact) + { + $send_email = new SendEmail($this->quote); + + return $send_email->run(null, $contact); + } + /** * Applies the invoice number * @return $this InvoiceService object diff --git a/app/Services/Quote/SendEmail.php b/app/Services/Quote/SendEmail.php new file mode 100644 index 000000000000..f721beb5e7fc --- /dev/null +++ b/app/Services/Quote/SendEmail.php @@ -0,0 +1,40 @@ +quote = $quote; + } + + /** + * Builds the correct template to send + * @param string $reminder_template The template name ie reminder1 + * @return array + */ + public function run($reminder_template = null, $contact = null): array + { + if (!$reminder_template) { + $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) use ($email_builder) { + if ($invitation->contact->send_invoice && $invitation->contact->email) { + EmailQuote::dispatchNow($email_builder, $invitation); + } + }); + + + } +} diff --git a/app/Utils/Traits/MakesReminders.php b/app/Utils/Traits/MakesReminders.php index 93379f57670e..46327c580883 100644 --- a/app/Utils/Traits/MakesReminders.php +++ b/app/Utils/Traits/MakesReminders.php @@ -40,12 +40,12 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } } - + if ($settings->enable_reminder1 !== false && $settings->schedule_reminder1 == 'before_due_date' && $settings->num_days_reminder1 > 0) { @@ -54,7 +54,7 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } @@ -69,12 +69,12 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } } - + if ($settings->enable_reminder2 !== false && $settings->schedule_reminder2 == 'after_invoice_date' && $settings->num_days_reminder2 > 0) { @@ -83,12 +83,12 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } } - + if ($settings->enable_reminder2 !== false && $settings->schedule_reminder2 == 'before_due_date' && $settings->num_days_reminder2 > 0) { @@ -97,7 +97,7 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } @@ -112,7 +112,7 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } @@ -126,12 +126,12 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } } - + if ($settings->enable_reminder3 !== false && $settings->schedule_reminder3 == 'before_due_date' && $settings->num_days_reminder3 > 0) { @@ -140,7 +140,7 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } @@ -155,13 +155,53 @@ trait MakesReminders if (!$nsd) { $nsd = $reminder_date; } - + if ($reminder_date->lt($nsd)) { $nsd = $reminder_date; } } - + $this->next_send_date = $nsd; $this->save(); } + + public function inReminderWindow($schedule_reminder, $num_days_reminder) + { + switch ($schedule_reminder) { + case 'after_invoice_date': + return Carbon::parse($this->date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); + break; + case 'before_due_date': + return Carbon::parse($this->due_date)->subDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); + break; + case 'after_due_date': + return Carbon::parse($this->due_date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); + break; + default: + # code... + break; + } + } + + public function calculateTemplate(): string + { + //if invoice is currently a draft, or being marked as sent, this will be the initial email + $customer = $this->client; + + //if the invoice + if ($customer->getSetting('enable_reminder1') !== false && $this->inReminderWindow($customer->getSetting('schedule_reminder1'), + $customer->getSetting('num_days_reminder1'))) { + return 'template1'; + } elseif ($customer->getSetting('enable_reminder2') !== false && $this->inReminderWindow($customer->getSetting('schedule_reminder2'), + $customer->getSetting('num_days_reminder2'))) { + return 'template2'; + } elseif ($customer->getSetting('enable_reminder3') !== false && $this->inReminderWindow($customer->getSetting('schedule_reminder3'), + $customer->getSetting('num_days_reminder3'))) { + return 'template3'; + } else { + return 'invoice'; + } + + //also implement endless reminders here + } }