mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 13:12:50 +01:00
Merge branch 'v5-develop' into v5-develop
This commit is contained in:
commit
a386a33a7e
10
CHANGELOG.md
10
CHANGELOG.md
@ -3,12 +3,22 @@
|
||||
## [Unreleased (daily channel)](https://github.com/invoiceninja/invoiceninja/tree/v5-develop)
|
||||
|
||||
### Added:
|
||||
- Subscriptions are now going to show the frequency in the table (#5412)
|
||||
- Subscriptions: During upgrade webhook request message will be shown for easier debugging (#5411)
|
||||
- PDF: Custom fields now will be shared across invoices, quotes & credits (#5410)
|
||||
- Client portal: Invoices are now sorted in the descending order (#5408)
|
||||
- Payments: ACH notification after the initial bank account connecting process (#5405)
|
||||
|
||||
### Fixed:
|
||||
- Fixes for counters where patterns without {$counter} could causes endless recursion.
|
||||
- Fixes for surcharge tax displayed amount on PDF.
|
||||
- Fixes for custom designs not rendering the custom template
|
||||
- Fixes for missing bulk actions on Subscriptions
|
||||
- Fixes CSS padding on the show page for recurring invoices (#5412)
|
||||
- Fixes for rendering invalid HTML & parsing invalid XML (#5395)
|
||||
|
||||
### Removed:
|
||||
- Removed one-time payments table (#5412)
|
||||
|
||||
## v5.1.43
|
||||
|
||||
|
@ -89,29 +89,6 @@ class PaymentMethodController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return void
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return void
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function verify(ClientGatewayToken $payment_method)
|
||||
{
|
||||
$gateway = $this->getClientGateway();
|
||||
|
@ -111,6 +111,7 @@ class BillingPortalPurchase extends Component
|
||||
'discount_applied' => false,
|
||||
'show_loading_bar' => false,
|
||||
'not_eligible' => null,
|
||||
'not_eligible_message' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -327,6 +328,7 @@ class BillingPortalPurchase extends Component
|
||||
|
||||
if (is_array($is_eligible)) {
|
||||
$this->steps['not_eligible'] = true;
|
||||
$this->steps['not_eligible_message'] = $is_eligible['exception'];
|
||||
$this->steps['show_loading_bar'] = false;
|
||||
|
||||
return;
|
||||
|
@ -26,6 +26,11 @@ class InvoicesTable extends Component
|
||||
|
||||
public $status = [];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->sort_asc = false;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$local_status = [];
|
||||
@ -62,6 +67,7 @@ class InvoicesTable extends Component
|
||||
$query = $query
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('status_id', '<>', Invoice::STATUS_DRAFT)
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.invoices-table', [
|
||||
|
@ -1,40 +0,0 @@
|
||||
<?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\Invoice;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
class SubscriptionInvoicesTable extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
use WithSorting;
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Invoice::query()
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->whereNotNull('subscription_id')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->where('balance', '=', 0)
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.subscriptions-invoices-table', [
|
||||
'invoices' => $query,
|
||||
]);
|
||||
}
|
||||
}
|
43
app/Mail/Gateways/ACHVerificationNotification.php
Normal file
43
app/Mail/Gateways/ACHVerificationNotification.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?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\Mail\Gateways;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ACHVerificationNotification extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->view('email.gateways.ach-verification-notification');
|
||||
}
|
||||
}
|
@ -14,8 +14,11 @@ namespace App\PaymentDrivers\Stripe;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Mail\Gateways\ACHVerificationNotification;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
@ -47,7 +50,7 @@ class ACH
|
||||
return render('gateways.stripe.ach.authorize', array_merge($data));
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
public function authorizeResponse(Request $request)
|
||||
{
|
||||
$this->stripe->init();
|
||||
|
||||
@ -63,6 +66,14 @@ class ACH
|
||||
|
||||
$client_gateway_token = $this->storePaymentMethod($source, $request->input('method'), $customer);
|
||||
|
||||
$mailer = new NinjaMailerObject();
|
||||
$mailer->mailable = new ACHVerificationNotification();
|
||||
$mailer->company = auth('contact')->user()->client->company;
|
||||
$mailer->settings = auth('contact')->user()->client->company->settings;
|
||||
$mailer->to_user = auth('contact')->user();
|
||||
|
||||
NinjaMailerJob::dispatchNow($mailer);
|
||||
|
||||
return redirect()->route('client.payment_methods.verification', ['payment_method' => $client_gateway_token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
|
||||
}
|
||||
|
||||
|
@ -209,8 +209,14 @@ trait DesignHelpers
|
||||
|
||||
$javascript = 'document.querySelectorAll("tbody > tr > td").forEach(t=>{if(""!==t.innerText){let e=t.getAttribute("data-ref").slice(0,-3);document.querySelector(`th[data-ref="${e}-th"]`).removeAttribute("hidden")}}),document.querySelectorAll("tbody > tr > td").forEach(t=>{let e=t.getAttribute("data-ref").slice(0,-3);(e=document.querySelector(`th[data-ref="${e}-th"]`)).hasAttribute("hidden")&&""==t.innerText&&t.setAttribute("hidden","true")});';
|
||||
|
||||
// Previously we've been decoding the HTML on the backend and XML parsing isn't good options because it requires,
|
||||
// strict & valid HTML to even output/decode. Decoding is now done on the frontend with this piece of Javascript.
|
||||
|
||||
$html_decode = 'document.querySelectorAll(`[data-state="encoded-html"]`).forEach((element) => element.innerHTML = element.innerText)';
|
||||
|
||||
return ['element' => 'div', 'elements' => [
|
||||
['element' => 'script', 'content' => $javascript],
|
||||
['element' => 'script', 'content' => $html_decode],
|
||||
]];
|
||||
}
|
||||
|
||||
|
@ -130,19 +130,14 @@ trait PdfMakerUtilities
|
||||
}
|
||||
|
||||
if ($contains_html) {
|
||||
// Support for injecting direct HTML into elements.
|
||||
// Example: Without documentFragment(): <b>Hello!</b> will result: <b>Hello!</b>
|
||||
// With document fragment we can evaluate HTML directly.
|
||||
// If the element contains the HTML, we gonna display it as is. DOMDocument, is going to
|
||||
// encode it for us, preventing any errors on the backend due processing stage.
|
||||
// Later, we decode this using Javascript so it looks like it's normal HTML being injected.
|
||||
// To get all elements that need frontend decoding, we use 'data-ref' property.
|
||||
|
||||
$_child = $this->document->createElement($child['element'], '');
|
||||
|
||||
$fragment = $this->document->createDocumentFragment();
|
||||
|
||||
$fragment->appendXML(
|
||||
strtr($child['content'], ['&' => '&'])
|
||||
);
|
||||
|
||||
$_child->appendChild($fragment);
|
||||
$_child->setAttribute('data-state', 'encoded-html');
|
||||
$_child->nodeValue = htmlspecialchars($child['content']);
|
||||
} else {
|
||||
// .. in case string doesn't contain any HTML, we'll just return
|
||||
// raw $content.
|
||||
|
@ -214,6 +214,10 @@ class HtmlEngine
|
||||
$data['$quote.valid_until'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->entity->client->locale()), 'label' => ctrans('texts.valid_until')];
|
||||
$data['$credit_amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_amount')];
|
||||
$data['$credit_balance'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
|
||||
$data['$quote.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
|
||||
$data['$quote.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
|
||||
$data['$quote.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
|
||||
$data['$quote.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
|
||||
|
||||
$data['$credit_number'] = &$data['$number'];
|
||||
$data['$credit_no'] = &$data['$number'];
|
||||
|
@ -2138,6 +2138,35 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
boardview
|
||||
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2020, Jacob Bonk
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
boolean_selector
|
||||
build
|
||||
@ -6282,6 +6311,31 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------
|
||||
draggable_scrollbar
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 Draggable Scrollbar Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
extended_image
|
||||
|
||||
|
4
public/flutter_service_worker.js
vendored
4
public/flutter_service_worker.js
vendored
@ -10,7 +10,7 @@ const RESOURCES = {
|
||||
"/": "23224b5e03519aaa87594403d54412cf",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
"assets/NOTICES": "e80e999afd09f0f14597c78d582d9c7c",
|
||||
"assets/NOTICES": "3bf0be7e0e4deca198e5f5c4800f9232",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "1288c9e28052e028aba623321f7826ac",
|
||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||
"assets/assets/images/google-icon.png": "0f118259ce403274f407f5e982e681c3",
|
||||
@ -31,7 +31,7 @@ const RESOURCES = {
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"main.dart.js": "7c03ff039c186c66a6f79f7e6d5e817c",
|
||||
"main.dart.js": "589fe812bacbb3ceb0a5e0714f69a8cd",
|
||||
"version.json": "e021a7a1750aa3e7d1d89b51ac9837e9"
|
||||
};
|
||||
|
||||
|
252175
public/main.dart.js
vendored
252175
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
251448
public/main.foss.dart.js
vendored
251448
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
248079
public/main.wasm.dart.js
vendored
248079
public/main.wasm.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -0,0 +1,14 @@
|
||||
@component('email.template.master', ['design' => 'light'])
|
||||
@slot('header')
|
||||
@include('email.components.header', ['logo' => 'https://www.invoiceninja.com/wp-content/uploads/2015/10/logo-white-horizontal-1.png'])
|
||||
@endslot
|
||||
|
||||
<p>Hello,</p>
|
||||
|
||||
<p>Connecting bank accounts require verification. Stripe will automatically sends two
|
||||
small deposits for this purpose. These deposits take 1-2 business days to appear on the customer's online
|
||||
statement.
|
||||
</p>
|
||||
|
||||
<p>Thank you!</p>
|
||||
@endcomponent
|
@ -59,10 +59,12 @@
|
||||
|
||||
<div class="relative flex justify-center text-sm leading-5">
|
||||
<h1 class="text-2xl font-bold tracking-wide bg-gray-50 px-6 py-0">
|
||||
{{ ctrans('texts.total') }}: {{ \App\Utils\Number::formatMoney($price, $subscription->company) }}
|
||||
{{ ctrans('texts.total') }}
|
||||
: {{ \App\Utils\Number::formatMoney($price, $subscription->company) }}
|
||||
|
||||
@if($steps['discount_applied'])
|
||||
<small class="ml-1 line-through text-gray-500">{{ \App\Utils\Number::formatMoney($subscription->price, $subscription->company) }}</small>
|
||||
<small
|
||||
class="ml-1 line-through text-gray-500">{{ \App\Utils\Number::formatMoney($subscription->price, $subscription->company) }}</small>
|
||||
@endif
|
||||
</h1>
|
||||
</div>
|
||||
@ -208,6 +210,10 @@
|
||||
|
||||
@if($steps['not_eligible'] && !is_null($steps['not_eligible']))
|
||||
<h1>{{ ctrans('texts.payment_error') }}</h1>
|
||||
|
||||
@if($steps['not_eligible_message'])
|
||||
<small class="mt-4 block">{{ $steps['not_eligible_message'] }}</small>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,83 +0,0 @@
|
||||
<div>
|
||||
<p class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary text-white">
|
||||
One-time payments
|
||||
</p>
|
||||
|
||||
<div class="flex items-center justify-between mt-4">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-2 text-sm hidden md:block">{{ ctrans('texts.per_page') }}</span>
|
||||
<select wire:model="per_page" class="form-select py-1 text-sm">
|
||||
<option>5</option>
|
||||
<option selected>10</option>
|
||||
<option>15</option>
|
||||
<option>20</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
|
||||
<div class="align-middle inline-block min-w-full overflow-hidden rounded">
|
||||
<table class="min-w-full shadow rounded border border-gray-200 mt-4 credits-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-primary">
|
||||
<p role="button" wire:click="sortBy('number')" class="cursor-pointer">
|
||||
{{ ctrans('texts.subscription') }}
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-primary">
|
||||
<p role="button" wire:click="sortBy('number')" class="cursor-pointer">
|
||||
{{ ctrans('texts.invoice') }}
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
|
||||
<p role="button" wire:click="sortBy('amount')" class="cursor-pointer">
|
||||
{{ ctrans('texts.amount') }}
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-6 py-3 border-b border-gray-200 bg-primary text-left text-xs leading-4 font-medium text-white uppercase tracking-wider">
|
||||
<p role="button" wire:click="sortBy('date')" class="cursor-pointer">
|
||||
{{ ctrans('texts.date') }}
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($invoices as $invoice)
|
||||
<tr class="bg-white group hover:bg-gray-100">
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ $invoice->subscription->name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
<a href="{{ route('client.invoice.show', $invoice->hashed_id) }}"
|
||||
class="button-link text-primary">
|
||||
{{ $invoice->number }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ App\Utils\Number::formatMoney($invoice->amount, $invoice->client) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ $invoice->formatDate($invoice->date, $invoice->client->date_format()) }}
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr class="bg-white group hover:bg-gray-100">
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500" colspan="100%">
|
||||
{{ ctrans('texts.no_results') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center md:justify-between mt-6 mb-6">
|
||||
@if($invoices->total() > 0)
|
||||
<span class="text-gray-700 text-sm hidden md:block">
|
||||
{{ ctrans('texts.showing_x_of', ['first' => $invoices->firstItem(), 'last' => $invoices->lastItem(), 'total' => $invoices->total()]) }}
|
||||
</span>
|
||||
@endif
|
||||
{{ $invoices->links('portal/ninja2020/vendor/pagination') }}
|
||||
</div>
|
||||
</div>
|
@ -1,9 +1,5 @@
|
||||
<div>
|
||||
<p class="mb-4 uppercase leading-4 tracking-wide inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary text-white">
|
||||
Subscriptions
|
||||
</p>
|
||||
|
||||
<div class="flex items-center justify-between mt-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-2 text-sm hidden md:block">{{ ctrans('texts.per_page') }}</span>
|
||||
<select wire:model="per_page" class="form-select py-1 text-sm">
|
||||
@ -24,6 +20,11 @@
|
||||
{{ ctrans('texts.subscription') }}
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-primary">
|
||||
<p role="button" wire:click="sortBy('number')" class="cursor-pointer">
|
||||
{{ ctrans('texts.frequency') }}
|
||||
</p>
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-white uppercase border-b border-gray-200 bg-primary">
|
||||
<p role="button" wire:click="sortBy('number')" class="cursor-pointer">
|
||||
{{ ctrans('texts.invoice') }}
|
||||
@ -47,6 +48,9 @@
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ $recurring_invoice->subscription->name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
{{ \App\Models\RecurringInvoice::frequencyForKey($recurring_invoice->frequency_id) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
|
||||
<a href="{{ route('client.recurring_invoice.show', $recurring_invoice->hashed_id) }}"
|
||||
class="button-link text-primary">
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
@push('head')
|
||||
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
|
||||
@endpush
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
@push('head')
|
||||
<meta name="pdf-url" content="{{ $invoice->pdf_file_path() }}">
|
||||
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
|
||||
@endpush
|
||||
|
@ -60,7 +60,7 @@
|
||||
</div>
|
||||
|
||||
@if(is_null($invoice->subscription_id) || optional($invoice->subscription)->allow_cancellation)
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4 mt-4">
|
||||
<div class="bg-white shadow sm:rounded-lg mt-4">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div>
|
||||
@ -86,11 +86,11 @@
|
||||
@endif
|
||||
|
||||
@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 mt-4">
|
||||
<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>
|
||||
|
||||
<div class="flex mt-4">
|
||||
<div class="flex mt-4 space-x-2">
|
||||
@foreach($invoice->subscription->service()->getPlans() as $subscription)
|
||||
<a href="{{ route('client.subscription.plan_switch', ['recurring_invoice' => $invoice->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
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('subscription-invoices-table')
|
||||
@livewire('subscription-recurring-invoices-table')
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -54,7 +54,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence
|
||||
Route::get('payment_methods/{payment_method}/verification', 'ClientPortal\PaymentMethodController@verify')->name('payment_methods.verification');
|
||||
Route::post('payment_methods/{payment_method}/verification', 'ClientPortal\PaymentMethodController@processVerification');
|
||||
|
||||
Route::resource('payment_methods', 'ClientPortal\PaymentMethodController'); // name = (payment_methods. index / create / show / update / destroy / edit
|
||||
Route::resource('payment_methods', 'ClientPortal\PaymentMethodController')->except(['edit', 'update']); // name = (payment_methods. index / create / show / update / destroy / edit
|
||||
|
||||
Route::match(['GET', 'POST'], 'quotes/approve', 'ClientPortal\QuoteController@bulk')->name('quotes.bulk');
|
||||
Route::get('quotes', 'ClientPortal\QuoteController@index')->name('quotes.index')->middleware('portal_enabled');
|
||||
|
Loading…
Reference in New Issue
Block a user