mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Subscription calculations test'
This commit is contained in:
parent
dfa773d6b9
commit
3de5665d94
@ -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
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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,
|
||||
|
@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
75
tests/Unit/SubscriptionsCalcTest.php
Normal file
75
tests/Unit/SubscriptionsCalcTest.php
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user