From c010c99547a736ffcf3a241760f0029f3539da69 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 17 Feb 2024 10:09:59 +1100 Subject: [PATCH] Build pipeline for payment --- .../Subscription/SubscriptionCalculator.php | 96 --------- .../BillingPortal/Payments/Methods.php | 30 ++- app/Models/Subscription.php | 6 + .../Subscription/SubscriptionCalculator.php | 182 ++++++++++++++++++ 4 files changed, 216 insertions(+), 98 deletions(-) delete mode 100644 app/Helpers/Subscription/SubscriptionCalculator.php create mode 100644 app/Services/Subscription/SubscriptionCalculator.php diff --git a/app/Helpers/Subscription/SubscriptionCalculator.php b/app/Helpers/Subscription/SubscriptionCalculator.php deleted file mode 100644 index 4413b0d593a7..000000000000 --- a/app/Helpers/Subscription/SubscriptionCalculator.php +++ /dev/null @@ -1,96 +0,0 @@ -target_subscription = $target_subscription; - $this->invoice = $invoice; - } - - /** - * Tests if the user is currently up - * to date with their payments for - * a given recurring invoice - * - * @return bool - */ - public function isPaidUp(): bool - { - $outstanding_invoices_exist = Invoice::query()->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) - ->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; - - $refund_invoice = false; - - //are they paid up to date. - - //yes - calculate refund - if ($this->isPaidUp()) { - $refund_invoice = $this->getRefundInvoice(); - } - - if ($refund_invoice) { - /** @var \App\Models\Subscription $subscription **/ - $subscription = Subscription::find($this->invoice->subscription_id); - $pro_rata = new ProRata(); - - $to_date = $subscription->service()->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) - ->orderBy('id', 'desc') - ->first(); - } -} diff --git a/app/Livewire/BillingPortal/Payments/Methods.php b/app/Livewire/BillingPortal/Payments/Methods.php index a27397ef6747..d7b75781dfd9 100644 --- a/app/Livewire/BillingPortal/Payments/Methods.php +++ b/app/Livewire/BillingPortal/Payments/Methods.php @@ -12,8 +12,9 @@ namespace App\Livewire\BillingPortal\Payments; -use App\Models\Subscription; use Livewire\Component; +use App\Models\Subscription; +use Illuminate\Support\Facades\Cache; class Methods extends Component { @@ -36,7 +37,32 @@ class Methods extends Component public function handleSelect(string $company_gateway_id, string $gateway_type_id) { - dd($this->context['bundle']); + + $this->dispatch('purchase.context', property: 'client_id', value: auth()->guard('contact')->user()->client->hashed_id); + + nlog($this->context); + + $invoice = $this->subscription + ->calc() + ->buildPurchaseInvoice($this->context) + ->service() + ->fillDefaults() + ->adjustInventory() + ->save(); + + + + Cache::put($this->hash, [ + 'subscription_id' => $this->subscription->hashed_id, + 'email' => auth()->guard('contact')->user()->email, + 'client_id' => auth()->guard('contact')->user()->client->hashed_id, + 'invoice_id' => $invoice->hashed_id, + 'context' => 'purchase', + 'campaign' => $this->context['campaign'], + 'bundle' => $this->context['bundle'], + ], now()->addMinutes(60)); + + } public function render() diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php index 132b57cfe6e3..50d922264240 100644 --- a/app/Models/Subscription.php +++ b/app/Models/Subscription.php @@ -12,6 +12,7 @@ namespace App\Models; use App\Services\Subscription\PaymentLinkService; +use App\Services\Subscription\SubscriptionCalculator; use App\Services\Subscription\SubscriptionService; use App\Services\Subscription\SubscriptionStatus; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -146,6 +147,11 @@ class Subscription extends BaseModel return (new SubscriptionStatus($this, $recurring_invoice))->run(); } + public function calc(): SubscriptionCalculator + { + return new SubscriptionCalculator($this); + } + public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(Company::class); diff --git a/app/Services/Subscription/SubscriptionCalculator.php b/app/Services/Subscription/SubscriptionCalculator.php new file mode 100644 index 000000000000..b7b7e37eec53 --- /dev/null +++ b/app/Services/Subscription/SubscriptionCalculator.php @@ -0,0 +1,182 @@ +subscription->company_id, $this->subscription->user_id); + $invoice->subscription_id = $this->subscription->id; + $invoice->client_id = $this->decodePrimaryKey($context['client_id']); + $invoice->is_proforma = true; + $invoice->number = "####" . ctrans('texts.subscription') . "_" . now()->format('Y-m-d') . "_" . rand(0, 100000); + $invoice->line_items = $this->buildItems($context); + + if(isset($context['valid_coupon']) && $context['valid_coupon']) { + $invoice->discount = $this->subscription->promo_discount; + $invoice->is_amount_discount = $this->subscription->is_amount_discount; + } + + return $invoice_repo->save([], $invoice); + + } + + /** + * Build Line Items + * + * @param array $context + * @return array + */ + private function buildItems(array $context): array + { + + $bundle = $context['bundle']; + + $recurring = array_merge(isset($bundle['recurring_products']) ? $bundle['recurring_products'] : [], isset($bundle['optional_recurring_products']) ? $bundle['optional_recurring_products'] : []); + $one_time = array_merge(isset($bundle['one_time_products']) ? $bundle['one_time_products'] : [], isset($bundle['optional_one_time_products']) ? $bundle['optional_one_time_products'] : []); + + $items = array_filter(array_merge($recurring, $one_time), function ($product) { + return $product['quantity'] >= 1; + }); + + return collect($items)->map(function ($item){ + + $line_item = new InvoiceItem(); + $line_item->product_key = $item['product']['product_key']; + $line_item->quantity = (float) $item['quantity']; + $line_item->cost = (float) ['product']['price']; + $line_item->notes = $item['product']['notes']; + + return $line_item; + + })->toArray(); + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /** + * Tests if the user is currently up + * to date with their payments for + * a given recurring invoice + * + * @return bool + */ + public function isPaidUp(Invoice $invoice): bool + { + $outstanding_invoices_exist = Invoice::query()->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('subscription_id', $invoice->subscription_id) + ->where('client_id', $invoice->client_id) + ->where('balance', '>', 0) + ->exists(); + + return ! $outstanding_invoices_exist; + } + + public function calcUpgradePlan(Invoice $invoice) + { + //set the starting refund amount + $refund_amount = 0; + + $refund_invoice = false; + + //are they paid up to date. + + //yes - calculate refund + if ($this->isPaidUp($invoice)) { + $refund_invoice = $this->getRefundInvoice($invoice); + } + + if ($refund_invoice) { + /** @var \App\Models\Subscription $subscription **/ + $subscription = Subscription::find($invoice->subscription_id); + $pro_rata = new ProRata(); + + $to_date = $subscription->service()->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->subscription->price, now(), $to_date, $this->subscription->frequency_id); + + return $charge_amount - $refund_amount; + } + + //no - return full freight charge. + return $this->subscription->price; + } + + public function executeUpgradePlan() {} + + private function getRefundInvoice(Invoice $invoice) + { + return Invoice::where('subscription_id', $invoice->subscription_id) + ->where('client_id', $invoice->client_id) + ->where('is_deleted', 0) + ->orderBy('id', 'desc') + ->first(); + } +}