Subscription calculations test'

This commit is contained in:
David Bomba 2021-10-21 15:08:46 +11:00
parent dfa773d6b9
commit 3de5665d94
5 changed files with 191 additions and 8 deletions

View File

@ -21,7 +21,11 @@ use Illuminate\Support\Carbon;
class ProRata
{
/**
* Returns the amount to refund based on
* the time interval and the frequency duration
*
* @param float $amount
* @param Carbon $from_date
* @param Carbon $to_date
@ -36,6 +40,23 @@ class ProRata
return round( (($days/$days_in_frequency) * $amount),2);
}
/**
* Returns the amount to charge based on
* the time interval and the frequency duration
*
* @param float $amount
* @param Carbon $from_date
* @param Carbon $to_date
* @param int $frequency
* @return float
*/
public function charge(float $amount, Carbon $from_date, Carbon $to_date, int $frequency) :float
{
$days = $from_date->diffInDays($to_date);
$days_in_frequency = $this->getDaysInFrequency($frequency);
return round( (($days/$days_in_frequency) * $amount),2);
}
/**
* Prepares the line items of an invoice

View File

@ -11,6 +11,7 @@
namespace App\Helpers\Subscription;
use App\Helpers\Invoice\ProRata;
use App\Models\Invoice;
use App\Models\RecurringInvoice;
use App\Models\Subscription;
@ -20,11 +21,15 @@ use App\Models\Subscription;
*/
class SubscriptionCalculator
{
public Subscription $subscription;
public function __construct(Subscription $subscription)
public Subscription $target_subscription;
public Invoice $invoice
public function __construct(Subscription $target_subscription, Invoice $invoice)
{
$this->subscription = $subscription;
$this->target_subscription = $target_subscription;
$this->invoice = $invoice;
}
/**
@ -34,15 +39,63 @@ class SubscriptionCalculator
*
* @return bool
*/
public function isPaidUp(RecurringInvoice $recurring_invoice) :bool
public function isPaidUp() :bool
{
$outstanding_invoices_exist = Invoice::whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('recurring_id', $recurring_invoice->id)
->where('balance', '>', 0)
->exists();
->where('subscription_id', $this->invoice->subscription_id)
->where('client_id', $this->invoice->client_id)
->where('balance', '>', 0)
->exists();
return ! $outstanding_invoices_exist;
}
public function calcUpgradePlan()
{
//set the starting refund amount
$refund_amount = 0;
//are they paid up to date.
//yes - calculate refund
if($this->isPaidUp())
$refund_invoice = $this->getRefundInvoice();
if($refund_invoice)
{
$subscription = Subscription::find($this->invoice->subscription_id);
$pro_rata = new ProRata();
$to_date = $subscription->getNextDateForFrequency(Carbon::parse($refund_invoice->date), $subscription->frequency_id);
$refund_amount = $pro_rata->refund($refund_invoice->amount, now(), $to_date, $subscription->frequency_id);
$charge_amount = $pro_rata->charge($this->target_subscription->price, now(), $to_date, $this->target_subscription->frequency_id);
return ($charge_amount - $refund_amount);
}
//no - return full freight charge.
return $this->target_subscription->price;
}
public function executeUpgradePlan()
{
}
private function getRefundInvoice()
{
return Invoice::where('subscription_id', $this->invoice->subscription_id)
->where('client_id', $this->invoice->client_id)
->where('is_deleted', 0)
->where('balance', '>', 0)
->orderBy('id', 'desc')
->first();
}
}

View File

@ -934,6 +934,39 @@ class SubscriptionService
}
public function getNextDateForFrequency($date, $frequency)
{
switch ($frequency) {
case RecurringInvoice::FREQUENCY_DAILY:
return $date->addDay();
case RecurringInvoice::FREQUENCY_WEEKLY:
return $date->addDays(7);
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
return $date->addDays(13);
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
return $date->addWeeks(4);
case RecurringInvoice::FREQUENCY_MONTHLY:
return $date->addMonthNoOverflow();
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
return $date->addMonthNoOverflow(2);
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
return $date->addMonthNoOverflow(3);
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
return $date->addMonthNoOverflow(4);
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
return $date->addMonthNoOverflow(6);
case RecurringInvoice::FREQUENCY_ANNUALLY:
return $date->addYear();
case RecurringInvoice::FREQUENCY_TWO_YEARS:
return $date->addYears(2);
case RecurringInvoice::FREQUENCY_THREE_YEARS:
return $date->addYears(3);
default:
return 0;
}
}
/**
* 'email' => $this->email ?? $this->contact->email,
* 'quantity' => $this->quantity,

View File

@ -12,6 +12,7 @@
namespace Database\Factories;
use App\Models\RecurringInvoice;
use App\Models\Subscription;
use Illuminate\Database\Eloquent\Factories\Factory;
@ -32,7 +33,7 @@ class SubscriptionFactory extends Factory
public function definition()
{
return [
'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY
];
}
}

View File

@ -0,0 +1,75 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Unit;
use App\Models\Invoice;
use App\Models\Subscription;
use Tests\MockUnitData;
use Tests\TestCase;
/**
* @test
*/
class SubscriptionsCalcTest extends TestCase
{
use MockUnitData;
/**
* Important consideration with Base64
* encoding checks.
*
* No method can guarantee against false positives.
*/
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
}
public function testCalcUpgradePrice()
{
$subscription = Subscription::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'price' => 10,
]);
$invoice = Invoice::factory()->create([
'line_items' => $this->buildLineItems(),
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'tax_rate1' => 0,
'tax_name1' => '',
'tax_rate2' => 0,
'tax_name2' => '',
'tax_rate3' => 0,
'tax_name3' => '',
'discount' => 0,
'subscription_id' => $subscription->id,
'date' => now()
]);
$invoice = $invoice->calc()->getInvoice();
$this->assertEquals(10, $invoice->amount);
$invoice->service()->markSent()->save();
$this->assertEquals(10, $invoice->amount);
$this->assertEquals(10, $invoice->balance);
}
}