mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-05 18:52:44 +01:00
Client portal improvements & bug fixes (#3678)
* fix turbolink 404 * fix checkbox when adding card using stripe * Fix hashed_ids problem when using PayPal * php-cs-fixer * Bump tailwind & purgecss * fix auth scope * scope to auth contact company
This commit is contained in:
parent
7f9abbf96b
commit
2704bf2f82
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
@ -71,8 +72,8 @@ class PaymentController extends Controller
|
||||
public function process()
|
||||
{
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys(request()->invoices))
|
||||
->whereClientId(auth()->user()->client->id)
|
||||
->get();
|
||||
->whereClientId(auth('contact')->user()->company()->id)
|
||||
->get();
|
||||
|
||||
$amount = $invoices->sum('balance');
|
||||
|
||||
@ -92,16 +93,12 @@ class PaymentController extends Controller
|
||||
return $invoice;
|
||||
});
|
||||
|
||||
|
||||
$payment_methods = auth()->user()->client->getPaymentMethods($amount);
|
||||
|
||||
//boot the payment gateway
|
||||
$gateway = CompanyGateway::find(request()->input('company_gateway_id'));
|
||||
|
||||
$payment_method_id = request()->input('payment_method_id');
|
||||
|
||||
//if there is a gateway fee, now is the time to calculate it
|
||||
//and add it to the invoice
|
||||
// Place to calculate gateway fee.
|
||||
|
||||
$data = [
|
||||
'invoices' => $invoices,
|
||||
@ -110,7 +107,7 @@ class PaymentController extends Controller
|
||||
'amount_with_fee' => $amount + $gateway->calcGatewayFee($amount),
|
||||
'token' => auth()->user()->client->gateway_token($gateway->id, $payment_method_id),
|
||||
'payment_method_id' => $payment_method_id,
|
||||
'hashed_ids' => explode(",", request()->input('hashed_ids')),
|
||||
'hashed_ids' => request()->invoices,
|
||||
];
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
@ -18,9 +19,9 @@ use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SystemLogTrait;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Omnipay\Omnipay;
|
||||
|
||||
/**
|
||||
@ -44,6 +45,7 @@ use Omnipay\Omnipay;
|
||||
class BasePaymentDriver
|
||||
{
|
||||
use SystemLogTrait;
|
||||
use MakesHash;
|
||||
|
||||
/* The company gateway instance*/
|
||||
protected $company_gateway;
|
||||
@ -105,7 +107,7 @@ class BasePaymentDriver
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompanyGatewayId() :int
|
||||
public function getCompanyGatewayId(): int
|
||||
{
|
||||
return $this->company_gateway->id;
|
||||
}
|
||||
@ -113,7 +115,7 @@ class BasePaymentDriver
|
||||
* Returns whether refunds are possible with the gateway
|
||||
* @return boolean TRUE|FALSE
|
||||
*/
|
||||
public function getRefundable() :bool
|
||||
public function getRefundable(): bool
|
||||
{
|
||||
return $this->refundable;
|
||||
}
|
||||
@ -122,7 +124,7 @@ class BasePaymentDriver
|
||||
* Returns whether token billing is possible with the gateway
|
||||
* @return boolean TRUE|FALSE
|
||||
*/
|
||||
public function getTokenBilling() :bool
|
||||
public function getTokenBilling(): bool
|
||||
{
|
||||
return $this->token_billing;
|
||||
}
|
||||
@ -132,7 +134,7 @@ class BasePaymentDriver
|
||||
* authorise and credit card.
|
||||
* @return [type] [description]
|
||||
*/
|
||||
public function canAuthoriseCreditCard() :bool
|
||||
public function canAuthoriseCreditCard(): bool
|
||||
{
|
||||
return $this->can_authorise_credit_card;
|
||||
}
|
||||
@ -149,11 +151,7 @@ class BasePaymentDriver
|
||||
$amount = $payment->getCompletedAmount();
|
||||
}
|
||||
|
||||
if ($payment->is_deleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $amount) {
|
||||
if ($payment->is_deleted || !$amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -224,9 +222,9 @@ class BasePaymentDriver
|
||||
refund($options) - refund an already processed transaction
|
||||
void($options) - generally can only be called up to 24 hours after submitting a transaction
|
||||
acceptNotification() - convert an incoming request from an off-site gateway to a generic notification object for further processing
|
||||
*/
|
||||
*/
|
||||
|
||||
protected function paymentDetails($input) : array
|
||||
protected function paymentDetails($input): array
|
||||
{
|
||||
$data = [
|
||||
'currency' => $this->client->getCurrencyCode(),
|
||||
@ -242,10 +240,10 @@ class BasePaymentDriver
|
||||
{
|
||||
$this->gateway();
|
||||
|
||||
$response = $this->gateway
|
||||
->purchase($data)
|
||||
->setItems($items)
|
||||
->send();
|
||||
$response = $this->gateway
|
||||
->purchase($data)
|
||||
->setItems($items)
|
||||
->send();
|
||||
|
||||
return $response;
|
||||
/*
|
||||
@ -257,11 +255,11 @@ class BasePaymentDriver
|
||||
$this->gateway();
|
||||
|
||||
return $this->gateway
|
||||
->completePurchase($data)
|
||||
->send();
|
||||
->completePurchase($data)
|
||||
->send();
|
||||
}
|
||||
|
||||
public function createPayment($data) : Payment
|
||||
public function createPayment($data): Payment
|
||||
{
|
||||
$payment = PaymentFactory::create($this->client->company->id, $this->client->user->id);
|
||||
$payment->client_id = $this->client->id;
|
||||
@ -274,11 +272,14 @@ class BasePaymentDriver
|
||||
}
|
||||
|
||||
|
||||
public function attachInvoices(Payment $payment, $hashed_ids) : Payment
|
||||
public function attachInvoices(Payment $payment, $hashed_ids): Payment
|
||||
{
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys($hashed_ids))
|
||||
->whereClientId($this->client->id)
|
||||
->get();
|
||||
$transformed = $this->transformKeys($hashed_ids);
|
||||
$array = is_array($transformed) ? $transformed : [$transformed];
|
||||
|
||||
$invoices = Invoice::whereIn('id', $array)
|
||||
->whereClientId($this->client->id)
|
||||
->get();
|
||||
|
||||
$payment->invoices()->sync($invoices);
|
||||
$payment->save();
|
||||
|
@ -25,7 +25,7 @@
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"tailwindcss": "^1.3",
|
||||
"tailwindcss": "^1.4",
|
||||
"turbolinks": "^5.2.0"
|
||||
}
|
||||
}
|
||||
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
@ -1,2 +1,2 @@
|
||||
/*! For license information please see action-selectors.js.LICENSE.txt */
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=2)}({2:function(e,t,n){e.exports=n("Boob")},Boob:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.parentElement=document.querySelector(".form-check-parent"),this.parentForm=document.getElementById("bulkActions")}var t,r,o;return t=e,(r=[{key:"watchCheckboxes",value:function(e){var t=this;document.querySelectorAll(".form-check-child").forEach((function(n){e.checked?(n.checked=e.checked,t.processChildItem(n,document.getElementById("bulkActions"))):(n.checked=!1,document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})))}))}},{key:"processChildItem",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};n.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()}));var r=document.createElement("INPUT");r.setAttribute("name","invoices[]"),r.setAttribute("value",e.dataset.value),r.setAttribute("class","child-hidden-input"),r.hidden=!0,t.append(r)}},{key:"handle",value:function(){var e=this;this.parentElement.addEventListener("click",(function(){e.watchCheckboxes(e.parentElement)}));var t=!0,n=!1,r=void 0;try{for(var o,c=function(){var t=o.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm,{single:!0}),document.querySelector('button[value="payment"]').click()}))},u=document.querySelectorAll(".pay-now-button")[Symbol.iterator]();!(t=(o=u.next()).done);t=!0)c()}catch(e){n=!0,r=e}finally{try{t||null==u.return||u.return()}finally{if(n)throw r}}var l=!0,i=!1,a=void 0;try{for(var d,f=function(){var t=d.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm)}))},s=document.querySelectorAll(".form-check-child")[Symbol.iterator]();!(l=(d=s.next()).done);l=!0)f()}catch(e){i=!0,a=e}finally{try{l||null==s.return||s.return()}finally{if(i)throw a}}}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}});
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=2)}({2:function(e,t,n){e.exports=n("Boob")},Boob:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.parentElement=document.querySelector(".form-check-parent"),this.parentForm=document.getElementById("bulkActions")}var t,r,o;return t=e,(r=[{key:"watchCheckboxes",value:function(e){var t=this;document.querySelectorAll(".form-check-child").forEach((function(n){e.checked?(n.checked=e.checked,t.processChildItem(n,document.getElementById("bulkActions"))):(n.checked=!1,document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})))}))}},{key:"processChildItem",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};n.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()}));var r=document.createElement("INPUT");r.setAttribute("name","invoices[]"),r.setAttribute("value",e.dataset.value),r.setAttribute("class","child-hidden-input"),r.hidden=!0,t.append(r)}},{key:"handle",value:function(){var e=this;this.parentElement.addEventListener("click",(function(){e.watchCheckboxes(e.parentElement)}));var t=!0,n=!1,r=void 0;try{for(var o,c=function(){var t=o.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm)}))},u=document.querySelectorAll(".form-check-child")[Symbol.iterator]();!(t=(o=u.next()).done);t=!0)c()}catch(e){n=!0,r=e}finally{try{t||null==u.return||u.return()}finally{if(n)throw r}}}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}});
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=49b0ee243139f6e72bd9",
|
||||
"/css/app.css": "/css/app.css?id=c317169666198ce30427",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=caec43815d9a13168a38",
|
||||
"/css/app.css": "/css/app.css?id=9b6694588d0604760071",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=3e58537817b968346c9f",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=af49e24958be5fc00c92",
|
||||
"/js/clients/payment_methods/authorize-stripe-card.js": "/js/clients/payment_methods/authorize-stripe-card.js?id=f4c45f0da9868d840799",
|
||||
"/js/clients/payments/process.js": "/js/clients/payments/process.js?id=bb91cb611d1bdab40973",
|
||||
|
@ -50,13 +50,6 @@ class ActionSelectors {
|
||||
this.watchCheckboxes(this.parentElement);
|
||||
});
|
||||
|
||||
for (let child of document.querySelectorAll(".pay-now-button")) {
|
||||
child.addEventListener("click", () => {
|
||||
this.processChildItem(child, this.parentForm, { single: true });
|
||||
document.querySelector('button[value="payment"]').click();
|
||||
});
|
||||
}
|
||||
|
||||
for (let child of document.querySelectorAll(".form-check-child")) {
|
||||
child.addEventListener("click", () => {
|
||||
this.processChildItem(child, this.parentForm);
|
||||
|
@ -87,9 +87,14 @@
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-no-wrap flex items-center justify-end text-sm leading-5 font-medium">
|
||||
@if($invoice->isPayable())
|
||||
<button class="button button-primary py-1 px-2 text-xs uppercase mr-3 pay-now-button" data-value="{{ $invoice->hashed_id }}">
|
||||
@lang('texts.pay_now')
|
||||
</button>
|
||||
<form action="{{ route('client.invoices.bulk') }}" method="post">
|
||||
@csrf
|
||||
<input type="hidden" name="invoices[]" value="{{ $invoice->hashed_id }}">
|
||||
<input type="hidden" name="action" value="payment">
|
||||
<button class="button button-primary py-1 px-2 text-xs uppercase mr-3">
|
||||
@lang('texts.pay_now')
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<a href="{{ route('client.invoice.show', $invoice->hashed_id) }}" class="button-link">
|
||||
@lang('texts.view')
|
||||
|
@ -80,7 +80,7 @@
|
||||
{{ ctrans('texts.token_billing_checkbox') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<input type="checkbox" class="form-check" name="token-billing-checkbox"/>
|
||||
<input type="checkbox" class="form-checkbox" name="token-billing-checkbox"/>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
|
@ -41,7 +41,7 @@
|
||||
<div class="rounded-md bg-white shadow-xs">
|
||||
<div class="py-1">
|
||||
@foreach($payment_methods as $payment_method)
|
||||
<a href="#" @click="{ open = false }"
|
||||
<a data-turbolinks="false" href="#" @click="{ open = false }"
|
||||
data-company-gateway-id="{{ $payment_method['company_gateway_id'] }}"
|
||||
data-gateway-type-id="{{ $payment_method['gateway_type_id'] }}"
|
||||
class="dropdown-gateway-button block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900">
|
||||
|
3
tailwind.config.js
vendored
3
tailwind.config.js
vendored
@ -1,6 +1,9 @@
|
||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
|
||||
module.exports = {
|
||||
purge: [
|
||||
'./resources/views/portal/ninja2020/**/*.blade.php',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
|
7
webpack.mix.js
vendored
7
webpack.mix.js
vendored
@ -1,8 +1,6 @@
|
||||
const mix = require("laravel-mix");
|
||||
const tailwindcss = require("tailwindcss");
|
||||
|
||||
require("laravel-mix-purgecss");
|
||||
|
||||
mix.js("resources/js/app.js", "public/js")
|
||||
.js(
|
||||
"resources/js/clients/payment_methods/authorize-stripe-card.js",
|
||||
@ -41,11 +39,6 @@ mix.sass("resources/sass/app.scss", "public/css")
|
||||
.options({
|
||||
processCssUrls: false,
|
||||
postCss: [tailwindcss("./tailwind.config.js")]
|
||||
})
|
||||
.purgeCss({
|
||||
enabled: mix.inProduction(),
|
||||
extensions: ["html", "php"]
|
||||
});
|
||||
|
||||
mix.version();
|
||||
mix.disableNotifications();
|
||||
|
Loading…
Reference in New Issue
Block a user