diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index db9d4f4e16ae..b2b4937286ec 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -410,22 +410,22 @@ class CompanySettings extends BaseSettings $data->country_id = (string)config('ninja.i18n.country_id'); $data->translations = (object) []; - $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject(); - $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate(); - $data->email_subject_quote = EmailTemplateDefaults::emailQuoteSubject(); - $data->email_subject_payment = EmailTemplateDefaults::emailPaymentSubject(); - $data->email_subject_statement = EmailTemplateDefaults::emailStatementSubject(); - $data->email_template_quote = EmailTemplateDefaults::emailQuoteTemplate(); - $data->email_template_payment = EmailTemplateDefaults::emailPaymentTemplate(); - $data->email_template_statement = EmailTemplateDefaults::emailStatementTemplate(); - $data->email_subject_reminder1 = EmailTemplateDefaults::emailReminder1Subject(); - $data->email_subject_reminder2 = EmailTemplateDefaults::emailReminder2Subject(); - $data->email_subject_reminder3 = EmailTemplateDefaults::emailReminder3Subject(); - $data->email_subject_reminder_endless = EmailTemplateDefaults::emailReminderEndlessSubject(); - $data->email_template_reminder1 = EmailTemplateDefaults::emailReminder1Template(); - $data->email_template_reminder2 = EmailTemplateDefaults::emailReminder2Template(); - $data->email_template_reminder3 = EmailTemplateDefaults::emailReminder3Template(); - $data->email_template_reminder_endless = EmailTemplateDefaults::emailReminderEndlessTemplate(); + // $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject(); + // $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate(); + // $data->email_subject_quote = EmailTemplateDefaults::emailQuoteSubject(); + // $data->email_subject_payment = EmailTemplateDefaults::emailPaymentSubject(); + // $data->email_subject_statement = EmailTemplateDefaults::emailStatementSubject(); + // $data->email_template_quote = EmailTemplateDefaults::emailQuoteTemplate(); + // $data->email_template_payment = EmailTemplateDefaults::emailPaymentTemplate(); + // $data->email_template_statement = EmailTemplateDefaults::emailStatementTemplate(); + // $data->email_subject_reminder1 = EmailTemplateDefaults::emailReminder1Subject(); + // $data->email_subject_reminder2 = EmailTemplateDefaults::emailReminder2Subject(); + // $data->email_subject_reminder3 = EmailTemplateDefaults::emailReminder3Subject(); + // $data->email_subject_reminder_endless = EmailTemplateDefaults::emailReminderEndlessSubject(); + // $data->email_template_reminder1 = EmailTemplateDefaults::emailReminder1Template(); + // $data->email_template_reminder2 = EmailTemplateDefaults::emailReminder2Template(); + // $data->email_template_reminder3 = EmailTemplateDefaults::emailReminder3Template(); + // $data->email_template_reminder_endless = EmailTemplateDefaults::emailReminderEndlessTemplate(); return self::setCasts($data, self::$casts); diff --git a/app/Http/Requests/Company/DestroyCompanyRequest.php b/app/Http/Requests/Company/DestroyCompanyRequest.php index a67232090f98..7a76b1df6fff 100644 --- a/app/Http/Requests/Company/DestroyCompanyRequest.php +++ b/app/Http/Requests/Company/DestroyCompanyRequest.php @@ -24,7 +24,7 @@ class DestroyCompanyRequest extends Request public function authorize() : bool { - return auth()->user()->can('edit', $this->company); + return auth()->user()->isOwner(); } } \ No newline at end of file diff --git a/app/Http/Requests/User/DestroyUserRequest.php b/app/Http/Requests/User/DestroyUserRequest.php index 96a3ce05c7b5..7eb489b373e9 100644 --- a/app/Http/Requests/User/DestroyUserRequest.php +++ b/app/Http/Requests/User/DestroyUserRequest.php @@ -24,7 +24,7 @@ class DestroyUserRequest extends Request public function authorize() : bool { - return auth()->user()->can('edit', $this->user); + return auth()->user()->isOwner(); } } \ No newline at end of file diff --git a/app/Models/Client.php b/app/Models/Client.php index c1bfa0cb95b6..fc311bf55886 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -182,7 +182,7 @@ class Client extends BaseModel public function locale() { - return $this->language()->locale; + return $this->language()->locale ?: 'en'; } public function date_format() diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 44dcc2cd93d5..82ec4dae9a0e 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -25,6 +25,7 @@ use App\Utils\Number; use App\Utils\Traits\InvoiceEmailBuilder; use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesInvoiceValues; +use App\Utils\Traits\MakesReminders; use App\Utils\Traits\NumberFormatter; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -43,7 +44,8 @@ class Invoice extends BaseModel use PresentableTrait; use MakesInvoiceValues; use InvoiceEmailBuilder; - + use MakesReminders; + protected $presenter = 'App\Models\Presenters\InvoicePresenter'; protected $hidden = [ @@ -439,6 +441,8 @@ class Invoice extends BaseModel $this->markInvitationsSent(); + $this->setReminder(); + event(new InvoiceWasMarkedSent($this)); UpdateClientBalance::dispatchNow($this->client, $this->balance); diff --git a/app/Models/User.php b/app/Models/User.php index bbfa308c8acb..681075fb5a2f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -235,6 +235,13 @@ class User extends Authenticatable implements MustVerifyEmail } + public function isOwner() : bool + { + + return $this->company_user->is_owner; + + } + /** * Returns all user created contacts * diff --git a/app/Utils/Traits/InvoiceEmailBuilder.php b/app/Utils/Traits/InvoiceEmailBuilder.php index 37ce15b48929..524b8bf5113e 100644 --- a/app/Utils/Traits/InvoiceEmailBuilder.php +++ b/app/Utils/Traits/InvoiceEmailBuilder.php @@ -48,8 +48,24 @@ trait InvoiceEmailBuilder $client = $this->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', [], null, $this->client->locale()); + } + $subject_template = $client->getSetting('email_subject_'.$reminder_template); + if(iconv_strlen($subject_template) == 0){ + + if($reminder_template == 'invoice') + $subject_template = trans('texts.invoice_subject', [], null, $this->client->locale()); + else + $subject_template = trans('texts.reminder_subject', [], null, $this->client->locale()); + + } + + $data['body'] = $this->parseTemplate($body_template, false); $data['subject'] = $this->parseTemplate($subject_template, true); diff --git a/app/Utils/Traits/MakesReminders.php b/app/Utils/Traits/MakesReminders.php new file mode 100644 index 000000000000..1c694b9ef518 --- /dev/null +++ b/app/Utils/Traits/MakesReminders.php @@ -0,0 +1,165 @@ +client->getMergedSettings(); + + if(!$this->isPayable()) + { + $this->next_send_date = null; + $this->save(); + return; //exit early + } + + $nsd = null; + + if($settings->enable_reminder1 !== false && + $settings->schedule_reminder1 == 'after_invoice_date' && + $settings->num_days_reminder1 > 0) + { + $reminder_date = Carbon::parse($this->date)->addDays($settings->num_days_reminder1); + + 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) + { + $reminder_date = Carbon::parse($this->due_date)->subDays($settings->num_days_reminder1); + + if(!$nsd) + $nsd = $reminder_date; + + if($reminder_date->lt($nsd)) + $nsd = $reminder_date; + } + + + if($settings->enable_reminder1 !== false && + $settings->schedule_reminder1 == 'after_due_date' && + $settings->num_days_reminder1 > 0) + { + $reminder_date = Carbon::parse($this->due_date)->addDays($settings->num_days_reminder1); + + 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) + { + $reminder_date = Carbon::parse($this->date)->addDays($settings->num_days_reminder2); + + 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) + { + $reminder_date = Carbon::parse($this->due_date)->subDays($settings->num_days_reminder2); + + if(!$nsd) + $nsd = $reminder_date; + + if($reminder_date->lt($nsd)) + $nsd = $reminder_date; + } + + + if($settings->enable_reminder2 !== false && + $settings->schedule_reminder2 == 'after_due_date' && + $settings->num_days_reminder2 > 0) + { + $reminder_date = Carbon::parse($this->due_date)->addDays($settings->num_days_reminder2); + + if(!$nsd) + $nsd = $reminder_date; + + if($reminder_date->lt($nsd)) + $nsd = $reminder_date; + } + + if($settings->enable_reminder3 !== false && + $settings->schedule_reminder3 == 'after_invoice_date' && + $settings->num_days_reminder3 > 0) + { + $reminder_date = Carbon::parse($this->date)->addDays($settings->num_days_reminder3); + + 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) + { + $reminder_date = Carbon::parse($this->due_date)->subDays($settings->num_days_reminder3); + + if(!$nsd) + $nsd = $reminder_date; + + if($reminder_date->lt($nsd)) + $nsd = $reminder_date; + } + + + if($settings->enable_reminder3 !== false && + $settings->schedule_reminder3 == 'after_due_date' && + $settings->num_days_reminder3 > 0) + { + $reminder_date = Carbon::parse($this->due_date)->addDays($settings->num_days_reminder3); + + if(!$nsd) + $nsd = $reminder_date; + + if($reminder_date->lt($nsd)) + $nsd = $reminder_date; + } + + $this->next_send_date = $nsd; + $this->save(); + + } + +} + diff --git a/tests/Integration/CheckRemindersTest.php b/tests/Integration/CheckRemindersTest.php new file mode 100644 index 000000000000..ca3eaa270ed5 --- /dev/null +++ b/tests/Integration/CheckRemindersTest.php @@ -0,0 +1,169 @@ +makeTestData(); + } + + public function test_after_invoice_date_reminder() + { + $this->invoice->date = now(); + $this->invoice->due_date = Carbon::now()->addDays(30); + + $settings = $this->company->settings; + $settings->enable_reminder1 = true; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 7; + $settings->enable_reminder2 = true; + $settings->schedule_reminder2 = 'before_due_date'; + $settings->num_days_reminder2 = 1; + $settings->enable_reminder3 = true; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 1; + + $this->company->settings = $settings; + $this->invoice->markSent(); + $this->invoice->setReminder($settings); + + $this->assertEquals(0, Carbon::now()->addDays(7)->diffInDays($this->invoice->next_send_date)); + } + + public function test_no_reminders_sent_to_paid_invoices() + { + $this->invoice->date = now(); + $this->invoice->due_date = Carbon::now()->addDays(30); + + $settings = $this->company->settings; + $settings->enable_reminder1 = true; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 7; + $settings->enable_reminder2 = true; + $settings->schedule_reminder2 = 'before_due_date'; + $settings->num_days_reminder2 = 1; + $settings->enable_reminder3 = true; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 1; + + $this->company->settings = $settings; + $this->invoice->markSent(); + $this->invoice->setStatus(Invoice::STATUS_PAID); + $this->invoice->setReminder($settings); + + $this->assertEquals($this->invoice->next_send_date, null); + } + + public function test_before_due_date_reminder() + { + $this->invoice->date = now(); + $this->invoice->due_date = Carbon::now()->addDays(30); + + $settings = $this->company->settings; + $settings->enable_reminder1 = true; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 50; + $settings->enable_reminder2 = true; + $settings->schedule_reminder2 = 'before_due_date'; + $settings->num_days_reminder2 = 29; + $settings->enable_reminder3 = true; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 1; + + $this->company->settings = $settings; + $this->invoice->markSent(); + $this->invoice->setReminder($settings); + + $this->assertEquals(0, Carbon::parse($this->invoice->due_date)->subDays(29)->diffInDays($this->invoice->next_send_date)); + } + + public function test_after_due_date_reminder() + { + $this->invoice->date = now(); + $this->invoice->due_date = Carbon::now()->addDays(30); + + $settings = $this->company->settings; + $settings->enable_reminder1 = true; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 50; + $settings->enable_reminder2 = false; + $settings->schedule_reminder2 = 'before_due_date'; + $settings->num_days_reminder2 = 50; + $settings->enable_reminder3 = true; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 1; + + $this->company->settings = $settings; + $this->invoice->markSent(); + $this->invoice->setReminder($settings); + + $this->assertEquals(0, Carbon::parse($this->invoice->due_date)->addDays(1)->diffInDays($this->invoice->next_send_date)); + } + + public function test_turning_off_reminders() + { + $this->invoice->date = now(); + $this->invoice->due_date = Carbon::now()->addDays(30); + + $settings = $this->company->settings; + $settings->enable_reminder1 = false; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 50; + $settings->enable_reminder2 = false; + $settings->schedule_reminder2 = 'before_due_date'; + $settings->num_days_reminder2 = 50; + $settings->enable_reminder3 = false; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 1; + + $this->company->settings = $settings; + $this->invoice->markSent(); + $this->invoice->setReminder($settings); + + $this->assertEquals($this->invoice->next_send_date, null); + } + + public function test_edge_case_num_days_equals_zero_reminders() + { + $this->invoice->date = now(); + $this->invoice->due_date = Carbon::now()->addDays(30); + + $settings = $this->company->settings; + $settings->enable_reminder1 = false; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 0; + $settings->enable_reminder2 = false; + $settings->schedule_reminder2 = 'before_due_date'; + $settings->num_days_reminder2 = 0; + $settings->enable_reminder3 = false; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 0; + + $this->company->settings = $settings; + $this->invoice->markSent(); + $this->invoice->setReminder($settings); + + $this->assertEquals($this->invoice->next_send_date, null); + } +} \ No newline at end of file