From 3de5665d944622bc5cf7abdf9a295627c968abf1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 21 Oct 2021 15:08:46 +1100 Subject: [PATCH] Subscription calculations test' --- app/Helpers/Invoice/ProRata.php | 21 ++++++ .../Subscription/SubscriptionCalculator.php | 67 +++++++++++++++-- .../Subscription/SubscriptionService.php | 33 ++++++++ database/factories/SubscriptionFactory.php | 3 +- tests/Unit/SubscriptionsCalcTest.php | 75 +++++++++++++++++++ 5 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 tests/Unit/SubscriptionsCalcTest.php diff --git a/app/Helpers/Invoice/ProRata.php b/app/Helpers/Invoice/ProRata.php index 4026d41d30..c27394662c 100644 --- a/app/Helpers/Invoice/ProRata.php +++ b/app/Helpers/Invoice/ProRata.php @@ -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 diff --git a/app/Helpers/Subscription/SubscriptionCalculator.php b/app/Helpers/Subscription/SubscriptionCalculator.php index 597f727ca6..3956ef4617 100644 --- a/app/Helpers/Subscription/SubscriptionCalculator.php +++ b/app/Helpers/Subscription/SubscriptionCalculator.php @@ -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(); + + } + } \ No newline at end of file diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index da820b59c7..0d6cd9f553 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -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, diff --git a/database/factories/SubscriptionFactory.php b/database/factories/SubscriptionFactory.php index a9b008244c..63bff1c989 100644 --- a/database/factories/SubscriptionFactory.php +++ b/database/factories/SubscriptionFactory.php @@ -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 ]; } } diff --git a/tests/Unit/SubscriptionsCalcTest.php b/tests/Unit/SubscriptionsCalcTest.php new file mode 100644 index 0000000000..7100f25b85 --- /dev/null +++ b/tests/Unit/SubscriptionsCalcTest.php @@ -0,0 +1,75 @@ +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); + + } + + +} \ No newline at end of file