1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-17 16:42:48 +01:00

Merge pull request #5389 from turbo124/v5-stable

v5.1.42
This commit is contained in:
David Bomba 2021-04-09 08:00:05 +10:00 committed by GitHub
commit 94e9610c51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 170564 additions and 169702 deletions

View File

@ -1 +1 @@
5.1.41 5.1.42

View File

@ -24,14 +24,14 @@ class SubscriptionPlanSwitchController extends Controller
* *
* @param ShowPlanSwitchRequest $request * @param ShowPlanSwitchRequest $request
* @param Subscription $subscription * @param Subscription $subscription
* @param string $target_subscription * @param string $target
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/ */
public function index(ShowPlanSwitchRequest $request, Subscription $subscription, Subscription $target_subscription) public function index(ShowPlanSwitchRequest $request, Subscription $subscription, Subscription $target)
{ {
return render('subscriptions.switch', [ return render('subscriptions.switch', [
'subscription' => $subscription, 'subscription' => $subscription,
'target_subscription' => $target_subscription, 'target' => $target,
]); ]);
} }
} }

View File

@ -16,6 +16,7 @@ use Cz\Git\GitException;
use Cz\Git\GitRepository; use Cz\Git\GitRepository;
use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Artisan;
class SelfUpdateController extends BaseController class SelfUpdateController extends BaseController
{ {
@ -69,8 +70,21 @@ class SelfUpdateController extends BaseController
$output = ''; $output = '';
try { try {
// $res = $repo->pull();
$cacheCompiled = base_path('bootstrap/cache/compiled.php');
if (file_exists($cacheCompiled)) { unlink ($cacheCompiled); }
$cacheServices = base_path('bootstrap/cache/services.php');
if (file_exists($cacheServices)) { unlink ($cacheServices); }
Artisan::call('clear-compiled');
Artisan::call('cache:clear');
Artisan::call('debugbar:clear');
Artisan::call('route:clear');
Artisan::call('view:clear');
Artisan::call('config:clear');
$output = $repo->execute('stash');
$output = $repo->execute('reset hard origin/v5-stable');
$output = $repo->execute('pull origin'); $output = $repo->execute('pull origin');
} catch (GitException $e) { } catch (GitException $e) {

View File

@ -361,6 +361,7 @@ class WebhookController extends BaseController
$webhook->user_id = auth()->user()->id; $webhook->user_id = auth()->user()->id;
$webhook->event_id = $event_id; $webhook->event_id = $event_id;
$webhook->target_url = $target_url; $webhook->target_url = $target_url;
$webhook->fill($request->all());
$webhook->save(); $webhook->save();
if (! $webhook->id) { if (! $webhook->id) {

View File

@ -0,0 +1,38 @@
<?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 App\Http\Livewire;
use App\Models\RecurringInvoice;
use Livewire\Component;
class RecurringInvoiceCancellation extends Component
{
/**
* @var RecurringInvoice
*/
public $invoice;
public function processCancellation()
{
if ($this->invoice->subscription) {
return $this->invoice->subscription->service()->handleCancellation();
}
return redirect()->route('client.recurring_invoices.request_cancellation', ['recurring_invoice' => $this->invoice->hashed_id]);
}
public function render()
{
return render('components.livewire.recurring-invoice-cancellation');
}
}

View File

@ -12,30 +12,90 @@
namespace App\Http\Livewire; namespace App\Http\Livewire;
use App\Models\ClientContact;
use App\Models\Subscription;
use Illuminate\Support\Str;
use Livewire\Component; use Livewire\Component;
class SubscriptionPlanSwitch extends Component class SubscriptionPlanSwitch extends Component
{ {
/**
* @var Subscription
*/
public $subscription; public $subscription;
public $target_subscription; /**
* @var Subscription
*/
public $target;
/**
* @var ClientContact
*/
public $contact; public $contact;
/**
* @var array
*/
public $methods = []; public $methods = [];
/**
* @var string
*/
public $total; public $total;
/**
* @var array
*/
public $state = [
'payment_initialised' => false,
'show_loading_bar' => false,
'invoice' => null,
'company_gateway_id' => null,
'payment_method_id' => null,
];
/**
* @var mixed|string
*/
public $hash;
public function mount() public function mount()
{ {
$this->total = $this->subscription->service()->getPriceBetweenSubscriptions($this->subscription, $this->target);
$this->methods = $this->contact->client->service()->getPaymentMethods(100); $this->methods = $this->contact->client->service()->getPaymentMethods(100);
$this->total = 1; $this->hash = Str::uuid()->toString();
} }
public function handleBeforePaymentEvents() public function handleBeforePaymentEvents(): void
{ {
// .. $this->state['show_loading_bar'] = true;
$this->state['invoice'] = $this->subscription->service()->createChangePlanInvoice([
'subscription' => $this->subscription,
'target' => $this->target,
]);
$this->state['payment_initialised'] = true;
$this->emit('beforePaymentEventsCompleted');
}
/**
* Middle method between selecting payment method &
* submitting the from to the backend.
*
* @param $company_gateway_id
* @param $gateway_type_id
*/
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
{
$this->state['company_gateway_id'] = $company_gateway_id;
$this->state['payment_method_id'] = $gateway_type_id;
$this->handleBeforePaymentEvents();
} }
public function render() public function render()

View File

@ -184,7 +184,7 @@ class SubscriptionService
public function createChangePlanInvoice($data) public function createChangePlanInvoice($data)
{ {
return Invoice::where('status_id', Invoice::STATUS_SENT)->first();
} }
public function createInvoice($data): ?\App\Models\Invoice public function createInvoice($data): ?\App\Models\Invoice
@ -275,9 +275,9 @@ class SubscriptionService
} }
/** /**
* Get the single charge products for the * Get the single charge products for the
* subscription * subscription
* *
* @return ?Product Collection * @return ?Product Collection
*/ */
public function products() public function products()
@ -286,9 +286,9 @@ class SubscriptionService
} }
/** /**
* Get the recurring products for the * Get the recurring products for the
* subscription * subscription
* *
* @return ?Product Collection * @return ?Product Collection
*/ */
public function recurring_products() public function recurring_products()
@ -317,6 +317,21 @@ class SubscriptionService
public function handleCancellation() public function handleCancellation()
{ {
dd('Cancelling using SubscriptionService');
// .. // ..
} }
/**
* Get pro rata calculation between subscriptions.
*
* @param Subscription $current
* @param Subscription $target
*/
public function getPriceBetweenSubscriptions(Subscription $current, Subscription $target): int
{
// Calculate the pro rata. Return negative value if credits needed.
return 1;
}
} }

View File

@ -370,6 +370,9 @@ class HtmlEngine
//$data['$entity_footer'] = ['value' => $this->client->getSetting("{$this->entity_string}_footer"), 'label' => '']; //$data['$entity_footer'] = ['value' => $this->client->getSetting("{$this->entity_string}_footer"), 'label' => ''];
$data['$entity_footer'] = ['value' => $this->entity->footer, 'label' => '']; $data['$entity_footer'] = ['value' => $this->entity->footer, 'label' => ''];
$data['$page_size'] = ['value' => $this->settings->page_size, 'label' => ''];
$data['$page_layout'] = ['value' => $this->settings->page_layout, 'label' => ''];
$arrKeysLength = array_map('strlen', array_keys($data)); $arrKeysLength = array_map('strlen', array_keys($data));
array_multisort($arrKeysLength, SORT_DESC, $data); array_multisort($arrKeysLength, SORT_DESC, $data);

View File

@ -14,7 +14,7 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', ''), 'app_domain' => env('APP_DOMAIN', ''),
'app_version' => '5.1.41', 'app_version' => '5.1.42',
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false), 'api_secret' => env('API_SECRET', false),

View File

@ -31,7 +31,7 @@ const RESOURCES = {
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1", "assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08", "assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"main.dart.js": "ea0889d75bdaf28b60c0837c9d31e2ff", "main.dart.js": "7c03ff039c186c66a6f79f7e6d5e817c",
"version.json": "e021a7a1750aa3e7d1d89b51ac9837e9" "version.json": "e021a7a1750aa3e7d1d89b51ac9837e9"
}; };

113190
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

111780
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

115034
public/main.wasm.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,7 @@
@page { @page {
margin: -0.25cm !important; margin: -0.25cm !important;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: $global_margin; margin: $global_margin;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: $global_margin; margin: $global_margin;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: $global_margin; margin: $global_margin;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: $global_margin; margin: $global_margin;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: $global_margin; margin: $global_margin;
size: $page_size $page_layout;
} }
p { p {

View File

@ -16,6 +16,7 @@
@page { @page {
margin: -0.22cm; margin: -0.22cm;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: $global_margin; margin: $global_margin;
size: $page_size $page_layout;
} }
p { p {

View File

@ -14,6 +14,7 @@
@page { @page {
margin: -0.25cm !important; margin: -0.25cm !important;
size: $page_size $page_layout;
} }
p { p {

View File

@ -41,7 +41,7 @@
@foreach($subscription->service()->recurring_products() as $product) @foreach($subscription->service()->recurring_products() as $product)
<div class="flex items-center justify-between mb-4 bg-white rounded px-6 py-4 shadow-sm border"> <div class="flex items-center justify-between mb-4 bg-white rounded px-6 py-4 shadow-sm border">
<div class="text-sm">{{ $product->product_key }}</div> <div class="text-sm">{{ $product->notes }}</div>
<div data-ref="price-and-quantity-container"> <div data-ref="price-and-quantity-container">
<span <span
data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span> data-ref="price">{{ \App\Utils\Number::formatMoney($product->price, $subscription->company) }}</span>
@ -136,9 +136,6 @@
@elseif($steps['show_start_trial']) @elseif($steps['show_start_trial'])
<form wire:submit.prevent="handleTrial" class="mt-8"> <form wire:submit.prevent="handleTrial" class="mt-8">
@csrf @csrf
<p class="mb-4">Some text about the trial goes here. Details about the days, etc.</p>
<button class="px-3 py-2 border rounded mr-4 hover:border-blue-600"> <button class="px-3 py-2 border rounded mr-4 hover:border-blue-600">
{{ ctrans('texts.trial_call_to_action') }} {{ ctrans('texts.trial_call_to_action') }}
</button> </button>

View File

@ -0,0 +1,3 @@
<button wire:click="processCancellation" class="button button-danger button-block">
{{ ctrans('texts.confirm') }}
</button>

View File

@ -7,24 +7,58 @@
</div> </div>
<div class="relative flex justify-center text-sm leading-5"> <div class="relative flex justify-center text-sm leading-5">
<h1 class="text-2xl font-bold tracking-wide bg-gray-100 px-6 py-0"> <span class="font-bold tracking-wide bg-gray-100 px-6 py-0">Select a payment method:</span>
{{ ctrans('texts.total') }}: {{ \App\Utils\Number::formatMoney($total, $subscription->company) }} {{-- <h1 class="text-2xl font-bold tracking-wide bg-gray-100 px-6 py-0">--}}
{{-- <small class="ml-1 line-through text-gray-500">{{ \App\Utils\Number::formatMoney($subscription->price, $subscription->company) }}</small>--}} {{-- {{ ctrans('texts.total') }}: {{ \App\Utils\Number::formatMoney($total, $subscription->company) }}--}}
</h1> {{-- <small class="ml-1 line-through text-gray-500">{{ \App\Utils\Number::formatMoney($subscription->price, $subscription->company) }}</small>--}}
{{-- </h1>--}}
</div> </div>
</div> </div>
<!-- Payment methods --> @if($state['invoice'])
<form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}"
method="post" id="payment-method-form">
@csrf
@if($state['invoice'] instanceof \App\Models\Invoice)
<input type="hidden" name="invoices[]" value="{{ $state['invoice']->hashed_id }}">
<input type="hidden" name="payable_invoices[0][amount]"
value="{{ $state['invoice']->partial > 0 ? \App\Utils\Number::formatValue($state['invoice']->partial, $state['invoice']->client->currency()) : \App\Utils\Number::formatValue($state['invoice']->balance, $state['invoice']->client->currency()) }}">
<input type="hidden" name="payable_invoices[0][invoice_id]"
value="{{ $state['invoice']->hashed_id }}">
@endif
<input type="hidden" name="action" value="payment">
<input type="hidden" name="company_gateway_id" value="{{ $state['company_gateway_id'] }}"/>
<input type="hidden" name="payment_method_id" value="{{ $state['payment_method_id'] }}"/>
</form>
@endif
<!-- Payment methods -->
<div class="mt-8 flex flex-col items-center"> <div class="mt-8 flex flex-col items-center">
<small class="block mb-4">Select a payment method:</small>
<div> <div>
@foreach($this->methods as $method)
<button @if(!$state['payment_initialised'])
{{-- wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}')"--}} @foreach($this->methods as $method)
class="px-3 py-2 border bg-white rounded mr-4 hover:border-blue-600"> <button
{{ $method['label'] }} wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}')"
</button> class="px-3 py-2 border bg-white rounded mr-4 hover:border-blue-600">
@endforeach {{ $method['label'] }}
</button>
@endforeach
@endif
@if($state['show_loading_bar'])
<div class="flex justify-center">
<svg class="animate-spin h-8 w-8 text-primary" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
@endif
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
<div x-show="open" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" style="display:none;"> <div x-show="open" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center"
style="display:none;">
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" <div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
@ -33,10 +34,7 @@
</div> </div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto"> <div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<a href="{{ route('client.recurring_invoices.request_cancellation',['recurring_invoice' => $invoice->hashed_id]) }}" @livewire('recurring-invoice-cancellation', ['invoice' => $invoice])
class="button button-danger button-block">
{{ ctrans('texts.confirm') }}
</a>
</div> </div>
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto"> <div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
<button @click="open = false" type="button" class="button button-secondary button-block"> <button @click="open = false" type="button" class="button button-secondary button-block">

View File

@ -85,14 +85,14 @@
</div> </div>
@endif @endif
@if($invoice->subscription->allow_plan_changes) @if($invoice->subscription && $invoice->subscription->allow_plan_changes)
<div class="bg-white shadow overflow-hidden px-4 py-5 lg:rounded-lg"> <div class="bg-white shadow overflow-hidden px-4 py-5 lg:rounded-lg">
<h3 class="text-lg leading-6 font-medium text-gray-900">Switch Plans:</h3> <h3 class="text-lg leading-6 font-medium text-gray-900">Switch Plans:</h3>
<p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">Upgrade or downgrade your current plan.</p> <p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">Upgrade or downgrade your current plan.</p>
<div class="flex mt-4"> <div class="flex mt-4">
@foreach($invoice->subscription->service()->getPlans() as $subscription) @foreach($invoice->subscription->service()->getPlans() as $subscription)
<a href="{{ route('client.subscription.plan_switch', ['subscription' => $invoice->subscription->hashed_id, 'target_subscription' => $subscription->hashed_id]) }}" class="border rounded px-5 py-2 hover:border-gray-800 text-sm cursor-pointer">{{ $subscription->name }}</a> <a href="{{ route('client.subscription.plan_switch', ['subscription' => $invoice->subscription->hashed_id, 'target' => $subscription->hashed_id]) }}" class="border rounded px-5 py-2 hover:border-gray-800 text-sm cursor-pointer">{{ $subscription->name }}</a>
@endforeach @endforeach
</div> </div>
</div> </div>

View File

@ -42,6 +42,12 @@
</div> </div>
<!-- Payment box --> <!-- Payment box -->
@livewire('subscription-plan-switch', compact('subscription', 'target_subscription', 'contact')) @livewire('subscription-plan-switch', compact('subscription', 'target', 'contact'))
</div> </div>
@endsection @endsection
@push('footer')
<script>
Livewire.on('beforePaymentEventsCompleted', () => document.getElementById('payment-method-form').submit());
</script>
@endpush

View File

@ -72,7 +72,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence
Route::get('documents/{document}/download', 'ClientPortal\DocumentController@download')->name('documents.download'); Route::get('documents/{document}/download', 'ClientPortal\DocumentController@download')->name('documents.download');
Route::resource('documents', 'ClientPortal\DocumentController')->only(['index', 'show']); Route::resource('documents', 'ClientPortal\DocumentController')->only(['index', 'show']);
Route::get('subscriptions/{subscription}/plan_switch/{target_subscription}', 'ClientPortal\SubscriptionPlanSwitchController@index')->name('subscription.plan_switch'); Route::get('subscriptions/{subscription}/plan_switch/{target}', 'ClientPortal\SubscriptionPlanSwitchController@index')->name('subscription.plan_switch');
Route::resource('subscriptions', 'ClientPortal\SubscriptionController')->only(['index']); Route::resource('subscriptions', 'ClientPortal\SubscriptionController')->only(['index']);