mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Subscriptions v2
This commit is contained in:
parent
02d30ee778
commit
33d4402189
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
@ -19,15 +20,17 @@ use App\Mail\ContactPasswordlessLogin;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Subscription;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Utils\Number;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use Livewire\Component;
|
||||
|
||||
class BillingPortalPurchasev2 extends Component
|
||||
@ -179,6 +182,13 @@ class BillingPortalPurchasev2 extends Component
|
||||
*/
|
||||
public $campaign;
|
||||
|
||||
public $bundle;
|
||||
public $recurring_products;
|
||||
public $products;
|
||||
public $optional_recurring_products;
|
||||
public $optional_products;
|
||||
public $total;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
@ -196,46 +206,156 @@ class BillingPortalPurchasev2 extends Component
|
||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
||||
$this->price = $this->subscription->promo_price;
|
||||
}
|
||||
|
||||
$this->recurring_products = $this->subscription->service()->recurring_products();
|
||||
$this->products = $this->subscription->service()->products();
|
||||
$this->optional_recurring_products = $this->subscription->service()->optional_recurring_products();
|
||||
$this->optional_products = $this->subscription->service()->optional_products();
|
||||
|
||||
$this->buildBundle();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function updatingData()
|
||||
public function buildBundle()
|
||||
{
|
||||
nlog('updating');
|
||||
// nlog($this->data);
|
||||
$this->bundle = collect();
|
||||
|
||||
$data = $this->data;
|
||||
|
||||
foreach($this->recurring_products as $key => $p)
|
||||
{
|
||||
|
||||
$qty = isset($data[$key]['recurring_qty']) ? $data[$key]['recurring_qty'] : 1;
|
||||
$total = $p->price * $qty;
|
||||
|
||||
$this->bundle->push([
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
'is_recurring' => true
|
||||
]);
|
||||
}
|
||||
|
||||
foreach($this->products as $key => $p)
|
||||
{
|
||||
|
||||
$qty = 1;
|
||||
$total = $p->price * $qty;
|
||||
|
||||
$this->bundle->push([
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
'is_recurring' => false
|
||||
]);
|
||||
}
|
||||
|
||||
foreach($this->data as $key => $value)
|
||||
{
|
||||
if(isset($this->data[$key]['optional_recurring_qty']))
|
||||
{
|
||||
$p = $this->optional_recurring_products->first(function ($v,$k) use($key){
|
||||
return $k == $key;
|
||||
});
|
||||
|
||||
$qty = isset($this->data[$key]['optional_recurring_qty']) ? $this->data[$key]['optional_recurring_qty'] : 0;
|
||||
$total = $p->price * $qty;
|
||||
|
||||
if($qty == 0)
|
||||
return;
|
||||
|
||||
|
||||
$this->bundle->push([
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
'is_recurring' => true
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
if(isset($this->data[$key]['optional_qty']))
|
||||
{
|
||||
$p = $this->optional_products->first(function ($v,$k) use($key){
|
||||
return $k == $key;
|
||||
});
|
||||
|
||||
$qty = isset($this->data[$key]['optional_qty']) ? $this->data[$key]['optional_qty'] : 0;
|
||||
$total = $p->price * $qty;
|
||||
|
||||
if($qty == 0)
|
||||
return;
|
||||
|
||||
$this->bundle->push([
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
'is_recurring' => false
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->total = Number::formatMoney($this->bundle->sum('total'), $this->subscription->company);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function updatedData()
|
||||
{
|
||||
nlog('updated');
|
||||
nlog($this->data);
|
||||
$validatedData = $this->validate();
|
||||
nlog( $validatedData );
|
||||
}
|
||||
|
||||
public function updating($prop)
|
||||
{
|
||||
// $this->resetValidation($prop);
|
||||
// $this->resetErrorBag($prop);
|
||||
}
|
||||
|
||||
public function updated($propertyName)
|
||||
{
|
||||
nlog("validating {$propertyName}");
|
||||
$this->errors = $this->validateOnly($propertyName);
|
||||
$x = $this->validateOnly($propertyName, $this->rules(), [], $this->attributes());
|
||||
|
||||
nlog($this->errors);
|
||||
$validatedData = $this->validate();
|
||||
nlog( $validatedData );
|
||||
// // $validatedData = $this->validate();
|
||||
$this->buildBundle();
|
||||
|
||||
// $order_validator = Validator::make($this->all(), $this->rules(), [], $this->attributes());
|
||||
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'email' => ['required', 'email'],
|
||||
'data' => ['required', 'array'],
|
||||
'data.*.recurring_qty' => ['required', 'between:100,1000'],
|
||||
'data.*.optional_recurring_qty' => ['required', 'between:100,1000'],
|
||||
'data.*.optional_qty' => ['required', 'between:100,1000'],
|
||||
$rules = [
|
||||
'data.*.recurring_qty' => 'numeric|between:0,1000',
|
||||
'data.*.optional_recurring_qty' => 'numeric|between:0,1000',
|
||||
'data.*.optional_qty' => 'numeric|between:0,1000',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function attributes()
|
||||
{
|
||||
$attributes = [
|
||||
'data.*.recurring_qty' => 'recurring_qty',
|
||||
'data.*.optional_recurring_qty' => 'optional_recurring_qty',
|
||||
'data.*.optional_qty' => 'optional_qty',
|
||||
];
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user authentication
|
||||
*
|
||||
|
@ -7,10 +7,12 @@
|
||||
{{ $subscription->name }}
|
||||
</h1>
|
||||
</div>
|
||||
<form wire:submit.prevent="submit">
|
||||
|
||||
<!-- Recurring Plan Products-->
|
||||
<ul role="list" class="-my-6 divide-y divide-gray-200">
|
||||
@if(!empty($subscription->recurring_product_ids))
|
||||
@foreach($subscription->service()->recurring_products() as $index => $product)
|
||||
@foreach($recurring_products as $index => $product)
|
||||
<li class="flex py-6">
|
||||
@if(false)
|
||||
<div class="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200">
|
||||
@ -32,18 +34,11 @@
|
||||
<p class="text-gray-500 w-full"></p>
|
||||
<div class="flex place-content-end">
|
||||
<p class="text-sm font-light text-gray-700 text-right mr-2 mt-2">{{ ctrans('texts.qty') }}</p>
|
||||
<input wire:model="data.{{ $index }}.recurring_qty" type="text" class="w-1/4 rounded-md border-gray-300 shadow-sm sm:text-sm text-center" placeholder="0"/>
|
||||
<input wire:model.debounce.300ms="data.{{ $index }}.recurring_qty" type="text" class="w-1/4 rounded-md border-gray-300 shadow-sm sm:text-sm text-center" placeholder="0"/>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
{{ isset($data[$index]['recurring_qty']) ? $data[$index]['recurring_qty'] : 'merp' }}
|
||||
|
||||
@if($errors)
|
||||
@foreach($errors as $error)
|
||||
{{ $error }}
|
||||
@endforeach
|
||||
@endif
|
||||
@error('data.{{$index}}.recurring_qty')
|
||||
@error("data.{$index}.recurring_qty")
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||
<span class="block sm:inline">{{ $message }} </span>
|
||||
<span class="absolute top-0 bottom-0 right-0 px-4 py-3">
|
||||
@ -55,7 +50,7 @@
|
||||
@endif
|
||||
<!-- One Time Plan Products-->
|
||||
@if(!empty($subscription->product_ids))
|
||||
@foreach($subscription->service()->products() as $product)
|
||||
@foreach($products as $product)
|
||||
<li class="flex py-6">
|
||||
@if(false)
|
||||
<div class="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200">
|
||||
@ -93,7 +88,7 @@
|
||||
<!-- Optional Recurring Products-->
|
||||
<ul role="list" class="-my-6 divide-y divide-gray-200">
|
||||
@if(!empty($subscription->optional_recurring_product_ids))
|
||||
@foreach($subscription->service()->optional_recurring_products() as $index => $product)
|
||||
@foreach($optional_recurring_products as $index => $product)
|
||||
<li class="flex py-6">
|
||||
@if(false)
|
||||
<div class="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200 mr-2">
|
||||
@ -104,7 +99,7 @@
|
||||
<div>
|
||||
<div class="flex justify-between text-base font-medium text-gray-900">
|
||||
<h3>{!! nl2br($product->notes) !!}</h3>
|
||||
<p class="ml-0">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</p>
|
||||
<p class="ml-0">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }} </p>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500"></p>
|
||||
</div>
|
||||
@ -112,21 +107,22 @@
|
||||
<p class="text-gray-500 w-full"></p>
|
||||
<div class="flex place-content-end">
|
||||
<p class="text-sm font-light text-gray-700 text-right mr-2 mt-2">{{ ctrans('texts.qty') }}</p>
|
||||
<input wire:model="data.{{ $index }}.optional_recurring_qty" type="text" class="w-1/4 rounded-md border-gray-300 shadow-sm sm:text-sm text-center" placeholder="0"/>
|
||||
<input wire:model.debounce.300ms="data.{{ $index }}.optional_recurring_qty" type="text" class="w-1/4 rounded-md border-gray-300 shadow-sm sm:text-sm text-center" placeholder="0"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@error("data.{$index}.optional_recurring_qty")
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||
<span class="block sm:inline">{{ isset($data[$index]['optional_recurring_qty']) ? $data[$index]['optional_recurring_qty'] : '' }}</span>
|
||||
<span class="block sm:inline">{{ $message }} </span>
|
||||
<span class="absolute top-0 bottom-0 right-0 px-4 py-3">
|
||||
</div>
|
||||
|
||||
@enderror
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
@endif
|
||||
@if(!empty($subscription->optional_product_ids))
|
||||
@foreach($subscription->service()->optional_products() as $index => $product)
|
||||
@foreach($optional_products as $index => $product)
|
||||
<li class="flex py-6">
|
||||
@if(false)
|
||||
<div class="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200 mr-2">
|
||||
@ -145,13 +141,15 @@
|
||||
<p class="text-gray-500 w-full"></p>
|
||||
<div class="flex place-content-end">
|
||||
<p class="text-sm font-light text-gray-700 text-right mr-2 mt-2">{{ ctrans('texts.qty') }}</p>
|
||||
<input type="text" wire:model="data.{{ $index }}.optional_qty" class="w-1/4 rounded-md border-gray-300 shadow-sm sm:text-sm text-center" placeholder="0">
|
||||
<input type="text" wire:model.debounce.300ms="data.{{ $index }}.optional_qty" class="w-1/4 rounded-md border-gray-300 shadow-sm sm:text-sm text-center" placeholder="0">
|
||||
</div>
|
||||
</div>
|
||||
@error("data.{$index}.optional_qty")
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||
<span class="block sm:inline">{{ isset($data[$index]['optional_qty']) ? $data[$index]['optional_qty'] : '' }}</span>
|
||||
<span class="block sm:inline">{{ $message }} </span>
|
||||
<span class="absolute top-0 bottom-0 right-0 px-4 py-3">
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
@ -160,27 +158,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="col-span-4 bg-blue-500 flex flex-col item-center p-2 h-screen">
|
||||
<div class="w-full p-4">
|
||||
<div id="summary" class="px-4 text-white">
|
||||
<h1 class="font-semibold text-2xl border-b-2 border-gray-200 border-opacity-50 pb-2 text-white">{{ ctrans('texts.order') }}</h1>
|
||||
|
||||
@foreach($subscription->service()->recurring_products() as $product)
|
||||
<div class="flex justify-between mt-1 mb-1">
|
||||
<span class="font-light text-sm uppercase">{!! nl2br(substr($product->notes, 0, 50)) !!}</span>
|
||||
<span class="font-semibold text-sm">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span>
|
||||
</div>
|
||||
@foreach($bundle as $item)
|
||||
<div class="flex justify-between mt-1 mb-1">
|
||||
<span class="font-light text-sm uppercase">{{$item['product']}} x {{$item['qty']}}</span>
|
||||
<span class="font-semibold text-sm">{{ $item['price'] }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@foreach($subscription->service()->products() as $product)
|
||||
<div class="flex justify-between mt-1 mb-1">
|
||||
<span class="font-light text-sm uppercase">{!! nl2br(substr($product->notes, 0, 50)) !!}</span>
|
||||
<span class="font-semibold text-sm">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
|
||||
@if(!empty($subscription->promo_code) && !$subscription->trial_enabled)
|
||||
<form wire:submit.prevent="handleCoupon" class="">
|
||||
@csrf
|
||||
@ -190,7 +181,7 @@
|
||||
<div class="relative flex flex-grow items-stretch focus-within:z-10">
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
</div>
|
||||
<input type="text" wire:model.defer="coupon" class="block w-full rounded-none rounded-l-md border-gray-300 pl-10 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" placeholder="">
|
||||
<input type="text" wire:model.debounce.300ms="coupon" class="block w-full rounded-none rounded-l-md border-gray-300 pl-10 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" placeholder="">
|
||||
</div>
|
||||
<button type="button" class="relative -ml-px inline-flex items-center space-x-2 rounded-r-md border border-gray-300 bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
||||
|
||||
@ -204,7 +195,7 @@
|
||||
<div class="border-t-2 border-gray-200 border-opacity-50 mt-8">
|
||||
<div class="flex font-semibold justify-between py-6 text-sm uppercase">
|
||||
<span>{{ ctrans('texts.total') }}</span>
|
||||
<span>{{ \App\Utils\Number::formatMoney($price, $subscription->company) }}</span>
|
||||
<span>{{ $total }}</span>
|
||||
</div>
|
||||
<button class="bg-white font-semibold hover:bg-gray-600 py-3 text-sm text-blue-500 uppercase w-full">Checkout</button>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user