Recurring Dates

This commit is contained in:
David Bomba 2020-09-13 20:20:11 +10:00
parent 2b877d3414
commit 9c7dc24bd2
12 changed files with 180 additions and 33 deletions

View File

@ -49,7 +49,6 @@ class InvoiceToRecurringInvoiceFactory
$recurring_invoice->client_id = $invoice->client_id;
$recurring_invoice->company_id = $invoice->company_id;
$recurring_invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$recurring_invoice->start_date = null;
$recurring_invoice->last_sent_date = null;
$recurring_invoice->next_send_date = null;
$recurring_invoice->remaining_cycles = 0;

View File

@ -48,7 +48,6 @@ class RecurringInvoiceFactory
$invoice->user_id = $user_id;
$invoice->company_id = $company_id;
$invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$invoice->start_date = null;
$invoice->last_sent_date = null;
$invoice->next_send_date = null;
$invoice->remaining_cycles = 0;

View File

@ -47,7 +47,6 @@ class RecurringQuoteFactory
$quote->user_id = $user_id;
$quote->company_id = $company_id;
$quote->frequency_id = RecurringQuote::FREQUENCY_MONTHLY;
$quote->start_date = null;
$quote->last_sent_date = null;
$quote->next_send_date = null;
$quote->remaining_cycles = 0;

View File

@ -101,5 +101,6 @@ class StoreRecurringInvoiceRequest extends Request
public function messages()
{
return [];
}
}

View File

@ -11,6 +11,8 @@
namespace App\Models;
use App\Helpers\Invoice\InvoiceSum;
use App\Helpers\Invoice\InvoiceSumInclusive;
use App\Models\Filterable;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
@ -74,6 +76,7 @@ class RecurringInvoice extends BaseModel
'po_number',
'date',
'due_date',
'due_date_days',
'line_items',
'settings',
'footer',
@ -93,7 +96,6 @@ class RecurringInvoice extends BaseModel
'amount',
'partial',
'frequency_id',
'start_date',
];
protected $casts = [
@ -176,7 +178,7 @@ class RecurringInvoice extends BaseModel
public function getStatusAttribute()
{
if ($this->status_id == self::STATUS_ACTIVE && $this->start_date > Carbon::now()) { //marked as active, but yet to fire first cycle
if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) { //marked as active, but yet to fire first cycle
return self::STATUS_PENDING;
} elseif ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) {
return self::STATUS_COMPLETED;
@ -215,6 +217,38 @@ class RecurringInvoice extends BaseModel
}
}
public function nextDateByFrequency($date)
{
switch ($this->frequency_id) {
case self::FREQUENCY_WEEKLY:
return Carbon::parse($date->addWeek());
case self::FREQUENCY_TWO_WEEKS:
return Carbon::parse($date->addWeeks(2));
case self::FREQUENCY_FOUR_WEEKS:
return Carbon::parse($date->addWeeks(4));
case self::FREQUENCY_MONTHLY:
return Carbon::parse($date->addMonthNoOverflow());
case self::FREQUENCY_TWO_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(2));
case self::FREQUENCY_THREE_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(3));
case self::FREQUENCY_FOUR_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(4));
case self::FREQUENCY_SIX_MONTHS:
return Carbon::parse($date->addMonthsNoOverflow(6));
case self::FREQUENCY_ANNUALLY:
return Carbon::parse($date->addYear());
case self::FREQUENCY_TWO_YEARS:
return Carbon::parse($date->addYears(2));
case self::FREQUENCY_THREE_YEARS:
return Carbon::parse($date->addYears(3));
default:
return null;
}
}
public function remainingCycles() : int
{
if ($this->remaining_cycles == 0) {
@ -299,15 +333,33 @@ class RecurringInvoice extends BaseModel
}
}
public function calc()
{
$invoice_calc = null;
if ($this->uses_inclusive_taxes) {
$invoice_calc = new InvoiceSumInclusive($this);
} else {
$invoice_calc = new InvoiceSum($this);
}
return $invoice_calc->build();
}
/*
* Important to note when playing with carbon dates - in order
* not to modify the original instance, always use a `->copy()`
*
*/
public function recurringDates()
{
//todo send back a list of the next send dates and due dates
info($this->next_send_date);
/* Return early if nothing to send back! */
if( $this->status_id == self::STATUS_COMPLETED ||
$this->status_id == self::STATUS_DRAFT ||
$this->status_id == self::STATUS_CANCELLED ||
$this->remaining_cycles == 0) {
$this->remaining_cycles == 0 ||
!$this->next_send_date) {
return [];
}
@ -318,26 +370,37 @@ class RecurringInvoice extends BaseModel
if($this->remaining_cycles == -1)
$iterations = 10;
$next_send_date = $this->next_send_date;
$due_date_days = $this->due_date_days;
$data = [];
if(!$due_date_days)
$due_date_days = 1;
$next_send_date = Carbon::parse($this->next_send_date)->copy();
$data = [];
for($x=0; $x<$iterations; $x++)
{
for($x=0; $x<$iterations; $x++)
{
$next_due_date = $next_send_date->copy()->addDays($this->due_date_days);
$data[] = [
'next_send_date' => $next_send_date->format('Y-m-d'),
'due_date' => $next_send_date->addDays($due_date_days)->format('Y-m-d')
];
$next_send_date = Carbon::parse($next_send_date);
$next_due_date = Carbon::parse($next_due_date);
}
$data[] = [
'next_send_date' => $next_send_date->format('Y-m-d'),
'due_date' => $next_due_date->format('Y-m-d'),
];
$next_send_date = $this->nextDateByFrequency($next_send_date);
return $data;
}
/*If no due date is set - unset the due_date value */
if(!$this->due_date_days || $this->due_date_days == 0){
foreach($data as $key => $value)
$data[$key]['due_date'] = '';
}
return $data;
}

View File

@ -77,7 +77,7 @@ class RecurringQuote extends BaseModel
'custom_value4',
'amount',
'frequency_id',
'start_date',
'due_date_days',
];
protected $touches = [];

View File

@ -133,8 +133,8 @@ class RecurringInvoiceTransformer extends EntityTransformer
'line_items' => $invoice->line_items ?: (array) [],
'entity_type' => 'recurring_invoice',
'frequency_id' => (string) $invoice->frequency_id,
'start_date' => $invoice->start_date ?: '',
'remaining_cycles' => (int) $invoice->remaining_cycles,
'recurring_dates' => (array) $invoice->recurringDates(),
];
}
}

View File

@ -118,7 +118,6 @@ class RecurringQuoteTransformer extends EntityTransformer
'custom_text_value2' => $quote->custom_text_value2 ?: '',
'settings' => $quote->settings ?: '',
'frequency_id' => (int) $quote->frequency_id,
'start_date' => $quote->start_date ?: '',
'last_sent_date' => $quote->last_sent_date ?: '',
'next_send_date' => $quote->next_send_date ?: '',
'remaining_cycles' => (int) $quote->remaining_cycles,

View File

@ -25,9 +25,8 @@ $factory->define(App\Models\RecurringInvoice::class, function (Faker $faker) {
'due_date' => $faker->date(),
'line_items' => false,
'frequency_id' => App\Models\RecurringInvoice::FREQUENCY_MONTHLY,
'start_date' => $faker->date(),
'last_sent_date' => $faker->date(),
'next_send_date' => $faker->date(),
'last_sent_date' => now()->subMonth(),
'next_send_date' => now()->addMonthNoOverflow(),
'remaining_cycles' => $faker->numberBetween(1, 10),
'amount' => $faker->randomFloat(2, $min = 1, $max = 1000), // 48.8932

View File

@ -71,6 +71,7 @@ class AddIsPublicToDocumentsTable extends Migration
$table->integer('remaining_cycles')->nullable()->change();
$table->dropColumn('start_date');
$table->integer('due_date_days')->nullable();
$table->date('partial_due_date')->nullable();
});
}

View File

@ -317,7 +317,7 @@ trait MockAccountData
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$recurring_invoice->remaining_cycles = 2;
$recurring_invoice->start_date = Carbon::now();
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->save();
$recurring_invoice->number = $this->getNextInvoiceNumber($this->invoice->client);
@ -327,7 +327,7 @@ trait MockAccountData
$recurring_invoice->next_send_date = Carbon::now()->addMinutes(2);
$recurring_invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$recurring_invoice->remaining_cycles = 2;
$recurring_invoice->start_date = Carbon::now();
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->save();
$recurring_invoice->number = $this->getNextInvoiceNumber($this->invoice->client);
@ -337,7 +337,7 @@ trait MockAccountData
$recurring_invoice->next_send_date = Carbon::now()->addMinutes(10);
$recurring_invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$recurring_invoice->remaining_cycles = 2;
$recurring_invoice->start_date = Carbon::now();
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->save();
$recurring_invoice->number = $this->getNextInvoiceNumber($this->invoice->client);
@ -347,7 +347,7 @@ trait MockAccountData
$recurring_invoice->next_send_date = Carbon::now()->addMinutes(15);
$recurring_invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$recurring_invoice->remaining_cycles = 2;
$recurring_invoice->start_date = Carbon::now();
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->save();
$recurring_invoice->number = $this->getNextInvoiceNumber($this->invoice->client);
@ -357,7 +357,7 @@ trait MockAccountData
$recurring_invoice->next_send_date = Carbon::now()->addMinutes(20);
$recurring_invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$recurring_invoice->remaining_cycles = 2;
$recurring_invoice->start_date = Carbon::now();
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->save();
$recurring_invoice->number = $this->getNextInvoiceNumber($this->invoice->client);
@ -367,7 +367,7 @@ trait MockAccountData
$recurring_invoice->next_send_date = Carbon::now()->addDays(10);
$recurring_invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$recurring_invoice->remaining_cycles = 2;
$recurring_invoice->start_date = Carbon::now();
$recurring_invoice->next_send_date = Carbon::now();
$recurring_invoice->save();
$recurring_invoice->number = $this->getNextInvoiceNumber($this->invoice->client);

View File

@ -0,0 +1,87 @@
<?php
namespace Tests\Unit;
use App\Factory\CloneQuoteToInvoiceFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\Invoice;
use App\Models\RecurringInvoice;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
*/
class RecurringDatesTest extends TestCase
{
use MockAccountData;
use DatabaseTransactions;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
}
public function testRecurringDatesDraftInvoice()
{
$recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id);
$recurring_invoice->line_items = $this->buildLineItems();
$recurring_invoice->client_id = $this->client->id;
$recurring_invoice->save();
$recurring_invoice->calc()->getInvoice();
$this->assertEquals(0, count($recurring_invoice->recurringDates()));
}
public function testRecurringDatesPendingInvoice()
{
$recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id);
$recurring_invoice->line_items = $this->buildLineItems();
$recurring_invoice->client_id = $this->client->id;
$recurring_invoice->status_id = RecurringInvoice::STATUS_PENDING;
$recurring_invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$recurring_invoice->remaining_cycles = 5;
$recurring_invoice->due_date_days = 5;
$recurring_invoice->next_send_date = now();
$recurring_invoice->save();
$recurring_invoice->calc()->getInvoice();
$this->assertEquals(5, count($recurring_invoice->recurringDates()));
}
public function testRecurringDatesPendingInvoiceWithNoDueDate()
{
$recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id);
$recurring_invoice->line_items = $this->buildLineItems();
$recurring_invoice->client_id = $this->client->id;
$recurring_invoice->status_id = RecurringInvoice::STATUS_PENDING;
$recurring_invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$recurring_invoice->remaining_cycles = 5;
$recurring_invoice->due_date_days = null;
$recurring_invoice->next_send_date = now();
$recurring_invoice->save();
$recurring_invoice->calc()->getInvoice();
$this->assertEquals(5, count($recurring_invoice->recurringDates()));
}
}