mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 21:22:58 +01:00
commit
0c2e18beb3
19
README.md
19
README.md
@ -4,12 +4,18 @@
|
||||
|
||||
![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop)
|
||||
![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable)
|
||||
[![codecov](https://codecov.io/gh/invoiceninja/invoiceninja/branch/v2/graph/badge.svg)](https://codecov.io/gh/invoiceninja/invoiceninja)
|
||||
|
||||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
||||
|
||||
# Invoice Ninja version 5 is in Beta!
|
||||
# Invoice Ninja version 5.1 RC2!
|
||||
|
||||
We will be using the lessons learnt in Invoice Ninja 4.0 to build a bigger better platform to work from. If you would like to contribute to the project we will gladly accept contributions for code, user guides, bug tracking and feedback! Please consider the following guidelines prior to submitting a pull request:
|
||||
Invoice Ninja version 5.1 has now reached Release Candidate 2!
|
||||
|
||||
What does this mean exactly? We consider this version _almost_ stable. There may be some remaining small issues which we would love to get feedback on. We would really appreciate the community booting up this version and attempting the migration from their Invoice Ninja V4 application and inspect the migrated data.
|
||||
|
||||
We'd also like feedback on any issues that you can see, and help us nail down the few remaining issues before Version 5 graduates to Stable Gold Release.
|
||||
|
||||
Please note we do not consider this version ready for production use, please stick with your V4 installation for your production clients!
|
||||
|
||||
## Quick Start
|
||||
|
||||
@ -17,13 +23,10 @@ Currently the client portal and API are of alpha quality, to get started:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/invoiceninja/invoiceninja.git
|
||||
git checkout v2
|
||||
git checkout v5-stable
|
||||
cp .env.example .env
|
||||
cp .env.dusk.example .env.dusk.local
|
||||
php artisan key:generate
|
||||
composer update
|
||||
npm i
|
||||
npm run production
|
||||
```
|
||||
|
||||
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
|
||||
@ -33,7 +36,7 @@ Run if you want to load sample data, remember to configure .env
|
||||
php artisan migrate:fresh --seed && php artisan db:seed && php artisan ninja:create-test-data
|
||||
```
|
||||
|
||||
To Run the web server
|
||||
To run the web server
|
||||
```
|
||||
php artisan serve
|
||||
```
|
||||
|
@ -1 +1 @@
|
||||
5.0.55
|
||||
5.0.56
|
@ -232,11 +232,11 @@ class CompanySettings extends BaseSettings
|
||||
public $id_number = ''; //@implemented
|
||||
|
||||
public $page_size = 'A4'; //Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6
|
||||
public $font_size = 9; //@implemented
|
||||
public $font_size = 7; //@implemented
|
||||
public $primary_font = 'Roboto';
|
||||
public $secondary_font = 'Roboto';
|
||||
public $primary_color = '#4caf50';
|
||||
public $secondary_color = '#2196f3';
|
||||
public $primary_color = '#142cb5';
|
||||
public $secondary_color = '#7081e0';
|
||||
|
||||
public $hide_paid_to_date = false; //@TODO where?
|
||||
public $embed_documents = false; //@TODO where?
|
||||
|
@ -184,7 +184,9 @@ class BaseController extends Controller
|
||||
|
||||
protected function refreshResponse($query)
|
||||
{
|
||||
if (auth()->user()->getCompany()->is_large)
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->getCompany()->is_large)
|
||||
$this->manager->parseIncludes($this->mini_load);
|
||||
else
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
@ -200,74 +202,145 @@ class BaseController extends Controller
|
||||
$transformer = new $this->entity_transformer($this->serializer);
|
||||
$updated_at = request()->has('updated_at') ? request()->input('updated_at') : 0;
|
||||
|
||||
// if (auth()->user()->getCompany()->is_large && ! request()->has('updated_at')) {
|
||||
// return response()->json(['message' => ctrans('texts.large_account_update_parameter'), 'errors' =>[]], 401);
|
||||
// }
|
||||
|
||||
$updated_at = date('Y-m-d H:i:s', $updated_at);
|
||||
|
||||
$query->with(
|
||||
[
|
||||
'company' => function ($query) use ($updated_at) {
|
||||
'company' => function ($query) use ($updated_at, $user) {
|
||||
$query->whereNotNull('updated_at')->with('documents');
|
||||
},
|
||||
'company.clients' => function ($query) use ($updated_at) {
|
||||
'company.clients' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_client'))
|
||||
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.company_gateways' => function ($query) {
|
||||
'company.company_gateways' => function ($query) use ($user) {
|
||||
$query->whereNotNull('updated_at');
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('company_gateways.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.credits'=> function ($query) use ($updated_at) {
|
||||
'company.credits'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_credit'))
|
||||
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.designs'=> function ($query) use ($updated_at) {
|
||||
'company.designs'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('company');
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('designs.user_id', $user->id);
|
||||
},
|
||||
'company.documents'=> function ($query) use ($updated_at) {
|
||||
'company.documents'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
},
|
||||
'company.expenses'=> function ($query) use ($updated_at) {
|
||||
'company.expenses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if(!$user->hasPermission('view_expense'))
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
},
|
||||
'company.groups' => function ($query) use ($updated_at) {
|
||||
'company.groups' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('group_settings.user_id', $user->id);
|
||||
},
|
||||
'company.invoices'=> function ($query) use ($updated_at) {
|
||||
'company.invoices'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_invoice'))
|
||||
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.payments'=> function ($query) use ($updated_at) {
|
||||
'company.payments'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('paymentables', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_payment'))
|
||||
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.payment_terms'=> function ($query) use ($updated_at) {
|
||||
'company.payment_terms'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('payment_terms.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.products' => function ($query) use ($updated_at) {
|
||||
'company.products' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if(!$user->hasPermission('view_product'))
|
||||
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.projects'=> function ($query) use ($updated_at) {
|
||||
'company.projects'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if(!$user->hasPermission('view_project'))
|
||||
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.quotes'=> function ($query) use ($updated_at) {
|
||||
'company.quotes'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_quote'))
|
||||
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.recurring_invoices'=> function ($query) use ($updated_at) {
|
||||
'company.recurring_invoices'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_recurring_invoice'))
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.tasks'=> function ($query) use ($updated_at) {
|
||||
'company.tasks'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if(!$user->hasPermission('view_task'))
|
||||
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($updated_at) {
|
||||
'company.tax_rates' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('tax_rates.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.vendors'=> function ($query) use ($updated_at) {
|
||||
'company.vendors'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
||||
|
||||
if(!$user->hasPermission('view_vendor'))
|
||||
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($updated_at) {
|
||||
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('expense_categories.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.task_statuses'=> function ($query) use ($updated_at) {
|
||||
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('task_statuses.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('activities.user_id', $user->id);
|
||||
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -90,7 +90,7 @@ class InvoiceController extends Controller
|
||||
|
||||
//filter invoices which are payable
|
||||
$invoices = $invoices->filter(function ($invoice) {
|
||||
return $invoice->isPayable() && $invoice->balance > 0;
|
||||
return $invoice->isPayable();
|
||||
});
|
||||
|
||||
//return early if no invoices.
|
||||
|
@ -106,7 +106,7 @@ class PaymentController extends Controller
|
||||
if ($payable_invoices->count() == 0) {
|
||||
return redirect()
|
||||
->route('client.invoices.index')
|
||||
->with(['warning' => 'No payable invoices selected.']);
|
||||
->with(['message' => 'No payable invoices selected.']);
|
||||
}
|
||||
|
||||
$settings = auth()->user()->client->getMergedSettings();
|
||||
|
@ -370,14 +370,25 @@ class UserController extends BaseController
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
{
|
||||
$old_email = $user->email;
|
||||
$old_company_user = $user->company_user;
|
||||
$old_user = $user;
|
||||
|
||||
$new_email = $request->input('email');
|
||||
|
||||
$user = $this->user_repo->save($request->all(), $user);
|
||||
$user = $user->fresh();
|
||||
|
||||
if ($old_email != $new_email) {
|
||||
UserEmailChanged::dispatch($new_email, $old_email, auth()->user()->company());
|
||||
}
|
||||
|
||||
if(
|
||||
strcasecmp($old_company_user->permissions, $user->company_user->permissions) != 0 ||
|
||||
$old_company_user->is_admin != $user->company_user->is_admin
|
||||
){
|
||||
$user->company_user()->update(["permissions_updated_at" => now()]);
|
||||
}
|
||||
|
||||
event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars()));
|
||||
|
||||
return $this->itemResponse($user);
|
||||
|
@ -28,6 +28,7 @@ class CompanyUser extends Pivot
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'permissions_updated_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
|
@ -202,15 +202,22 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
$this->id = auth()->user()->id;
|
||||
}
|
||||
|
||||
if (request()->header('X-API-TOKEN')) {
|
||||
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
|
||||
->where('company_tokens.token', request()->header('X-API-TOKEN'))
|
||||
->withTrashed();
|
||||
} else {
|
||||
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
|
||||
->where('company_user.user_id', $this->id)
|
||||
->withTrashed();
|
||||
}
|
||||
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'user_id', 'id', 'user_id')
|
||||
->withTrashed();
|
||||
|
||||
// if (request()->header('X-API-TOKEN')) {
|
||||
|
||||
// nlog("with an API token");
|
||||
// nlog(request()->header('X-API-TOKEN'));
|
||||
|
||||
// return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
|
||||
// ->where('company_tokens.token', request()->header('X-API-TOKEN'))
|
||||
// ->withTrashed();
|
||||
// } else {
|
||||
// return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
|
||||
// ->where('company_user.user_id', $this->id)
|
||||
// ->withTrashed();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
@ -81,7 +82,14 @@ class AuthorizeCreditCard
|
||||
|
||||
private function processTokenPayment($request)
|
||||
{
|
||||
$client_gateway_token =ClientGatewayToken::where('token', $request->token)->firstOrFail();
|
||||
$client_gateway_token = ClientGatewayToken::query()
|
||||
->where('id', $this->decodePrimaryKey($request->token))
|
||||
->where('company_id', auth('contact')->user()->client->company->id)
|
||||
->first();
|
||||
|
||||
if (!$client_gateway_token) {
|
||||
throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401);
|
||||
}
|
||||
|
||||
$data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($client_gateway_token->gateway_customer_reference, $client_gateway_token->token, $request->input('amount_with_fee'));
|
||||
|
||||
@ -129,7 +137,7 @@ class AuthorizeCreditCard
|
||||
PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $amount);
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -156,7 +164,7 @@ class AuthorizeCreditCard
|
||||
$payment_record = [];
|
||||
$payment_record['amount'] = $amount;
|
||||
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
|
||||
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||
$payment_record['transaction_reference'] = $response->getTransactionResponse()->getTransId();
|
||||
|
||||
$payment = $this->authorize->createPayment($payment_record);
|
||||
|
@ -69,7 +69,7 @@ class AuthorizePaymentDriver extends BaseDriver
|
||||
['name' => 'client_name', 'label' => ctrans('texts.name'), 'type' => 'text', 'validation' => 'required|min:2'],
|
||||
['name' => 'contact_email', 'label' => ctrans('texts.email'), 'type' => 'text', 'validation' => 'required|email:rfc'],
|
||||
['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required'],
|
||||
['name' => 'client_address_line_2', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'sometimes'],
|
||||
['name' => 'client_address_line_2', 'label' => ctrans('texts.address2'), 'type' => 'text', 'validation' => 'sometimes'],
|
||||
['name' => 'client_city', 'label' => ctrans('texts.city'), 'type' => 'text', 'validation' => 'required'],
|
||||
['name' => 'client_state', 'label' => ctrans('texts.state'), 'type' => 'text', 'validation' => 'required'],
|
||||
['name' => 'client_postal_code', 'label' => ctrans('texts.postal_code'), 'type' => 'text', 'validation' => 'required'],
|
||||
@ -103,7 +103,7 @@ class AuthorizePaymentDriver extends BaseDriver
|
||||
}
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
{
|
||||
$this->setPaymentMethod($cgt->gateway_type_id);
|
||||
|
||||
return $this->payment_method->tokenBilling($cgt, $payment_hash);
|
||||
|
@ -231,7 +231,7 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
|
||||
$payment->service()->updateInvoicePayment($this->payment_hash);
|
||||
|
||||
if ($this->client->getSetting('client_online_payment_notification'))
|
||||
if ($this->client->getSetting('client_online_payment_notification'))
|
||||
$payment->service()->sendEmail();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
||||
|
@ -12,9 +12,12 @@
|
||||
|
||||
namespace App\PaymentDrivers\CheckoutCom;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\PaymentDrivers\CheckoutComPaymentDriver;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Checkout\Library\Exceptions\CheckoutHttpException;
|
||||
use Checkout\Models\Payments\IdSource;
|
||||
use Checkout\Models\Payments\Payment;
|
||||
@ -25,6 +28,7 @@ use Illuminate\View\View;
|
||||
class CreditCard
|
||||
{
|
||||
use Utilities;
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* @var CheckoutComPaymentDriver
|
||||
@ -78,6 +82,15 @@ class CreditCard
|
||||
{
|
||||
$this->checkout->init();
|
||||
|
||||
$cgt = ClientGatewayToken::query()
|
||||
->where('id', $this->decodePrimaryKey($request->input('token')))
|
||||
->where('company_id', auth('contact')->user()->client->company->id)
|
||||
->first();
|
||||
|
||||
if (!$cgt) {
|
||||
throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401);
|
||||
}
|
||||
|
||||
$state = [
|
||||
'server_response' => json_decode($request->gateway_response),
|
||||
'value' => $request->value,
|
||||
@ -90,11 +103,12 @@ class CreditCard
|
||||
|
||||
$state = array_merge($state, $request->all());
|
||||
$state['store_card'] = boolval($state['store_card']);
|
||||
$state['token'] = $cgt;
|
||||
|
||||
$this->checkout->payment_hash->data = array_merge((array) $this->checkout->payment_hash->data, $state);
|
||||
$this->checkout->payment_hash->data = array_merge((array)$this->checkout->payment_hash->data, $state);
|
||||
$this->checkout->payment_hash->save();
|
||||
|
||||
if ($request->has('token') && !is_null($request->token) && !empty($request->token)) {
|
||||
if ($request->has('token')) {
|
||||
return $this->attemptPaymentUsingToken($request);
|
||||
}
|
||||
|
||||
@ -103,7 +117,7 @@ class CreditCard
|
||||
|
||||
private function attemptPaymentUsingToken(PaymentResponseRequest $request)
|
||||
{
|
||||
$method = new IdSource($this->checkout->payment_hash->data->token);
|
||||
$method = new IdSource($this->checkout->payment_hash->data->token->token);
|
||||
|
||||
return $this->completePayment($method, $request);
|
||||
}
|
||||
@ -125,7 +139,7 @@ class CreditCard
|
||||
$payment->amount = $this->checkout->payment_hash->data->value;
|
||||
$payment->reference = $this->checkout->payment_hash->data->reference;
|
||||
|
||||
$this->checkout->payment_hash->data = array_merge((array) $this->checkout->payment_hash->data, ['checkout_payment_ref' => $payment]);
|
||||
$this->checkout->payment_hash->data = array_merge((array)$this->checkout->payment_hash->data, ['checkout_payment_ref' => $payment]);
|
||||
$this->checkout->payment_hash->save();
|
||||
|
||||
if ($this->checkout->client->currency()->code === 'EUR') {
|
||||
@ -155,7 +169,7 @@ class CreditCard
|
||||
if ($response->status == 'Declined') {
|
||||
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
|
||||
|
||||
PaymentFailureMailer::dispatch($this->checkout->client, $response->response_summary, $this->checkout->client->company, $this->checkout->payment_hash->data->value);
|
||||
PaymentFailureMailer::dispatch($this->checkout->client, $response->response_summary, $this->checkout->client->company, $this->checkout->payment_hash->data->value);
|
||||
|
||||
|
||||
return $this->processUnsuccessfulPayment($response);
|
||||
|
@ -81,7 +81,7 @@ trait Utilities
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->getParent()->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
public function processUnsuccessfulPayment(Payment $_payment)
|
||||
public function processUnsuccessfulPayment(Payment $_payment, $throw_exception = true)
|
||||
{
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->getParent()->client,
|
||||
@ -103,7 +103,9 @@ trait Utilities
|
||||
$this->getParent()->client
|
||||
);
|
||||
|
||||
throw new PaymentFailed($_payment->status, $_payment->http_code);
|
||||
if ($throw_exception) {
|
||||
throw new PaymentFailed($_payment->status, $_payment->http_code);
|
||||
}
|
||||
}
|
||||
|
||||
private function processPendingPayment(Payment $_payment)
|
||||
|
@ -12,18 +12,24 @@
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\CheckoutCom\CreditCard;
|
||||
use App\PaymentDrivers\CheckoutCom\Utilities;
|
||||
use App\Utils\Traits\SystemLogTrait;
|
||||
use Checkout\CheckoutApi;
|
||||
use Checkout\Library\Exceptions\CheckoutHttpException;
|
||||
use Checkout\Models\Payments\IdSource;
|
||||
use Checkout\Models\Payments\Refund;
|
||||
use Exception;
|
||||
|
||||
@ -96,8 +102,8 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
public function init()
|
||||
{
|
||||
$config = [
|
||||
'secret' => $this->company_gateway->getConfigField('secretApiKey'),
|
||||
'public' => $this->company_gateway->getConfigField('publicApiKey'),
|
||||
'secret' => $this->company_gateway->getConfigField('secretApiKey'),
|
||||
'public' => $this->company_gateway->getConfigField('publicApiKey'),
|
||||
'sandbox' => $this->company_gateway->getConfigField('testMode'),
|
||||
];
|
||||
|
||||
@ -108,7 +114,7 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
|
||||
/**
|
||||
* Process different view depending on payment type
|
||||
* @param int $gateway_type_id The gateway type
|
||||
* @param int $gateway_type_id The gateway type
|
||||
* @return string The view string
|
||||
*/
|
||||
public function viewForType($gateway_type_id)
|
||||
@ -132,7 +138,7 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
/**
|
||||
* Payment View
|
||||
*
|
||||
* @param array $data Payment data array
|
||||
* @param array $data Payment data array
|
||||
* @return view The payment view
|
||||
*/
|
||||
public function processPaymentView(array $data)
|
||||
@ -143,7 +149,7 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
/**
|
||||
* Process the payment response
|
||||
*
|
||||
* @param Request $request The payment request
|
||||
* @param Request $request The payment request
|
||||
* @return view The payment response view
|
||||
*/
|
||||
public function processPaymentResponse($request)
|
||||
@ -188,7 +194,95 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
// ..
|
||||
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
|
||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->first();
|
||||
|
||||
if ($invoice) {
|
||||
$description = "Invoice {$invoice->number} for {$amount} for client {$this->client->present()->name()}";
|
||||
} else {
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->client->present()->name()}";
|
||||
}
|
||||
|
||||
$this->init();
|
||||
|
||||
$method = new IdSource($cgt->token);
|
||||
|
||||
$payment = new \Checkout\Models\Payments\Payment($method, $this->client->getCurrencyCode());
|
||||
$payment->amount = $this->convertToCheckoutAmount($amount, $this->client->getCurrencyCode());
|
||||
$payment->reference = $cgt->meta->last4 . '-' . now();
|
||||
|
||||
$request = new PaymentResponseRequest();
|
||||
$request->setMethod('POST');
|
||||
$request->request->add(['payment_hash' => $payment_hash->hash]);
|
||||
|
||||
//$this->setPaymentHash($payment_hash);
|
||||
|
||||
try {
|
||||
$response = $this->gateway->payments()->request($payment);
|
||||
|
||||
if ($response->status == 'Authorized') {
|
||||
$this->confirmGatewayFee($request);
|
||||
|
||||
$data = [
|
||||
'payment_method' => $response->source['id'],
|
||||
'payment_type' => PaymentType::parseCardType(strtolower($response->source['scheme'])),
|
||||
'amount' => $amount,
|
||||
'transaction_reference' => $response->id,
|
||||
];
|
||||
|
||||
$payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $response, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_CHECKOUT,
|
||||
$this->client
|
||||
);
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
if ($response->status == 'Declined') {
|
||||
$this->unWindGatewayFees($payment_hash);
|
||||
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->client, $response->response_summary,
|
||||
$this->client->company,
|
||||
$amount
|
||||
);
|
||||
|
||||
$message = [
|
||||
'server_response' => $response,
|
||||
'data' => $payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_CHECKOUT,
|
||||
$this->client
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception | CheckoutHttpException $e) {
|
||||
$this->unWindGatewayFees($payment_hash);
|
||||
$message = $e instanceof CheckoutHttpException
|
||||
? $e->getBody()
|
||||
: $e->getMessage();
|
||||
|
||||
$data = [
|
||||
'status' => '',
|
||||
'error_type' => '',
|
||||
'error_code' => $e->getCode(),
|
||||
'param' => '',
|
||||
'message' => $message,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client);
|
||||
}
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
|
||||
|
@ -22,6 +22,7 @@ use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Exception;
|
||||
use Stripe\Customer;
|
||||
use Stripe\Exception\CardException;
|
||||
@ -29,6 +30,8 @@ use Stripe\Exception\InvalidRequestException;
|
||||
|
||||
class ACH
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/** @var StripePaymentDriver */
|
||||
public $stripe;
|
||||
|
||||
@ -109,18 +112,27 @@ class ACH
|
||||
{
|
||||
$this->stripe->init();
|
||||
|
||||
$source = ClientGatewayToken::query()
|
||||
->where('id', $this->decodePrimaryKey($request->source))
|
||||
->where('company_id', auth('contact')->user()->client->company->id)
|
||||
->first();
|
||||
|
||||
if (!$source) {
|
||||
throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401);
|
||||
}
|
||||
|
||||
$state = [
|
||||
'payment_method' => $request->payment_method_id,
|
||||
'gateway_type_id' => $request->company_gateway_id,
|
||||
'amount' => $this->stripe->convertToStripeAmount($request->amount, $this->stripe->client->currency()->precision),
|
||||
'currency' => $request->currency,
|
||||
'source' => $request->source,
|
||||
'customer' => $request->customer,
|
||||
];
|
||||
|
||||
$state = array_merge($state, $request->all());
|
||||
$state['source'] = $source->token;
|
||||
|
||||
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $state);
|
||||
$this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state);
|
||||
$this->stripe->payment_hash->save();
|
||||
|
||||
try {
|
||||
@ -133,7 +145,7 @@ class ACH
|
||||
|
||||
$state = array_merge($state, $request->all());
|
||||
|
||||
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $state);
|
||||
$this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state);
|
||||
$this->stripe->payment_hash->save();
|
||||
|
||||
if ($state['charge']->status === 'pending' && is_null($state['charge']->failure_message)) {
|
||||
@ -145,6 +157,8 @@ class ACH
|
||||
if ($e instanceof CardException) {
|
||||
return redirect()->route('client.payment_methods.verification', ['id' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
|
||||
}
|
||||
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,8 +217,8 @@ class ACH
|
||||
{
|
||||
try {
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->brand = (string) sprintf('%s (%s)', $method->bank_name, ctrans('texts.ach'));
|
||||
$payment_meta->last4 = (string) $method->last4;
|
||||
$payment_meta->brand = (string)sprintf('%s (%s)', $method->bank_name, ctrans('texts.ach'));
|
||||
$payment_meta->last4 = (string)$method->last4;
|
||||
$payment_meta->type = GatewayType::BANK_TRANSFER;
|
||||
|
||||
$data = [
|
||||
|
@ -95,17 +95,37 @@ class StripePaymentDriver extends BaseDriver
|
||||
{
|
||||
$types = [
|
||||
GatewayType::CREDIT_CARD,
|
||||
GatewayType::BANK_TRANSFER,
|
||||
GatewayType::CRYPTO,
|
||||
GatewayType::ALIPAY,
|
||||
// GatewayType::SEPA, // TODO: Missing implementation
|
||||
// GatewayType::APPLE_PAY, // TODO:: Missing implementation
|
||||
];
|
||||
|
||||
if ($this->company_gateway->getSofortEnabled() && $this->invitation && $this->client() && isset($this->client()->country) && in_array($this->client()->country, ['AUT', 'BEL', 'DEU', 'ITA', 'NLD', 'ESP'])) {
|
||||
// $this->invitation = false
|
||||
// $this->client doesn't exist
|
||||
// $this->client->country is relationship?
|
||||
// Missing Slovenia for Alipay
|
||||
|
||||
if ($this->company_gateway->getSofortEnabled()
|
||||
&& $this->client
|
||||
&& isset($this->client->country)
|
||||
&& in_array($this->client->country->iso_3166_3, ['AUT', 'BEL', 'DEU', 'ITA', 'NLD', 'ESP'])) {
|
||||
$types[] = GatewayType::SOFORT;
|
||||
}
|
||||
|
||||
if ($this->company_gateway->getAchEnabled()
|
||||
&& $this->client
|
||||
&& isset($this->client->country)
|
||||
&& in_array($this->client->country->iso_3166_3, ['USA'])) {
|
||||
$types[] = GatewayType::BANK_TRANSFER;
|
||||
}
|
||||
|
||||
if ($this->company_gateway->getAchEnabled()
|
||||
&& $this->client
|
||||
&& isset($this->client->country)
|
||||
&& in_array($this->client->country->iso_3166_3, ['AUS', 'DNK', 'DEU', 'ITA', 'LUX', 'NOR', 'SVN', 'GBR', 'AUT', 'EST', 'GRC', 'JPN', 'MYS', 'PRT', 'ESP', 'USA', 'BEL', 'FIN', 'HKG', 'LVA', 'NLD', 'SGP', 'SWE', 'CAN', 'FRA', 'IRL', 'LTU', 'NZL', 'SVK', 'CHE'])) {
|
||||
$types[] = GatewayType::ALIPAY;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
@ -175,7 +195,7 @@ class StripePaymentDriver extends BaseDriver
|
||||
return $this->payment_method->paymentView($data);
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return $this->payment_method->paymentResponse($request);
|
||||
}
|
||||
|
@ -48,11 +48,11 @@ class AutoBillInvoice extends AbstractService
|
||||
$this->invoice = $this->invoice->service()->markSent()->save();
|
||||
|
||||
/* Mark the invoice as paid if there is no balance */
|
||||
if ((int)$this->invoice->balance == 0)
|
||||
if ((int)$this->invoice->balance == 0)
|
||||
return $this->invoice->service()->markPaid()->save();
|
||||
|
||||
|
||||
//if the credits cover the payments, we stop here, build the payment with credits and exit early
|
||||
if ($this->client->getSetting('use_credits_payment') != 'off')
|
||||
if ($this->client->getSetting('use_credits_payment') != 'off')
|
||||
$this->applyCreditPayment();
|
||||
|
||||
/* Determine $amount */
|
||||
@ -70,9 +70,9 @@ class AutoBillInvoice extends AbstractService
|
||||
$gateway_token = $this->getGateway($amount);
|
||||
|
||||
/* Bail out if no payment methods available */
|
||||
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing)
|
||||
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing)
|
||||
return $this->invoice;
|
||||
|
||||
|
||||
/* $gateway fee */
|
||||
//$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
|
||||
$this->invoice = $this->invoice->service()->addGatewayFee($gateway_token->gateway, $gateway_token->gateway_type_id, $amount)->save();
|
||||
@ -261,7 +261,7 @@ class AutoBillInvoice extends AbstractService
|
||||
* @param float $amount The amount to charge
|
||||
* @return ClientGatewayToken The client gateway token
|
||||
*/
|
||||
// private function
|
||||
// private function
|
||||
// {
|
||||
// $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC')->get();
|
||||
|
||||
@ -280,18 +280,18 @@ class AutoBillInvoice extends AbstractService
|
||||
$gateway_tokens = $this->client->gateway_tokens;
|
||||
|
||||
$filtered_gateways = $gateway_tokens->filter(function ($gateway_token) use($amount) {
|
||||
|
||||
|
||||
$company_gateway = $gateway_token->gateway;
|
||||
|
||||
//check if fees and limits are set
|
||||
if (isset($company_gateway->fees_and_limits) && property_exists($company_gateway->fees_and_limits, $gateway_token->gateway_type_id))
|
||||
if (isset($company_gateway->fees_and_limits) && property_exists($company_gateway->fees_and_limits, $gateway_token->gateway_type_id))
|
||||
{
|
||||
//if valid we keep this gateway_token
|
||||
if ($this->invoice->client->validGatewayForAmount($company_gateway->fees_and_limits->{$gateway_token->gateway_type_id}, $amount))
|
||||
if ($this->invoice->client->validGatewayForAmount($company_gateway->fees_and_limits->{$gateway_token->gateway_type_id}, $amount))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
return true; //if no fees_and_limits set then we automatically must add this gateway
|
||||
|
||||
|
@ -314,6 +314,12 @@ class Design extends BaseDesign
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
|
||||
} elseif ($column == '$product.quantity' && !$this->client->company->enable_product_quantity) {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
|
||||
} elseif ($column == '$product.tax_rate1') {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax1-th", 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
|
||||
} elseif ($column == '$product.tax_rate2') {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax2-th", 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
|
||||
} elseif ($column == '$product.tax_rate3') {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax3-th", 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
|
||||
} else {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
|
||||
}
|
||||
@ -394,6 +400,12 @@ class Design extends BaseDesign
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$product.quantity'], 'properties' => ['data-ref' => 'product_table-product.quantity-td', 'style' => 'display: none;']];
|
||||
} elseif ($cell == '$task.hours') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity'], 'properties' => ['data-ref' => 'task_table-task.hours-td']];
|
||||
} elseif ($cell == '$product.tax_rate1') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax1-td']];
|
||||
} elseif ($cell == '$product.tax_rate2') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td']];
|
||||
} elseif ($cell == '$product.tax_rate3') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td']];
|
||||
} else {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class CompanyUserTransformer extends EntityTransformer
|
||||
'updated_at' => (int) $company_user->updated_at,
|
||||
'archived_at' => (int) $company_user->deleted_at,
|
||||
'created_at' => (int) $company_user->created_at,
|
||||
|
||||
'permissions_updated_at' => (int) $company_user->permissions_updated_at,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,119 @@ trait GeneratesCounter
|
||||
//todo in the form validation, we need to ensure that if a prefix and pattern is set we throw a validation error,
|
||||
//only one type is allow else this will cause confusion to the end user
|
||||
|
||||
|
||||
|
||||
private function getNextEntityNumber($entity, Client $client)
|
||||
{
|
||||
$prefix = '';
|
||||
|
||||
$this->resetCounters($client);
|
||||
|
||||
$is_client_counter = false;
|
||||
|
||||
$counter_string = $this->getEntityCounter($entity, $client);
|
||||
$pattern = $this->getNumberPattern($entity, $client);
|
||||
|
||||
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
|
||||
|
||||
if (property_exists($client->settings, $counter_string)) {
|
||||
$counter = $client->settings->{$counter_string};
|
||||
} else {
|
||||
$counter = 1;
|
||||
}
|
||||
|
||||
$counter_entity = $client;
|
||||
} elseif (strpos($pattern, 'groupCounter') || strpos($pattern, 'group_counter')) {
|
||||
|
||||
if (property_exists($client->group_settings, $counter_string)) {
|
||||
$counter = $client->group_settings->{$counter_string};
|
||||
} else {
|
||||
$counter = 1;
|
||||
}
|
||||
|
||||
$counter_entity = $client->group_settings;
|
||||
|
||||
} else {
|
||||
$counter = $client->company->settings->{$counter_string};
|
||||
$counter_entity = $client->company;
|
||||
}
|
||||
|
||||
//If it is a quote - we need to
|
||||
$pattern = $this->getNumberPattern($entity, $client);
|
||||
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
|
||||
if($entity instanceof Invoice && $entity && $entity->recurring_id)
|
||||
$prefix = $client->getSetting('recurring_number_prefix');
|
||||
|
||||
$entity_number = $this->checkEntityNumber($entity, $client, $counter, $padding, $pattern, $prefix);
|
||||
|
||||
$this->incrementCounter($counter_entity, $counter_string);
|
||||
|
||||
return $entity_number;
|
||||
|
||||
}
|
||||
|
||||
private function getNumberPattern($entity, Client $client)
|
||||
{
|
||||
$pattern_string = '';
|
||||
|
||||
switch ($entity) {
|
||||
case Invoice::class:
|
||||
$pattern_string = 'invoice_number_pattern';
|
||||
break;
|
||||
case Quote::class:
|
||||
$pattern_string = 'quote_number_pattern';
|
||||
break;
|
||||
case RecurringInvoice::class:
|
||||
$pattern_string = 'recurring_invoice_number_pattern';
|
||||
break;
|
||||
case Payment::class:
|
||||
$pattern_string = 'payment_number_pattern';
|
||||
break;
|
||||
case Credit::class:
|
||||
$pattern_string = 'credit_number_pattern';
|
||||
break;
|
||||
case Project::class:
|
||||
$pattern_string = 'project_number_pattern';
|
||||
break;
|
||||
}
|
||||
|
||||
return $client->getSetting($pattern_string);
|
||||
}
|
||||
|
||||
private function getEntityCounter($entity, $client)
|
||||
{
|
||||
switch ($entity) {
|
||||
case Invoice::class:
|
||||
return 'invoice_number_counter';
|
||||
break;
|
||||
case Quote::class:
|
||||
|
||||
if ($this->hasSharedCounter($client))
|
||||
return 'invoice_number_counter';
|
||||
|
||||
return 'quote_number_counter';
|
||||
break;
|
||||
case RecurringInvoice::class:
|
||||
return 'recurring_invoice_number_counter';
|
||||
break;
|
||||
case Payment::class:
|
||||
return 'payment_number_counter';
|
||||
break;
|
||||
case Credit::class:
|
||||
return 'credit_number_counter';
|
||||
break;
|
||||
case Project::class:
|
||||
return 'project_number_counter';
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'default_number_counter';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next invoice number.
|
||||
*
|
||||
@ -43,42 +156,7 @@ trait GeneratesCounter
|
||||
*/
|
||||
public function getNextInvoiceNumber(Client $client, ?Invoice $invoice) :string
|
||||
{
|
||||
//Reset counters if enabled
|
||||
$this->resetCounters($client);
|
||||
|
||||
//todo handle if we have specific client patterns in the future
|
||||
$pattern = $client->getSetting('invoice_number_pattern');
|
||||
//Determine if we are using client_counters
|
||||
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
|
||||
if (property_exists($client->settings, 'invoice_number_counter')) {
|
||||
$counter = $client->settings->invoice_number_counter;
|
||||
} else {
|
||||
$counter = 1;
|
||||
}
|
||||
|
||||
$counter_entity = $client;
|
||||
} elseif (strpos($pattern, 'groupCounter') || strpos($pattern, 'group_counter')) {
|
||||
$counter = $client->group_settings->invoice_number_counter;
|
||||
$counter_entity = $client->group_settings;
|
||||
} else {
|
||||
$counter = $client->company->settings->invoice_number_counter;
|
||||
$counter_entity = $client->company;
|
||||
}
|
||||
|
||||
//Return a valid counter
|
||||
$pattern = $client->getSetting('invoice_number_pattern');
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
$prefix = '';
|
||||
|
||||
if ($invoice && $invoice->recurring_id) {
|
||||
$prefix = $client->getSetting('recurring_number_prefix');
|
||||
}
|
||||
|
||||
$invoice_number = $this->checkEntityNumber(Invoice::class, $client, $counter, $padding, $pattern, $prefix);
|
||||
|
||||
$this->incrementCounter($counter_entity, 'invoice_number_counter');
|
||||
|
||||
return $invoice_number;
|
||||
return $this->getNextEntityNumber(Invoice::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,145 +168,36 @@ trait GeneratesCounter
|
||||
*/
|
||||
public function getNextCreditNumber(Client $client) :string
|
||||
{
|
||||
//Reset counters if enabled
|
||||
$this->resetCounters($client);
|
||||
|
||||
//todo handle if we have specific client patterns in the future
|
||||
$pattern = $client->getSetting('credit_number_pattern');
|
||||
//Determine if we are using client_counters
|
||||
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
|
||||
$counter = $client->settings->credit_number_counter;
|
||||
$counter_entity = $client;
|
||||
} elseif (strpos($pattern, 'groupCounter') || strpos($pattern, 'group_counter')) {
|
||||
$counter = $client->group_settings->credit_number_counter;
|
||||
$counter_entity = $client->group_settings;
|
||||
} else {
|
||||
$counter = $client->company->settings->credit_number_counter;
|
||||
$counter_entity = $client->company;
|
||||
}
|
||||
|
||||
//Return a valid counter
|
||||
$pattern = $client->getSetting('credit_number_pattern');
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
|
||||
$credit_number = $this->checkEntityNumber(Credit::class, $client, $counter, $padding, $pattern);
|
||||
|
||||
$this->incrementCounter($counter_entity, 'credit_number_counter');
|
||||
|
||||
return $credit_number;
|
||||
return $this->getNextEntityNumber(Credit::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next quote number.
|
||||
*
|
||||
* @param Client $client The client
|
||||
*
|
||||
* @return string The next credit number.
|
||||
*/
|
||||
public function getNextQuoteNumber(Client $client)
|
||||
{
|
||||
//Reset counters if enabled
|
||||
$this->resetCounters($client);
|
||||
|
||||
$used_counter = 'quote_number_counter';
|
||||
|
||||
if ($this->hasSharedCounter($client)) {
|
||||
$used_counter = 'invoice_number_counter';
|
||||
}
|
||||
|
||||
//todo handle if we have specific client patterns in the future
|
||||
$pattern = $client->getSetting('quote_number_pattern');
|
||||
//Determine if we are using client_counters
|
||||
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
|
||||
$counter = $client->settings->{$used_counter};
|
||||
$counter_entity = $client;
|
||||
} elseif (strpos($pattern, 'groupCounter') || strpos($pattern, 'group_counter')) {
|
||||
$counter = $client->group_settings->{$used_counter};
|
||||
$counter_entity = $client->group_settings;
|
||||
} else {
|
||||
$counter = $client->company->settings->{$used_counter};
|
||||
$counter_entity = $client->company;
|
||||
}
|
||||
|
||||
//Return a valid counter
|
||||
$pattern = $client->getSetting('quote_number_pattern');
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
|
||||
$quote_number = $this->checkEntityNumber(Quote::class, $client, $counter, $padding, $pattern);
|
||||
|
||||
// if($this->recurring_id)
|
||||
// $quote_number = $this->prefixCounter($quote_number, $client->getSetting('recurring_number_prefix'));
|
||||
|
||||
$this->incrementCounter($counter_entity, $used_counter);
|
||||
|
||||
return $quote_number;
|
||||
return $this->getNextEntityNumber(Quote::class, $client);
|
||||
}
|
||||
|
||||
public function getNextRecurringInvoiceNumber(Client $client)
|
||||
{
|
||||
|
||||
//Reset counters if enabled
|
||||
$this->resetCounters($client);
|
||||
|
||||
$is_client_counter = false;
|
||||
|
||||
//todo handle if we have specific client patterns in the future
|
||||
$pattern = $client->company->settings->recurring_invoice_number_pattern;
|
||||
|
||||
//Determine if we are using client_counters
|
||||
if (strpos($pattern, 'client_counter') === false) {
|
||||
$counter = $client->company->settings->recurring_invoice_number_counter;
|
||||
} else {
|
||||
$counter = $client->settings->recurring_invoice_number_counter;
|
||||
$is_client_counter = true;
|
||||
}
|
||||
|
||||
//Return a valid counter
|
||||
$pattern = '';
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
$invoice_number = $this->checkEntityNumber(RecurringInvoice::class, $client, $counter, $padding, $pattern);
|
||||
//$invoice_number = $this->prefixCounter($invoice_number, $client->getSetting('recurring_number_prefix'));
|
||||
|
||||
//increment the correct invoice_number Counter (company vs client)
|
||||
if ($is_client_counter) {
|
||||
$this->incrementCounter($client, 'recurring_invoice_number_counter');
|
||||
} else {
|
||||
$this->incrementCounter($client->company, 'recurring_invoice_number_counter');
|
||||
}
|
||||
|
||||
return $invoice_number;
|
||||
return $this->getNextEntityNumber(RecurringInvoice::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment Number Generator.
|
||||
* @param Client $client
|
||||
* @return string The payment number
|
||||
* Gets the next Payment number.
|
||||
*
|
||||
* @param Client $client The client
|
||||
*
|
||||
* @return string The next payment number.
|
||||
*/
|
||||
public function getNextPaymentNumber(Client $client) :string
|
||||
{
|
||||
|
||||
//Reset counters if enabled
|
||||
$this->resetCounters($client);
|
||||
|
||||
$is_client_counter = false;
|
||||
|
||||
//todo handle if we have specific client patterns in the future
|
||||
$pattern = $client->company->settings->payment_number_pattern;
|
||||
|
||||
//Determine if we are using client_counters
|
||||
if (strpos($pattern, 'client_counter') === false) {
|
||||
$counter = $client->company->settings->payment_number_counter;
|
||||
} else {
|
||||
$counter = $client->settings->payment_number_counter;
|
||||
$is_client_counter = true;
|
||||
}
|
||||
|
||||
//Return a valid counter
|
||||
$pattern = '';
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
$payment_number = $this->checkEntityNumber(Payment::class, $client, $counter, $padding, $pattern);
|
||||
|
||||
//increment the correct invoice_number Counter (company vs client)
|
||||
if ($is_client_counter) {
|
||||
$this->incrementCounter($client, 'payment_number_counter');
|
||||
} else {
|
||||
$this->incrementCounter($client->company, 'payment_number_counter');
|
||||
}
|
||||
|
||||
return (string) $payment_number;
|
||||
return $this->getNextEntityNumber(Payment::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -342,7 +311,7 @@ trait GeneratesCounter
|
||||
*
|
||||
* @return bool True if has shared counter, False otherwise.
|
||||
*/
|
||||
public function hasSharedCounter(Client $client) : bool
|
||||
public function hasSharedCounter(Client $client) : bool
|
||||
{
|
||||
return (bool) $client->getSetting('shared_invoice_quote_counter');
|
||||
}
|
||||
@ -404,6 +373,9 @@ trait GeneratesCounter
|
||||
$settings->invoice_number_counter = 0;
|
||||
}
|
||||
|
||||
if(!property_exists($settings, $counter_name))
|
||||
$settings->{$counter_name} = 1;
|
||||
|
||||
$settings->{$counter_name} = $settings->{$counter_name} + 1;
|
||||
|
||||
$entity->settings = $settings;
|
||||
|
@ -13,7 +13,7 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', ''),
|
||||
'app_version' => '5.0.55',
|
||||
'app_version' => '5.0.56',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddPermissionChangedTimestamp extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('company_user', function (Blueprint $table) {
|
||||
$table->timestamp('permissions_updated_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
42
public/flutter_service_worker.js
vendored
42
public/flutter_service_worker.js
vendored
@ -3,35 +3,35 @@ const MANIFEST = 'flutter-app-manifest';
|
||||
const TEMP = 'flutter-temp-cache';
|
||||
const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"manifest.json": "77215c1737c7639764e64a192be2f7b8",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff",
|
||||
"assets/NOTICES": "c3e1cbfaeb1a4f54fadae1bd6558d91b",
|
||||
"assets/assets/images/logo.png": "090f69e23311a4b6d851b3880ae52541",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/google-icon.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/google-icon.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/logo.png": "090f69e23311a4b6d851b3880ae52541",
|
||||
"assets/NOTICES": "c3e1cbfaeb1a4f54fadae1bd6558d91b",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "1288c9e28052e028aba623321f7826ac",
|
||||
"/": "23224b5e03519aaa87594403d54412cf",
|
||||
"main.dart.js": "419ce42069c50ba32d64d01d76373c60",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"manifest.json": "77215c1737c7639764e64a192be2f7b8",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"version.json": "24380404aa64649901a0878a4f6aae18",
|
||||
"main.dart.js": "1071216a656504599447ac0e362ca27a",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
|
||||
};
|
||||
|
||||
|
2
public/js/app.js
vendored
2
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
@ -1,2 +1,2 @@
|
||||
/*! For license information please see checkout-credit-card.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=8)}({8:function(e,t,n){e.exports=n("fQHp")},fQHp: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.tokens=[]}var t,r,o;return t=e,(r=[{key:"mountFrames",value:function(){console.log("Mount checkout frames..")}},{key:"handlePaymentUsingToken",value:function(e){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=e.target.dataset.token}},{key:"handlePaymentUsingCreditCard",value:function(e){var t;document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid";var n=document.getElementById("pay-button"),r=null!==(t=document.querySelector('meta[name="public-key"]').content)&&void 0!==t?t:"",o=document.getElementById("payment-form");Frames.init(r),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,(function(e){n.disabled=!Frames.isCardValid()})),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,(function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()})),o.addEventListener("submit",(function(e){e.preventDefault(),Frames.submitCard()}))}},{key:"completePaymentUsingToken",value:function(e){var t=document.getElementById("pay-now-with-token");t.disabled=!0,t.querySelector("svg").classList.remove("hidden"),t.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}},{key:"handle",value:function(){var e=this;this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach((function(t){return t.addEventListener("click",e.handlePaymentUsingToken)})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}])&&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=8)}({8:function(e,t,n){e.exports=n("fQHp")},fQHp: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.tokens=[]}var t,r,o;return t=e,(r=[{key:"mountFrames",value:function(){console.log("Mount checkout frames..")}},{key:"handlePaymentUsingToken",value:function(e){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=e.target.dataset.token}},{key:"handlePaymentUsingCreditCard",value:function(e){var t;document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid";var n=document.getElementById("pay-button"),r=null!==(t=document.querySelector('meta[name="public-key"]').content)&&void 0!==t?t:"",o=document.getElementById("payment-form");Frames.init(r),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,(function(e){n.disabled=!Frames.isCardValid()})),Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED,(function(e){pay.button.disabled=!1})),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,(function(e){n.disabled=!0,document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()})),o.addEventListener("submit",(function(e){e.preventDefault(),Frames.submitCard()}))}},{key:"completePaymentUsingToken",value:function(e){var t=document.getElementById("pay-now-with-token");t.disabled=!0,t.querySelector("svg").classList.remove("hidden"),t.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}},{key:"handle",value:function(){var e=this;this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach((function(t){return t.addEventListener("click",e.handlePaymentUsingToken)})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}});
|
88811
public/main.dart.js
vendored
88811
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -1,12 +1,12 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=a33a5a58bfc6e2174841",
|
||||
"/js/app.js": "/js/app.js?id=1ee684e58f9f6eb754d5",
|
||||
"/css/app.css": "/css/app.css?id=599b11149976e86c83a3",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
|
||||
"/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=206d7de4ac97612980ff",
|
||||
"/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=a376eff2227da398b0ba",
|
||||
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=5469146cd629ea1b5c20",
|
||||
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=edc30120fdc238cd15ea",
|
||||
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=98e406fa8e4db0e93427",
|
||||
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad",
|
||||
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421",
|
||||
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=9418a9c5c137994c4bd8",
|
||||
|
16
resources/js/app.js
vendored
16
resources/js/app.js
vendored
@ -14,20 +14,6 @@ window.axios = require('axios');
|
||||
*/
|
||||
window.valid = require('card-validator');
|
||||
|
||||
/**
|
||||
* Toggle processing overlay.
|
||||
*/
|
||||
window.processingOverlay = (show) => {
|
||||
if (show) {
|
||||
return document
|
||||
.getElementById('processing-overlay')
|
||||
.classList.remove('hidden');
|
||||
}
|
||||
|
||||
return document
|
||||
.getElementById('processing-overlay')
|
||||
.classList.add('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove flashing message div after 3 seconds.
|
||||
@ -35,5 +21,5 @@ window.processingOverlay = (show) => {
|
||||
document.querySelectorAll('.disposable-alert').forEach((element) => {
|
||||
setTimeout(() => {
|
||||
element.remove();
|
||||
}, 3000);
|
||||
}, 5000);
|
||||
});
|
||||
|
@ -42,7 +42,13 @@ class CheckoutCreditCard {
|
||||
payButton.disabled = !Frames.isCardValid();
|
||||
});
|
||||
|
||||
Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED, function (event) {
|
||||
pay.button.disabled = false;
|
||||
});
|
||||
|
||||
Frames.addEventHandler(Frames.Events.CARD_TOKENIZED, function (event) {
|
||||
payButton.disabled = true;
|
||||
|
||||
document.querySelector(
|
||||
'input[name="gateway_response"]'
|
||||
).value = JSON.stringify(event);
|
||||
|
@ -286,7 +286,7 @@ return [
|
||||
'password' => 'Password',
|
||||
'pro_plan_product' => 'Pro Plan',
|
||||
'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan! <br/>
|
||||
Next StepsA payable invoice has been sent to the email
|
||||
Next Steps. A payable invoice has been sent to the email
|
||||
address associated with your account. To unlock all of the awesome
|
||||
Pro features, please follow the instructions on the invoice to pay
|
||||
for a year of Pro-level invoicing.
|
||||
@ -1091,7 +1091,7 @@ return [
|
||||
'invoice_item_fields' => 'Invoice Item Fields',
|
||||
'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.',
|
||||
'recurring_invoice_number' => 'Recurring Number',
|
||||
'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices.',
|
||||
'recurring_invoice_number_prefix_help' => 'Specify a prefix to be added to the invoice number for recurring invoices.',
|
||||
|
||||
// Client Passwords
|
||||
'enable_portal_password' => 'Password Protect Invoices',
|
||||
@ -3383,5 +3383,6 @@ return [
|
||||
'create_webhook_failure' => 'Failed to create Webhook',
|
||||
'number' => 'Number',
|
||||
'payment_message_extended' => 'Thank you for your payment of :amount for :invoice',
|
||||
|
||||
'online_payments_minimum_note' => 'Note: Online payments are supported only if amount is bigger than $1 or currency equivalent.',
|
||||
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method',
|
||||
];
|
||||
|
@ -38,7 +38,7 @@
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->token }}"
|
||||
data-token="{{ $token->hashed_id }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
|
||||
|
@ -141,7 +141,7 @@
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->token }}"
|
||||
data-token="{{ $token->hashed_id }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->token }}"
|
||||
data-token="{{ $token->hashed_id }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.bank_transfer') }} (*{{ $token->meta->last4 }})</span>
|
||||
|
@ -40,7 +40,7 @@
|
||||
<a href="#" @click="{ open = false }" data-company-gateway-id="{{ $payment_method['company_gateway_id'] }}" data-gateway-type-id="{{ $payment_method['gateway_type_id'] }}" class="block px-4 py-2 text-sm leading-5 text-gray-700 dropdown-gateway-button hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" data-cy="payment-method">
|
||||
{{ \App\Models\CompanyGateway::find($payment_method['company_gateway_id'])->firstOrFail()->getConfigField('name') }}
|
||||
</a>
|
||||
@else
|
||||
@elseif($total > 0)
|
||||
<a href="#" @click="{ open = false }" data-company-gateway-id="{{ $payment_method['company_gateway_id'] }}" data-gateway-type-id="{{ $payment_method['gateway_type_id'] }}" class="block px-4 py-2 text-sm leading-5 text-gray-700 dropdown-gateway-button hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" data-cy="payment-method">
|
||||
{{ $payment_method['label'] }}
|
||||
</a>
|
||||
@ -151,6 +151,10 @@
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if(intval($total) == 0)
|
||||
<small>* {{ ctrans('texts.online_payments_minimum_note') }}</small>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,7 +48,8 @@
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.invoice_number_placeholder', ['invoice' => $invoice->number])}} - {{ ctrans('texts.unpaid') }}
|
||||
{{ ctrans('texts.invoice_number_placeholder', ['invoice' => $invoice->number])}}
|
||||
- {{ ctrans('texts.paid') }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
@ -70,7 +71,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700 ml-2">{{ ctrans('texts.page') }}:
|
||||
<span class="text-sm text-gray-700 ml-2">{{ ctrans('texts.page') }}:
|
||||
<span id="current-page-container"></span>
|
||||
<span>{{ strtolower(ctrans('texts.of')) }}</span>
|
||||
<span id="total-page-container"></span>
|
||||
|
@ -21,7 +21,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
|
||||
<a href="{{ route('client.invoice.show', $invoice->hashed_id) }}?mode=portal" class="mr-4 text-primary">
|
||||
<a href="{{ route('client.invoice.show', $invoice->hashed_id) }}?mode=portal"
|
||||
class="mr-4 text-primary">
|
||||
← {{ ctrans('texts.client_portal') }}
|
||||
</a>
|
||||
|
||||
@ -36,15 +37,17 @@
|
||||
</div>
|
||||
</form>
|
||||
@else
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4" translate>
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.invoice_number_placeholder', ['invoice' => $invoice->number])}}
|
||||
- {{ ctrans('texts.unpaid') }}
|
||||
</h3>
|
||||
</div>
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.invoice_number_placeholder', ['invoice' => $invoice->number])}}
|
||||
- {{ ctrans('texts.paid') }}
|
||||
</h3>
|
||||
<a href="{{ route('client.invoice.show', $invoice->hashed_id) }}?mode=portal"
|
||||
class="mr-4 text-primary">
|
||||
← {{ ctrans('texts.client_portal') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -69,6 +69,12 @@
|
||||
@include('portal.ninja2020.components.primary-color')
|
||||
|
||||
<body class="antialiased {{ $custom_body_class ?? '' }}">
|
||||
@if(session()->has('message'))
|
||||
<div class="py-1 text-sm text-center text-white bg-primary disposable-alert">
|
||||
{{ session('message') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@yield('body')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js" data-cfasync="false"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user