diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 063cbe46daea..a650ba3c7ea4 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -16,6 +16,7 @@ use App\Helpers\Invoice\InvoiceSumInclusive; use App\Models\Filterable; use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesHash; +use App\Utils\Traits\Recurring\HasRecurrence; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; @@ -29,6 +30,8 @@ class RecurringInvoice extends BaseModel use SoftDeletes; use Filterable; use MakesDates; + use HasRecurrence; + /** * Invoice Statuses. */ @@ -387,7 +390,7 @@ class RecurringInvoice extends BaseModel 'due_date' => $next_due_date->format('Y-m-d'), ]; - $next_send_date = $this->nextDateByFrequency($next_send_date); + $next_send_date = $this->calculateDueDate($next_send_date); } @@ -404,20 +407,15 @@ class RecurringInvoice extends BaseModel } - private function calculateDueDate($terms, $date) + private function calculateDueDate($date) { - switch ($terms) { - case 'client_terms': + switch ($this->due_date_days) { + case 'terms': return $this->calculateDateFromTerms($date); break; - case 'first_of_month': - return $this->calculateFirstDayOfMonth($date); - break; - case 'last_of_month': - break; default: - return $this->setDayOfMonth($date, $terms); + return $this->setDayOfMonth($date, $this->due_date_days); break; } } diff --git a/app/Utils/Traits/Recurring/HasRecurrence.php b/app/Utils/Traits/Recurring/HasRecurrence.php index 30f7d331a896..6560e18dd956 100644 --- a/app/Utils/Traits/Recurring/HasRecurrence.php +++ b/app/Utils/Traits/Recurring/HasRecurrence.php @@ -12,6 +12,7 @@ namespace App\Utils\Traits\Recurring; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Log; trait HasRecurrence { @@ -39,7 +40,7 @@ trait HasRecurrence public function calculateLastDayOfMonth($date) { if($date->isLastOfMonth()) - return $date->copy()->endOfMonth()->addMonthNoOverflow(); + return $date->copy()->addMonthNoOverflow()->endOfMonth(); return $date->copy()->endOfMonth(); } @@ -50,13 +51,19 @@ trait HasRecurrence * @param Carbon $date The start date * @param String|Int $day_of_month The day of the month */ - public function setDateOfMonth($date, $day_of_month) + public function setDayOfMonth($date, $day_of_month) { $set_date = $date->copy()->setUnitNoOverflow('day', $day_of_month, 'month'); - if($set_date->isPast()) - return $set_date->addMonthNoOverflow(); + //If the set date is less than the original date we need to add a month. + //If we are overflowing dates, then we need to diff the dates and ensure it doesn't equal 0 + if($set_date->lte($date) || $set_date->diffInDays($date) == 0) + $set_date->addMonthNoOverflow(); + + if($day_of_month == '31') + $set_date->endOfMonth(); + return $set_date; } diff --git a/tests/Unit/RecurringDueDatesTest.php b/tests/Unit/RecurringDueDatesTest.php index 6196f360b7e2..fa153791a076 100644 --- a/tests/Unit/RecurringDueDatesTest.php +++ b/tests/Unit/RecurringDueDatesTest.php @@ -24,10 +24,6 @@ class RecurringDueDatesTest extends TestCase use HasRecurrence; - public function setUp() :void - { - - } public function testFirstDate() { @@ -37,6 +33,143 @@ class RecurringDueDatesTest extends TestCase $due_date = $this->calculateFirstDayOfMonth($date); $this->assertEquals('2020-03-01', $due_date->format('Y-m-d')); + } + + public function testFirstOfMonthOnFirst() + { + + $date = Carbon::parse('2020-02-01'); + + $due_date = $this->calculateFirstDayOfMonth($date); + + $this->assertEquals('2020-03-01', $due_date->format('Y-m-d')); + + } + + + public function testFirstOfMonthOnLast() + { + + $date = Carbon::parse('2020-03-31'); + + $due_date = $this->calculateFirstDayOfMonth($date); + + $this->assertEquals('2020-04-01', $due_date->format('Y-m-d')); + + } + + public function testLastOfMonth() + { + + $date = Carbon::parse('2020-02-15'); + + $due_date = $this->calculateLastDayOfMonth($date); + + $this->assertEquals('2020-02-29', $due_date->format('Y-m-d')); + + } + + public function testLastOfMonthOnFirst() + { + + $date = Carbon::parse('2020-02-1'); + + $due_date = $this->calculateLastDayOfMonth($date); + + $this->assertEquals('2020-02-29', $due_date->format('Y-m-d')); + + } + + public function testLastOfMonthOnLast() + { + + $date = Carbon::parse('2020-02-29'); + + $due_date = $this->calculateLastDayOfMonth($date); + + $this->assertEquals('2020-03-31', $due_date->format('Y-m-d')); + + } + + public function testDayOfMonth() + { + $date = Carbon::parse('2020-02-01'); + + $due_date = $this->setDayOfMonth($date, '15'); + + $this->assertEquals('2020-02-15', $due_date->format('Y-m-d')); } + + public function testDayOfMonthInFuture() + { + $date = Carbon::parse('2020-02-16'); + + $due_date = $this->setDayOfMonth($date, '15'); + + $this->assertEquals('2020-03-15', $due_date->format('Y-m-d')); + + } + + public function testDayOfMonthSameDay() + { + $date = Carbon::parse('2020-02-01'); + + $due_date = $this->setDayOfMonth($date, '1'); + + $this->assertEquals('2020-03-01', $due_date->format('Y-m-d')); + + } + + + public function testDayOfMonthWithOverflow() + { + $date = Carbon::parse('2020-1-31'); + + $due_date = $this->setDayOfMonth($date, '31'); + + $this->assertEquals('2020-02-29', $due_date->format('Y-m-d')); + + } + + public function testDayOfMonthWithOverflow2() + { + $date = Carbon::parse('2020-02-29'); + + $due_date = $this->setDayOfMonth($date, '31'); + + $this->assertEquals('2020-03-31', $due_date->format('Y-m-d')); + + } + + public function testDayOfMonthWithOverflow3() + { + $date = Carbon::parse('2020-01-30'); + + $due_date = $this->setDayOfMonth($date, '30'); + + $this->assertEquals('2020-02-29', $due_date->format('Y-m-d')); + + } + + public function testDayOfMonthWithOverflow4() + { + $date = Carbon::parse('2019-02-28'); + + $due_date = $this->setDayOfMonth($date, '31'); + + $this->assertEquals('2019-03-31', $due_date->format('Y-m-d')); + + } + + public function testDayOfMonthWithOverflow5() + { + $date = Carbon::parse('2019-1-31'); + + $due_date = $this->setDayOfMonth($date, '31'); + + $this->assertEquals('2019-02-28', $due_date->format('Y-m-d')); + + } + }