1
0
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:
Benjamin Beganović 2020-05-09 00:20:37 +02:00 committed by GitHub
parent 7f9abbf96b
commit 2704bf2f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 47 additions and 55 deletions

View File

@ -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,
];

View File

@ -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();

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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()}});

View File

@ -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",

View File

@ -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);

View File

@ -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')

View File

@ -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">

View File

@ -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
View File

@ -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
View File

@ -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();