mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Implement invoice reminder scheduler (#3160)
* Do not set email template defaults in settings * Set invoice reminders, trait + tets * Fixes for tets * Only all an account owner to delete companies/users
This commit is contained in:
parent
556b2ab1c9
commit
e125052f96
@ -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);
|
||||
|
||||
|
@ -24,7 +24,7 @@ class DestroyCompanyRequest extends Request
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->company);
|
||||
return auth()->user()->isOwner();
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ class DestroyUserRequest extends Request
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->user);
|
||||
return auth()->user()->isOwner();
|
||||
}
|
||||
|
||||
}
|
@ -182,7 +182,7 @@ class Client extends BaseModel
|
||||
|
||||
public function locale()
|
||||
{
|
||||
return $this->language()->locale;
|
||||
return $this->language()->locale ?: 'en';
|
||||
}
|
||||
|
||||
public function date_format()
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
|
||||
|
165
app/Utils/Traits/MakesReminders.php
Normal file
165
app/Utils/Traits/MakesReminders.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Utils\Traits;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Class MakesReminders.
|
||||
*/
|
||||
trait MakesReminders
|
||||
{
|
||||
|
||||
public function setReminder($settings = null)
|
||||
{
|
||||
if(!$settings)
|
||||
$settings = $this->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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
169
tests/Integration/CheckRemindersTest.php
Normal file
169
tests/Integration/CheckRemindersTest.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Integration;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Utils\Traits\MakesReminder
|
||||
*/
|
||||
class CheckRemindersTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
use DatabaseTransactions;
|
||||
use MakesReminders;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user