diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 2e246e1b514f..5e40c642af1a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -22,6 +22,7 @@ use App\Jobs\Ninja\CompanySizeCheck; use App\Jobs\Ninja\QueueSize; use App\Jobs\Ninja\SystemMaintenance; use App\Jobs\Ninja\TaskScheduler; +use App\Jobs\Quote\QuoteCheckExpired; use App\Jobs\Util\DiskCleanup; use App\Jobs\Util\ReminderJob; use App\Jobs\Util\SchedulerCheck; @@ -70,6 +71,9 @@ class Kernel extends ConsoleKernel /* Sends recurring invoices*/ $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping(); + /* Fires notifications for expired Quotes */ + $schedule->job(new QuoteCheckExpired)->dailyAt('05:00')->withoutOverlapping(); + /* Performs auto billing */ $schedule->job(new AutoBillCron)->dailyAt('06:00')->withoutOverlapping(); diff --git a/app/Jobs/Quote/QuoteCheckExpired.php b/app/Jobs/Quote/QuoteCheckExpired.php index 4c3a82dd1436..4e5c5d77b388 100644 --- a/app/Jobs/Quote/QuoteCheckExpired.php +++ b/app/Jobs/Quote/QuoteCheckExpired.php @@ -12,9 +12,14 @@ namespace App\Jobs\Quote; +use App\Jobs\Mail\NinjaMailer; +use App\Jobs\Mail\NinjaMailerJob; +use App\Jobs\Mail\NinjaMailerObject; use App\Libraries\MultiDB; +use App\Mail\Admin\QuoteExpiredObject; use App\Models\Quote; use App\Repositories\BaseRepository; +use App\Utils\Traits\Notifications\UserNotifies; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -23,7 +28,7 @@ use Illuminate\Queue\SerializesModels; class QuoteCheckExpired implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies; /** * Create a new job instance. @@ -65,11 +70,43 @@ class QuoteCheckExpired implements ShouldQueue $query->where('is_disabled', 0); }) // ->where('due_date', '<='. now()->toDateTimeString()) - ->whereBetween('due_date', [now()->subDay(), now()]) + ->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()]) ->cursor() ->each(function ($quote){ - + $this->queueExpiredQuoteNotification($quote); }); } + private function queueExpiredQuoteNotification(Quote $quote) + { + $nmo = new NinjaMailerObject; + $nmo->mailable = new NinjaMailer((new QuoteExpiredObject($quote, $quote->company))->build()); + $nmo->company = $quote->company; + $nmo->settings = $quote->company->settings; + + /* We loop through each user and determine whether they need to be notified */ + foreach ($quote->company->company_users as $company_user) { + + /* The User */ + $user = $company_user->user; + + if (! $user) { + continue; + } + + /* Returns an array of notification methods */ + $methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_expired', 'quote_expired_all']); + + /* If one of the methods is email then we fire the EntitySentMailer */ + if (($key = array_search('mail', $methods)) !== false) { + unset($methods[$key]); + + $nmo->to_user = $user; + + NinjaMailerJob::dispatch($nmo); + + } + } + } + } diff --git a/app/Mail/Admin/QuoteExpiredObject.php b/app/Mail/Admin/QuoteExpiredObject.php new file mode 100644 index 000000000000..b6fd001ae450 --- /dev/null +++ b/app/Mail/Admin/QuoteExpiredObject.php @@ -0,0 +1,103 @@ +quote = $quote; + $this->company = $company; + } + + public function build() + { + MultiDB::setDb($this->company->db); + + if (! $this->quote) { + return; + } + + App::forgetInstance('translator'); + /* Init a new copy of the translator*/ + $t = app('translator'); + /* Set the locale*/ + App::setLocale($this->company->getLocale()); + /* Set customized translations _NOW_ */ + $t->replace(Ninja::transformTranslations($this->company->settings)); + + $mail_obj = new stdClass; + $mail_obj->amount = $this->getAmount(); + $mail_obj->subject = $this->getSubject(); + $mail_obj->data = $this->getData(); + $mail_obj->markdown = 'email.admin.generic'; + $mail_obj->tag = $this->company->company_key; + + return $mail_obj; + } + + private function getAmount() + { + return Number::formatMoney($this->quote->amount, $this->quote->client); + } + + private function getSubject() + { + return + ctrans( + 'texts.notification_quote_expired_subject', + [ + 'client' => $this->quote->client->present()->name(), + 'invoice' => $this->quote->number, + ] + ); + } + + private function getData() + { + $settings = $this->quote->client->getMergedSettings(); + + $data = [ + 'title' => $this->getSubject(), + 'message' => ctrans( + 'texts.notification_quote_expired', + [ + 'amount' => $this->getAmount(), + 'client' => $this->quote->client->present()->name(), + 'invoice' => $this->quote->number, + ] + ), + 'url' => $this->quote->invitations->first()->getAdminLink(), + 'button' => ctrans('texts.view_quote'), + 'signature' => $settings->email_signature, + 'logo' => $this->company->present()->logo(), + 'settings' => $settings, + 'whitelabel' => $this->company->account->isPaid() ? true : false, + ]; + + return $data; + } +} diff --git a/lang/en/texts.php b/lang/en/texts.php index 3978323e62ba..fe2e75ac1590 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -4837,7 +4837,8 @@ $LANG = array( 'enable_applying_payments_later' => 'Enable Applying Payments Later', 'line_item_tax_rates' => 'Line Item Tax Rates', 'show_tasks_in_client_portal' => 'Show Tasks in Client Portal', - + 'notification_quote_expired_subject' => 'Quote :invoice has expired for :client', + 'notification_quote_expired' => 'The following Quote :invoice for client :client and :amount has now expired.', ); return $LANG;