mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' of https://github.com/turbo124/invoiceninja into v5-develop
This commit is contained in:
commit
8d490200b6
10
README.md
10
README.md
@ -7,15 +7,7 @@
|
|||||||
|
|
||||||
[](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
[](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.1 RC2!
|
# Invoice Ninja version 5!
|
||||||
|
|
||||||
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
|
## Quick Start
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
5.1.24
|
5.1.29
|
@ -165,6 +165,7 @@ class DemoMode extends Command
|
|||||||
'account_id' => $account->id,
|
'account_id' => $account->id,
|
||||||
'email' => 'small@example.com',
|
'email' => 'small@example.com',
|
||||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||||
|
'email_verified_at' => now(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,6 +200,7 @@ class DemoMode extends Command
|
|||||||
'password' => Hash::make('Password0'),
|
'password' => Hash::make('Password0'),
|
||||||
'account_id' => $account->id,
|
'account_id' => $account->id,
|
||||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||||
|
'email_verified_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$company_token = new CompanyToken;
|
$company_token = new CompanyToken;
|
||||||
|
@ -58,23 +58,16 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
||||||
|
|
||||||
|
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
||||||
|
|
||||||
/* Run hosted specific jobs */
|
/* Run hosted specific jobs */
|
||||||
if (Ninja::isHosted()) {
|
if (Ninja::isHosted()) {
|
||||||
|
|
||||||
$schedule->job(new AdjustEmailQuota())->daily()->withoutOverlapping();
|
$schedule->job(new AdjustEmailQuota)->daily()->withoutOverlapping();
|
||||||
$schedule->job(new SendFailedEmails())->daily()->withoutOverlapping();
|
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||||
|
|
||||||
}
|
}
|
||||||
/* Run queue's with this*/
|
|
||||||
if (Ninja::isSelfHost()) {
|
|
||||||
|
|
||||||
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
|
||||||
|
|
||||||
//we need to add this as we are seeing cached queues mess up the system on first load.
|
|
||||||
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
|
||||||
$schedule->job(new SchedulerCheck)->everyFiveMinutes()->withoutOverlapping();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
47
app/DataMapper/Billing/WebhookConfiguration.php
Normal file
47
app/DataMapper/Billing/WebhookConfiguration.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\DataMapper\Billing;
|
||||||
|
|
||||||
|
|
||||||
|
class WebhookConfiguration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $return_url = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $post_purchase_url = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $post_purchase_headers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $post_purchase_body = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $casts = [
|
||||||
|
'return_url' => 'string',
|
||||||
|
'post_purchase_url' => 'string',
|
||||||
|
'post_purchase_headers' => 'array',
|
||||||
|
'post_purchase_body' => 'object',
|
||||||
|
];
|
||||||
|
}
|
@ -108,6 +108,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $project_number_counter = 1; //@implemented
|
public $project_number_counter = 1; //@implemented
|
||||||
|
|
||||||
public $shared_invoice_quote_counter = false; //@implemented
|
public $shared_invoice_quote_counter = false; //@implemented
|
||||||
|
public $shared_invoice_credit_counter = false; //@implemented
|
||||||
public $recurring_number_prefix = 'R'; //@implemented
|
public $recurring_number_prefix = 'R'; //@implemented
|
||||||
public $reset_counter_frequency_id = '0'; //@implemented
|
public $reset_counter_frequency_id = '0'; //@implemented
|
||||||
public $reset_counter_date = ''; //@implemented
|
public $reset_counter_date = ''; //@implemented
|
||||||
@ -262,6 +263,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $hide_empty_columns_on_pdf = false;
|
public $hide_empty_columns_on_pdf = false;
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
|
'shared_invoice_credit_counter' => 'bool',
|
||||||
'reply_to_name' => 'string',
|
'reply_to_name' => 'string',
|
||||||
'hide_empty_columns_on_pdf' => 'bool',
|
'hide_empty_columns_on_pdf' => 'bool',
|
||||||
'enable_reminder_endless' => 'bool',
|
'enable_reminder_endless' => 'bool',
|
||||||
|
@ -51,7 +51,7 @@ class InvoiceItem
|
|||||||
|
|
||||||
public $custom_value4 = '';
|
public $custom_value4 = '';
|
||||||
|
|
||||||
public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee
|
public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 promo code
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
'type_id' => 'string',
|
'type_id' => 'string',
|
||||||
|
@ -35,7 +35,7 @@ class CompanyFactory
|
|||||||
$company->custom_fields = (object) [];
|
$company->custom_fields = (object) [];
|
||||||
$company->subdomain = '';
|
$company->subdomain = '';
|
||||||
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
|
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
|
||||||
$company->default_password_timeout = 30;
|
$company->default_password_timeout = 30 * 60000;
|
||||||
|
|
||||||
return $company;
|
return $company;
|
||||||
}
|
}
|
||||||
|
@ -142,11 +142,11 @@ class ActivityController extends BaseController
|
|||||||
$pdf = $this->makePdf(null, null, $backup->html_backup);
|
$pdf = $this->makePdf(null, null, $backup->html_backup);
|
||||||
|
|
||||||
if (isset($activity->invoice_id)) {
|
if (isset($activity->invoice_id)) {
|
||||||
$filename = $activity->invoice->number.'.pdf';
|
$filename = $activity->invoice->numberFormatter().'.pdf';
|
||||||
} elseif (isset($activity->quote_id)) {
|
} elseif (isset($activity->quote_id)) {
|
||||||
$filename = $activity->quote->number.'.pdf';
|
$filename = $activity->quote->numberFormatter().'.pdf';
|
||||||
} elseif (isset($activity->credit_id)) {
|
} elseif (isset($activity->credit_id)) {
|
||||||
$filename = $activity->credit->number.'.pdf';
|
$filename = $activity->credit->numberFormatter().'.pdf';
|
||||||
} else {
|
} else {
|
||||||
$filename = 'backup.pdf';
|
$filename = 'backup.pdf';
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,9 @@ use Google_Client;
|
|||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use PragmaRX\Google2FA\Google2FA;
|
||||||
use Turbo124\Beacon\Facades\LightLogs;
|
use Turbo124\Beacon\Facades\LightLogs;
|
||||||
|
|
||||||
class LoginController extends BaseController
|
class LoginController extends BaseController
|
||||||
@ -159,19 +162,48 @@ class LoginController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->attemptLogin($request)) {
|
if ($this->attemptLogin($request)) {
|
||||||
|
|
||||||
LightLogs::create(new LoginSuccess())
|
LightLogs::create(new LoginSuccess())
|
||||||
->increment()
|
->increment()
|
||||||
->batch();
|
->batch();
|
||||||
|
|
||||||
$user = $this->guard()->user();
|
$user = $this->guard()->user();
|
||||||
|
|
||||||
|
//if user has 2fa enabled - lets check this now:
|
||||||
|
|
||||||
|
if($user->google_2fa_secret && $request->has('one_time_password'))
|
||||||
|
{
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
|
||||||
|
if(strlen($request->input('one_time_password')) == 0 || !$google2fa->verifyKey(decrypt($user->google_2fa_secret), $request->input('one_time_password')))
|
||||||
|
{
|
||||||
|
return response()
|
||||||
|
->json(['message' => ctrans('texts.invalid_one_time_password')], 401)
|
||||||
|
->header('X-App-Version', config('ninja.app_version'))
|
||||||
|
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
elseif($user->google_2fa_secret && !$request->has('one_time_password')) {
|
||||||
|
|
||||||
|
return response()
|
||||||
|
->json(['message' => ctrans('texts.invalid_one_time_password')], 401)
|
||||||
|
->header('X-App-Version', config('ninja.app_version'))
|
||||||
|
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
||||||
|
}
|
||||||
|
|
||||||
$user->setCompany($user->account->default_company);
|
$user->setCompany($user->account->default_company);
|
||||||
|
$timeout = auth()->user()->company()->default_password_timeout;
|
||||||
|
|
||||||
|
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||||
|
|
||||||
$cu = CompanyUser::query()
|
$cu = CompanyUser::query()
|
||||||
->where('user_id', auth()->user()->id);
|
->where('user_id', auth()->user()->id);
|
||||||
|
|
||||||
return $this->listResponse($cu);
|
return $this->listResponse($cu);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
LightLogs::create(new LoginFailure())
|
LightLogs::create(new LoginFailure())
|
||||||
->increment()
|
->increment()
|
||||||
->batch();
|
->batch();
|
||||||
@ -182,6 +214,7 @@ class LoginController extends BaseController
|
|||||||
->json(['message' => ctrans('texts.invalid_credentials')], 401)
|
->json(['message' => ctrans('texts.invalid_credentials')], 401)
|
||||||
->header('X-App-Version', config('ninja.app_version'))
|
->header('X-App-Version', config('ninja.app_version'))
|
||||||
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,12 +302,14 @@ class LoginController extends BaseController
|
|||||||
$user = $google->getTokenResponse(request()->input('id_token'));
|
$user = $google->getTokenResponse(request()->input('id_token'));
|
||||||
|
|
||||||
if (is_array($user)) {
|
if (is_array($user)) {
|
||||||
|
|
||||||
$query = [
|
$query = [
|
||||||
'oauth_user_id' => $google->harvestSubField($user),
|
'oauth_user_id' => $google->harvestSubField($user),
|
||||||
'oauth_provider_id'=> 'google',
|
'oauth_provider_id'=> 'google',
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($existing_user = MultiDB::hasUser($query)) {
|
if ($existing_user = MultiDB::hasUser($query)) {
|
||||||
|
|
||||||
Auth::login($existing_user, true);
|
Auth::login($existing_user, true);
|
||||||
$existing_user->setCompany($existing_user->account->default_company);
|
$existing_user->setCompany($existing_user->account->default_company);
|
||||||
|
|
||||||
@ -282,37 +317,39 @@ class LoginController extends BaseController
|
|||||||
->where('user_id', auth()->user()->id);
|
->where('user_id', auth()->user()->id);
|
||||||
|
|
||||||
return $this->listResponse($cu);
|
return $this->listResponse($cu);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
|
|
||||||
$client = new Google_Client();
|
// we are no longer accessing the permissions for gmail - email permissions here
|
||||||
$client->setClientId(config('ninja.auth.google.client_id'));
|
|
||||||
$client->setClientSecret(config('ninja.auth.google.client_secret'));
|
|
||||||
$client->setRedirectUri(config('ninja.app_url'));
|
|
||||||
|
|
||||||
$token = false;
|
// $client = new Google_Client();
|
||||||
|
// $client->setClientId(config('ninja.auth.google.client_id'));
|
||||||
|
// $client->setClientSecret(config('ninja.auth.google.client_secret'));
|
||||||
|
// $client->setRedirectUri(config('ninja.app_url'));
|
||||||
|
|
||||||
try{
|
// $token = false;
|
||||||
$token = $client->authenticate(request()->input('server_auth_code'));
|
|
||||||
}
|
|
||||||
catch(\Exception $e) {
|
|
||||||
|
|
||||||
return response()
|
// try{
|
||||||
->json(['message' => ctrans('texts.invalid_credentials')], 401)
|
// $token = $client->authenticate(request()->input('server_auth_code'));
|
||||||
->header('X-App-Version', config('ninja.app_version'))
|
// }
|
||||||
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
// catch(\Exception $e) {
|
||||||
|
|
||||||
}
|
// return response()
|
||||||
|
// ->json(['message' => ctrans('texts.invalid_credentials')], 401)
|
||||||
|
// ->header('X-App-Version', config('ninja.app_version'))
|
||||||
|
// ->header('X-Api-Version', config('ninja.minimum_client_version'));
|
||||||
|
|
||||||
$refresh_token = '';
|
// }
|
||||||
|
|
||||||
if (array_key_exists('refresh_token', $token)) {
|
// $refresh_token = '';
|
||||||
$refresh_token = $token['refresh_token'];
|
|
||||||
}
|
// if (array_key_exists('refresh_token', $token)) {
|
||||||
|
// $refresh_token = $token['refresh_token'];
|
||||||
|
// }
|
||||||
|
|
||||||
//$access_token = $token['access_token'];
|
|
||||||
|
|
||||||
$name = OAuth::splitName($google->harvestName($user));
|
$name = OAuth::splitName($google->harvestName($user));
|
||||||
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\ClientPortal;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\BillingSubscription;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class BillingSubscriptionPurchaseController extends Controller
|
||||||
|
{
|
||||||
|
public function index(BillingSubscription $billing_subscription)
|
||||||
|
{
|
||||||
|
return view('billing-portal.purchase', [
|
||||||
|
'billing_subscription' => $billing_subscription,
|
||||||
|
'hash' => Str::uuid()->toString(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ use App\Utils\Traits\MakesHash;
|
|||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
@ -237,11 +238,18 @@ class PaymentController extends Controller
|
|||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hash_data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals];
|
||||||
|
|
||||||
|
if ($request->query('hash')) {
|
||||||
|
$hash_data['billing_context'] = Cache::get($request->query('hash'));
|
||||||
|
}
|
||||||
|
|
||||||
$payment_hash = new PaymentHash;
|
$payment_hash = new PaymentHash;
|
||||||
$payment_hash->hash = Str::random(128);
|
$payment_hash->hash = Str::random(128);
|
||||||
$payment_hash->data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals];
|
$payment_hash->data = $hash_data;
|
||||||
$payment_hash->fee_total = $fee_totals;
|
$payment_hash->fee_total = $fee_totals;
|
||||||
$payment_hash->fee_invoice_id = $first_invoice->id;
|
$payment_hash->fee_invoice_id = $first_invoice->id;
|
||||||
|
|
||||||
$payment_hash->save();
|
$payment_hash->save();
|
||||||
|
|
||||||
$totals = [
|
$totals = [
|
||||||
|
@ -21,9 +21,9 @@ use Illuminate\Http\Request;
|
|||||||
class ConnectedAccountController extends BaseController
|
class ConnectedAccountController extends BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $entity_type = CompanyUser::class;
|
protected $entity_type = User::class;
|
||||||
|
|
||||||
protected $entity_transformer = CompanyUserTransformer::class;
|
protected $entity_transformer = UserTransformer::class;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -89,23 +89,8 @@ class ConnectedAccountController extends BaseController
|
|||||||
|
|
||||||
$user = $google->getTokenResponse(request()->input('id_token'));
|
$user = $google->getTokenResponse(request()->input('id_token'));
|
||||||
|
|
||||||
if (is_array($user)) {
|
|
||||||
|
|
||||||
$query = [
|
|
||||||
'oauth_user_id' => $google->harvestSubField($user),
|
|
||||||
'oauth_provider_id'=> 'google',
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Cannot allow duplicates! */
|
|
||||||
if ($existing_user = MultiDB::hasUser($query)) {
|
|
||||||
return response()
|
|
||||||
->json(['message' => 'User already exists in system.'], 401)
|
|
||||||
->header('X-App-Version', config('ninja.app_version'))
|
|
||||||
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
|
|
||||||
$client = new Google_Client();
|
$client = new Google_Client();
|
||||||
$client->setClientId(config('ninja.auth.google.client_id'));
|
$client->setClientId(config('ninja.auth.google.client_id'));
|
||||||
$client->setClientSecret(config('ninja.auth.google.client_secret'));
|
$client->setClientSecret(config('ninja.auth.google.client_secret'));
|
||||||
@ -118,7 +103,6 @@ class ConnectedAccountController extends BaseController
|
|||||||
$refresh_token = $token['refresh_token'];
|
$refresh_token = $token['refresh_token'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$connected_account = [
|
$connected_account = [
|
||||||
'password' => '',
|
'password' => '',
|
||||||
'email' => $google->harvestEmail($user),
|
'email' => $google->harvestEmail($user),
|
||||||
@ -136,7 +120,8 @@ class ConnectedAccountController extends BaseController
|
|||||||
//$ct = CompanyUser::whereUserId(auth()->user()->id);
|
//$ct = CompanyUser::whereUserId(auth()->user()->id);
|
||||||
//return $this->listResponse($ct);
|
//return $this->listResponse($ct);
|
||||||
|
|
||||||
return $this->listResponse(auth()->user());
|
return $this->itemResponse(auth()->user());
|
||||||
|
// return $this->listResponse(auth()->user());
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()
|
return response()
|
||||||
|
@ -18,6 +18,7 @@ use App\Models\Invoice;
|
|||||||
use App\Models\InvoiceInvitation;
|
use App\Models\InvoiceInvitation;
|
||||||
use App\Services\PdfMaker\Design;
|
use App\Services\PdfMaker\Design;
|
||||||
use App\Services\PdfMaker\PdfMaker;
|
use App\Services\PdfMaker\PdfMaker;
|
||||||
|
use App\Utils\HostedPDF\NinjaPdf;
|
||||||
use App\Utils\HtmlEngine;
|
use App\Utils\HtmlEngine;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\PhantomJS\Phantom;
|
use App\Utils\PhantomJS\Phantom;
|
||||||
@ -134,6 +135,10 @@ class PreviewController extends BaseController
|
|||||||
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(config('ninja.invoiceninja_hosted_pdf_generation')){
|
||||||
|
return (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||||
|
}
|
||||||
|
|
||||||
//else
|
//else
|
||||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
||||||
|
|
||||||
@ -217,6 +222,10 @@ class PreviewController extends BaseController
|
|||||||
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(config('ninja.invoiceninja_hosted_pdf_generation')){
|
||||||
|
return (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||||
|
}
|
||||||
|
|
||||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
||||||
|
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
@ -47,17 +47,28 @@ class TwoFactorController extends BaseController
|
|||||||
|
|
||||||
public function enableTwoFactor()
|
public function enableTwoFactor()
|
||||||
{
|
{
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$secret = request()->input('secret');
|
$secret = request()->input('secret');
|
||||||
$oneTimePassword = request()->input('one_time_password');
|
$oneTimePassword = request()->input('one_time_password');
|
||||||
|
|
||||||
if (! $secret || ! \Google2FA::verifyKey($secret, $oneTimePassword)) {
|
if($google2fa->verifyKey($secret, $oneTimePassword) && $user->phone && $user->email_verified_at){
|
||||||
return response()->json('message' > ctrans('texts.invalid_one_time_password'));
|
|
||||||
} elseif (! $user->google_2fa_secret && $user->phone && $user->confirmed) {
|
|
||||||
$user->google_2fa_secret = encrypt($secret);
|
$user->google_2fa_secret = encrypt($secret);
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
|
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
|
||||||
|
|
||||||
|
} elseif (! $secret || ! $google2fa->verifyKey($secret, $oneTimePassword)) {
|
||||||
|
|
||||||
|
return response()->json(['message' => ctrans('texts.invalid_one_time_password')], 400);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
|
return response()->json(['message' => 'No phone record or user is not confirmed'], 400);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
171
app/Http/Livewire/BillingPortalPurchase.php
Normal file
171
app/Http/Livewire/BillingPortalPurchase.php
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Factory\ClientFactory;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Repositories\ClientContactRepository;
|
||||||
|
use App\Repositories\ClientRepository;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class BillingPortalPurchase extends Component
|
||||||
|
{
|
||||||
|
public $hash;
|
||||||
|
|
||||||
|
public $heading_text = 'Log in';
|
||||||
|
|
||||||
|
public $email;
|
||||||
|
|
||||||
|
public $password;
|
||||||
|
|
||||||
|
public $billing_subscription;
|
||||||
|
|
||||||
|
public $contact;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'email' => ['required', 'email'],
|
||||||
|
];
|
||||||
|
|
||||||
|
public $company_gateway_id;
|
||||||
|
|
||||||
|
public $payment_method_id;
|
||||||
|
|
||||||
|
public $steps = [
|
||||||
|
'passed_email' => false,
|
||||||
|
'existing_user' => false,
|
||||||
|
'fetched_payment_methods' => false,
|
||||||
|
'fetched_client' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
public $methods = [];
|
||||||
|
|
||||||
|
public $invoice;
|
||||||
|
|
||||||
|
public $coupon;
|
||||||
|
|
||||||
|
public function authenticate()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$contact = ClientContact::where('email', $this->email)->first();
|
||||||
|
|
||||||
|
if ($contact && $this->steps['existing_user'] === false) {
|
||||||
|
return $this->steps['existing_user'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contact && $this->steps['existing_user']) {
|
||||||
|
$attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password]);
|
||||||
|
|
||||||
|
return $attempt
|
||||||
|
? $this->getPaymentMethods($contact)
|
||||||
|
: session()->flash('message', 'These credentials do not match our records.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->steps['existing_user'] = false;
|
||||||
|
|
||||||
|
$contact = $this->createBlankClient();
|
||||||
|
|
||||||
|
if ($contact && $contact instanceof ClientContact) {
|
||||||
|
$this->getPaymentMethods($contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createBlankClient()
|
||||||
|
{
|
||||||
|
$company = $this->billing_subscription->company;
|
||||||
|
$user = $this->billing_subscription->user;
|
||||||
|
|
||||||
|
$client_repo = new ClientRepository(new ClientContactRepository());
|
||||||
|
|
||||||
|
$client = $client_repo->save([
|
||||||
|
'name' => 'Client Name',
|
||||||
|
'contacts' => [
|
||||||
|
['email' => $this->email],
|
||||||
|
]
|
||||||
|
], ClientFactory::create($company->id, $user->id));
|
||||||
|
|
||||||
|
return $client->contacts->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPaymentMethods(ClientContact $contact): self
|
||||||
|
{
|
||||||
|
$this->steps['fetched_payment_methods'] = true;
|
||||||
|
|
||||||
|
$this->methods = $contact->client->service()->getPaymentMethods(1000);
|
||||||
|
|
||||||
|
$this->heading_text = 'Pick a payment method';
|
||||||
|
|
||||||
|
Auth::guard('contact')->login($contact);
|
||||||
|
|
||||||
|
$this->contact = $contact;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
|
||||||
|
{
|
||||||
|
$this->company_gateway_id = $company_gateway_id;
|
||||||
|
$this->payment_method_id = $gateway_type_id;
|
||||||
|
|
||||||
|
$this->handleBeforePaymentEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleBeforePaymentEvents()
|
||||||
|
{
|
||||||
|
|
||||||
|
//stubs
|
||||||
|
$data = [
|
||||||
|
'client_id' => $this->contact->client->id,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'invitations' => [[
|
||||||
|
'key' => '',
|
||||||
|
'client_contact_id' => $this->contact->hashed_id,
|
||||||
|
]],
|
||||||
|
'user_input_promo_code' => $this->coupon,
|
||||||
|
'quantity' => 1, // Option to increase quantity
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->invoice = $this->billing_subscription
|
||||||
|
->service()
|
||||||
|
->createInvoice($data)
|
||||||
|
->service()
|
||||||
|
->markSent()
|
||||||
|
->save();
|
||||||
|
|
||||||
|
Cache::put($this->hash, [
|
||||||
|
'email' => $this->email ?? $this->contact->email,
|
||||||
|
'client_id' => $this->contact->client->id,
|
||||||
|
'invoice_id' => $this->invoice->id],
|
||||||
|
now()->addMinutes(60)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->emit('beforePaymentEventsCompleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//this isn't managed here - this is taken care of in the BS
|
||||||
|
public function applyCouponCode()
|
||||||
|
{
|
||||||
|
dd('Applying coupon code: ' . $this->coupon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
if ($this->contact instanceof ClientContact) {
|
||||||
|
$this->getPaymentMethods($this->contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
return render('components.livewire.billing-portal-purchase');
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,7 @@ class PasswordProtection
|
|||||||
if($timeout == 0)
|
if($timeout == 0)
|
||||||
$timeout = null;
|
$timeout = null;
|
||||||
else
|
else
|
||||||
$timeout = now()->addMinutes($timeout);
|
$timeout = now()->addMinutes($timeout/60000);
|
||||||
|
|
||||||
if (Cache::get(auth()->user()->hashed_id.'_logged_in')) {
|
if (Cache::get(auth()->user()->hashed_id.'_logged_in')) {
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ class PasswordProtection
|
|||||||
];
|
];
|
||||||
|
|
||||||
//If OAuth and user also has a password set - check both
|
//If OAuth and user also has a password set - check both
|
||||||
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
|
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
|
||||||
|
|
||||||
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
@ -54,7 +54,7 @@ class QueryLogging
|
|||||||
nlog($request->method().' - '.$request->url().": $count queries - ".$time);
|
nlog($request->method().' - '.$request->url().": $count queries - ".$time);
|
||||||
|
|
||||||
// if($count > 50)
|
// if($count > 50)
|
||||||
// Log::nlog($queries);
|
//nlog($queries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ use App\Models\Client;
|
|||||||
use App\Models\GroupSetting;
|
use App\Models\GroupSetting;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class StoreClientRequest extends Request
|
class StoreClientRequest extends Request
|
||||||
{
|
{
|
||||||
@ -48,7 +49,6 @@ class StoreClientRequest extends Request
|
|||||||
|
|
||||||
/* Ensure we have a client name, and that all emails are unique*/
|
/* Ensure we have a client name, and that all emails are unique*/
|
||||||
//$rules['name'] = 'required|min:1';
|
//$rules['name'] = 'required|min:1';
|
||||||
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
|
|
||||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||||
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
||||||
$rules['contacts.*.password'] = [
|
$rules['contacts.*.password'] = [
|
||||||
@ -66,6 +66,10 @@ class StoreClientRequest extends Request
|
|||||||
$rules['hosted_clients'] = new CanStoreClientsRule($this->company_id);
|
$rules['hosted_clients'] = new CanStoreClientsRule($this->company_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rules['number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
|
||||||
|
$rules['id_number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
|
||||||
|
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +126,7 @@ class StoreClientRequest extends Request
|
|||||||
public function messages()
|
public function messages()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
// 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
|
||||||
//'required' => trans('validation.required', ['attribute' => 'email']),
|
//'required' => trans('validation.required', ['attribute' => 'email']),
|
||||||
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
||||||
];
|
];
|
||||||
|
@ -16,6 +16,7 @@ use App\Http\Requests\Request;
|
|||||||
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateClientRequest extends Request
|
class UpdateClientRequest extends Request
|
||||||
{
|
{
|
||||||
@ -52,7 +53,14 @@ class UpdateClientRequest extends Request
|
|||||||
$rules['country_id'] = 'integer|nullable';
|
$rules['country_id'] = 'integer|nullable';
|
||||||
$rules['shipping_country_id'] = 'integer|nullable';
|
$rules['shipping_country_id'] = 'integer|nullable';
|
||||||
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
|
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
|
||||||
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
|
//$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
|
||||||
|
|
||||||
|
if($this->id_number)
|
||||||
|
$rules['id_number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id)->ignore($this->client->id);
|
||||||
|
|
||||||
|
if($this->number)
|
||||||
|
$rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id)->ignore($this->client->id);
|
||||||
|
|
||||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||||
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
||||||
$rules['contacts.*.password'] = [
|
$rules['contacts.*.password'] = [
|
||||||
@ -72,7 +80,6 @@ class UpdateClientRequest extends Request
|
|||||||
public function messages()
|
public function messages()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
|
||||||
'email' => ctrans('validation.email', ['attribute' => 'email']),
|
'email' => ctrans('validation.email', ['attribute' => 'email']),
|
||||||
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
|
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
|
||||||
'required' => ctrans('validation.required', ['attribute' => 'email']),
|
'required' => ctrans('validation.required', ['attribute' => 'email']),
|
||||||
|
@ -16,6 +16,7 @@ use App\Http\Requests\Request;
|
|||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\CleanLineItems;
|
use App\Utils\Traits\CleanLineItems;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateCreditRequest extends Request
|
class UpdateCreditRequest extends Request
|
||||||
{
|
{
|
||||||
@ -52,9 +53,8 @@ class UpdateCreditRequest extends Request
|
|||||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->input('number')) {
|
if($this->number)
|
||||||
$rules['number'] = 'unique:credits,number,'.$this->id.',id,company_id,'.$this->credit->company_id;
|
$rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id);
|
||||||
}
|
|
||||||
|
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
|
|
||||||
|
@ -35,9 +35,8 @@ class StoreExpenseRequest extends Request
|
|||||||
{
|
{
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
if (isset($this->number)) {
|
if ($this->number)
|
||||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
||||||
}
|
|
||||||
|
|
||||||
if(!empty($this->client_id))
|
if(!empty($this->client_id))
|
||||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||||
|
@ -16,6 +16,7 @@ use App\Http\ValidationRules\Invoice\LockedInvoiceRule;
|
|||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\CleanLineItems;
|
use App\Utils\Traits\CleanLineItems;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateInvoiceRequest extends Request
|
class UpdateInvoiceRequest extends Request
|
||||||
{
|
{
|
||||||
@ -49,10 +50,8 @@ class UpdateInvoiceRequest extends Request
|
|||||||
|
|
||||||
$rules['id'] = new LockedInvoiceRule($this->invoice);
|
$rules['id'] = new LockedInvoiceRule($this->invoice);
|
||||||
|
|
||||||
// if ($this->input('number') && strlen($this->input('number')) >= 1) {
|
if($this->number)
|
||||||
if ($this->input('number')) {
|
$rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id);
|
||||||
$rules['number'] = 'unique:invoices,number,'.$this->id.',id,company_id,'.$this->invoice->company_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ use App\Http\ValidationRules\PaymentAppliedValidAmount;
|
|||||||
use App\Http\ValidationRules\ValidCreditsPresentRule;
|
use App\Http\ValidationRules\ValidCreditsPresentRule;
|
||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdatePaymentRequest extends Request
|
class UpdatePaymentRequest extends Request
|
||||||
{
|
{
|
||||||
@ -35,12 +36,14 @@ class UpdatePaymentRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->payment->company_id,
|
|
||||||
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
|
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
|
||||||
'invoices.*.invoice_id' => 'distinct',
|
'invoices.*.invoice_id' => 'distinct',
|
||||||
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if($this->number)
|
||||||
|
$rules['number'] = Rule::unique('payments')->where('company_id', auth()->user()->company()->id)->ignore($this->payment->id);
|
||||||
|
|
||||||
if ($this->input('documents') && is_array($this->input('documents'))) {
|
if ($this->input('documents') && is_array($this->input('documents'))) {
|
||||||
$documents = count($this->input('documents'));
|
$documents = count($this->input('documents'));
|
||||||
|
|
||||||
@ -68,9 +71,9 @@ class UpdatePaymentRequest extends Request
|
|||||||
unset($input['amount']);
|
unset($input['amount']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($input['number'])) {
|
// if (isset($input['number'])) {
|
||||||
unset($input['number']);
|
// unset($input['number']);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
|
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
|
||||||
foreach ($input['invoices'] as $key => $value) {
|
foreach ($input['invoices'] as $key => $value) {
|
||||||
|
@ -15,6 +15,7 @@ use App\Http\Requests\Request;
|
|||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\CleanLineItems;
|
use App\Utils\Traits\CleanLineItems;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateQuoteRequest extends Request
|
class UpdateQuoteRequest extends Request
|
||||||
{
|
{
|
||||||
@ -46,9 +47,8 @@ class UpdateQuoteRequest extends Request
|
|||||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->input('number')) {
|
if($this->number)
|
||||||
$rules['number'] = 'unique:quotes,number,'.$this->id.',id,company_id,'.$this->quote->company_id;
|
$rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
|
||||||
}
|
|
||||||
|
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ use App\Utils\Traits\ChecksEntityStatus;
|
|||||||
use App\Utils\Traits\CleanLineItems;
|
use App\Utils\Traits\CleanLineItems;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateRecurringInvoiceRequest extends Request
|
class UpdateRecurringInvoiceRequest extends Request
|
||||||
{
|
{
|
||||||
@ -47,9 +48,9 @@ class UpdateRecurringInvoiceRequest extends Request
|
|||||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->input('number')) {
|
if($this->number)
|
||||||
$rules['number'] = 'unique:recurring_invoices,number,'.$this->recurring_invoice->id.',id,company_id,'.$this->recurring_invoice->company_id;
|
$rules['number'] = Rule::unique('recurring_invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_invoice->id);
|
||||||
}
|
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace App\Http\Requests\RecurringQuote;
|
|||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\CleanLineItems;
|
use App\Utils\Traits\CleanLineItems;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateRecurringQuoteRequest extends Request
|
class UpdateRecurringQuoteRequest extends Request
|
||||||
{
|
{
|
||||||
@ -47,6 +48,9 @@ class UpdateRecurringQuoteRequest extends Request
|
|||||||
|
|
||||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||||
|
|
||||||
|
if($this->number)
|
||||||
|
$rules['number'] = Rule::unique('recurring_quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_quote->id);
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Http\Requests\TaxRate;
|
namespace App\Http\Requests\TaxRate;
|
||||||
|
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateTaxRateRequest extends Request
|
class UpdateTaxRateRequest extends Request
|
||||||
{
|
{
|
||||||
@ -27,10 +28,14 @@ class UpdateTaxRateRequest extends Request
|
|||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
$rules = [];
|
||||||
// 'name' => 'unique:tax_rates,name,'.$this->tax_rate->name.',id,company_id,'.auth()->user()->companyId(),
|
|
||||||
'name' => 'unique:tax_rates,name,'.$this->id.',id,company_id,'.$this->company_id,
|
$rules['rate'] = 'numeric';
|
||||||
'rate' => 'numeric',
|
|
||||||
];
|
if($this->number)
|
||||||
|
$rules['number'] = Rule::unique('tax_rates')->where('company_id', auth()->user()->company()->id)->ignore($this->tax_rate->id);
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,6 @@ class ReconfirmUserRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
return auth()->user()->id == $this->user->id;
|
return auth()->user()->id == $this->user->id || auth()->user()->isAdmin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
app/Http/Requests/Vendor/UpdateVendorRequest.php
vendored
19
app/Http/Requests/Vendor/UpdateVendorRequest.php
vendored
@ -14,6 +14,7 @@ namespace App\Http\Requests\Vendor;
|
|||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
use App\Utils\Traits\ChecksEntityStatus;
|
use App\Utils\Traits\ChecksEntityStatus;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class UpdateVendorRequest extends Request
|
class UpdateVendorRequest extends Request
|
||||||
{
|
{
|
||||||
@ -35,18 +36,15 @@ class UpdateVendorRequest extends Request
|
|||||||
/* Ensure we have a client name, and that all emails are unique*/
|
/* Ensure we have a client name, and that all emails are unique*/
|
||||||
|
|
||||||
$rules['country_id'] = 'integer|nullable';
|
$rules['country_id'] = 'integer|nullable';
|
||||||
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
|
|
||||||
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
|
if($this->number)
|
||||||
|
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
|
||||||
|
|
||||||
|
if($this->id_number)
|
||||||
|
$rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
|
||||||
|
|
||||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||||
|
|
||||||
$contacts = request('contacts');
|
|
||||||
|
|
||||||
if (is_array($contacts)) {
|
|
||||||
// for ($i = 0; $i < count($contacts); $i++) {
|
|
||||||
// // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id);
|
|
||||||
// //$rules['contacts.' . $i . '.email'] = 'nullable|email';
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
@ -54,7 +52,6 @@ class UpdateVendorRequest extends Request
|
|||||||
public function messages()
|
public function messages()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
|
||||||
'email' => ctrans('validation.email', ['attribute' => 'email']),
|
'email' => ctrans('validation.email', ['attribute' => 'email']),
|
||||||
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
|
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
|
||||||
'required' => ctrans('validation.required', ['attribute' => 'email']),
|
'required' => ctrans('validation.required', ['attribute' => 'email']),
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs\Entity;
|
namespace App\Jobs\Entity;
|
||||||
|
|
||||||
|
use App\Models\Account;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
use App\Models\CreditInvitation;
|
use App\Models\CreditInvitation;
|
||||||
use App\Models\Design;
|
use App\Models\Design;
|
||||||
@ -24,6 +25,7 @@ use App\Models\RecurringInvoiceInvitation;
|
|||||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||||
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
|
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
|
||||||
|
use App\Utils\HostedPDF\NinjaPdf;
|
||||||
use App\Utils\HtmlEngine;
|
use App\Utils\HtmlEngine;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\PhantomJS\Phantom;
|
use App\Utils\PhantomJS\Phantom;
|
||||||
@ -114,10 +116,13 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
$entity_design_id = 'invoice_design_id';
|
$entity_design_id = 'invoice_design_id';
|
||||||
}
|
}
|
||||||
|
|
||||||
$file_path = $path.$this->entity->number.'.pdf';
|
$file_path = $path.$this->entity->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey($this->entity->client->getSetting($entity_design_id));
|
$entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey($this->entity->client->getSetting($entity_design_id));
|
||||||
|
|
||||||
|
if(!$this->company->account->hasFeature(Account::FEATURE_DIFFERENT_DESIGNS))
|
||||||
|
$entity_design_id = 2;
|
||||||
|
|
||||||
$design = Design::find($entity_design_id);
|
$design = Design::find($entity_design_id);
|
||||||
$html = new HtmlEngine($this->invitation);
|
$html = new HtmlEngine($this->invitation);
|
||||||
|
|
||||||
@ -156,7 +161,13 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
$pdf = null;
|
$pdf = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
|
|
||||||
|
if(config('ninja.invoiceninja_hosted_pdf_generation')){
|
||||||
|
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
|
||||||
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
nlog(print_r($e->getMessage(), 1));
|
nlog(print_r($e->getMessage(), 1));
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ use App\Events\Invoice\InvoiceWasEmailed;
|
|||||||
use App\Jobs\Entity\EmailEntity;
|
use App\Jobs\Entity\EmailEntity;
|
||||||
use App\Jobs\Util\WebHookHandler;
|
use App\Jobs\Util\WebHookHandler;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Account;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Webhook;
|
use App\Models\Webhook;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
@ -207,7 +208,7 @@ class SendReminders implements ShouldQueue
|
|||||||
$invoice->invitations->each(function ($invitation) use ($template, $invoice) {
|
$invoice->invitations->each(function ($invitation) use ($template, $invoice) {
|
||||||
|
|
||||||
//only send if enable_reminder setting is toggled to yes
|
//only send if enable_reminder setting is toggled to yes
|
||||||
if ($this->checkSendSetting($invoice, $template)) {
|
if ($this->checkSendSetting($invoice, $template) && $invoice->company->account->hasFeature(Account::FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||||
nlog("firing email");
|
nlog("firing email");
|
||||||
|
|
||||||
EmailEntity::dispatchNow($invitation, $invitation->company, $template);
|
EmailEntity::dispatchNow($invitation, $invitation->company, $template);
|
||||||
|
@ -80,9 +80,10 @@ class UserEmailChanged implements ShouldQueue
|
|||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
$nmo->to_user = $this->new_user;
|
// $nmo->to_user = $this->new_user;
|
||||||
|
// NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
$this->new_user->service()->invite($this->company);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,8 +214,13 @@ class Import implements ShouldQueue
|
|||||||
// if($check_data['status'] == 'errors')
|
// if($check_data['status'] == 'errors')
|
||||||
// throw new ProcessingMigrationArchiveFailed(implode("\n", $check_data));
|
// throw new ProcessingMigrationArchiveFailed(implode("\n", $check_data));
|
||||||
|
|
||||||
Mail::to($this->user->email, $this->user->name())
|
try{
|
||||||
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
Mail::to($this->user->email, $this->user->name())
|
||||||
|
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
||||||
|
}
|
||||||
|
catch(\Exception $e) {
|
||||||
|
nlog($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
/*After a migration first some basic jobs to ensure the system is up to date*/
|
/*After a migration first some basic jobs to ensure the system is up to date*/
|
||||||
VersionCheck::dispatch();
|
VersionCheck::dispatch();
|
||||||
|
@ -88,14 +88,14 @@ class CreditEmailEngine extends BaseEmailEngine
|
|||||||
->setViewText(ctrans('texts.view_credit'))
|
->setViewText(ctrans('texts.view_credit'))
|
||||||
->setInvitation($this->invitation);
|
->setInvitation($this->invitation);
|
||||||
|
|
||||||
if ($this->client->getSetting('pdf_email_attachment') !== false) {
|
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||||
$this->setAttachments([$this->credit->pdf_file_path()]);
|
$this->setAttachments([$this->credit->pdf_file_path()]);
|
||||||
|
|
||||||
// $this->setAttachments(['path' => $this->credit->pdf_file_path(), 'name' => basename($this->credit->pdf_file_path())]);
|
// $this->setAttachments(['path' => $this->credit->pdf_file_path(), 'name' => basename($this->credit->pdf_file_path())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//attach third party documents
|
//attach third party documents
|
||||||
if($this->client->getSetting('document_email_attachment') !== false){
|
if($this->client->getSetting('document_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
|
||||||
|
|
||||||
// Storage::url
|
// Storage::url
|
||||||
foreach($this->credit->documents as $document){
|
foreach($this->credit->documents as $document){
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Mail\Engine;
|
namespace App\Mail\Engine;
|
||||||
|
|
||||||
use App\DataMapper\EmailTemplateDefaults;
|
use App\DataMapper\EmailTemplateDefaults;
|
||||||
|
use App\Models\Account;
|
||||||
use App\Utils\HtmlEngine;
|
use App\Utils\HtmlEngine;
|
||||||
use App\Utils\Number;
|
use App\Utils\Number;
|
||||||
|
|
||||||
@ -97,14 +98,14 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
|||||||
->setViewText(ctrans('texts.view_invoice'))
|
->setViewText(ctrans('texts.view_invoice'))
|
||||||
->setInvitation($this->invitation);
|
->setInvitation($this->invitation);
|
||||||
|
|
||||||
if ($this->client->getSetting('pdf_email_attachment') !== false) {
|
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||||
$this->setAttachments([$this->invoice->pdf_file_path()]);
|
$this->setAttachments([$this->invoice->pdf_file_path()]);
|
||||||
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//attach third party documents
|
//attach third party documents
|
||||||
if($this->client->getSetting('document_email_attachment') !== false){
|
if($this->client->getSetting('document_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
|
||||||
|
|
||||||
// Storage::url
|
// Storage::url
|
||||||
foreach($this->invoice->documents as $document){
|
foreach($this->invoice->documents as $document){
|
||||||
|
@ -89,14 +89,14 @@ class QuoteEmailEngine extends BaseEmailEngine
|
|||||||
->setInvitation($this->invitation);
|
->setInvitation($this->invitation);
|
||||||
|
|
||||||
|
|
||||||
if ($this->client->getSetting('pdf_email_attachment') !== false) {
|
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||||
$this->setAttachments([$this->quote->pdf_file_path()]);
|
$this->setAttachments([$this->quote->pdf_file_path()]);
|
||||||
//$this->setAttachments(['path' => $this->quote->pdf_file_path(), 'name' => basename($this->quote->pdf_file_path())]);
|
//$this->setAttachments(['path' => $this->quote->pdf_file_path(), 'name' => basename($this->quote->pdf_file_path())]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//attach third party documents
|
//attach third party documents
|
||||||
if($this->client->getSetting('document_email_attachment') !== false){
|
if($this->client->getSetting('document_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
|
||||||
|
|
||||||
// Storage::url
|
// Storage::url
|
||||||
foreach($this->quote->documents as $document){
|
foreach($this->quote->documents as $document){
|
||||||
|
@ -186,6 +186,15 @@ class BaseModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getFileName($extension = 'pdf')
|
public function getFileName($extension = 'pdf')
|
||||||
{
|
{
|
||||||
return $this->number.'.'.$extension;
|
return $this->numberFormatter().'.'.$extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function numberFormatter()
|
||||||
|
{
|
||||||
|
$formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $this->number);
|
||||||
|
// Remove any runs of periods (thanks falstro!)
|
||||||
|
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
|
||||||
|
|
||||||
|
return $formatted_number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Services\BillingSubscription\BillingSubscriptionService;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
@ -53,6 +54,11 @@ class BillingSubscription extends BaseModel
|
|||||||
'deleted_at' => 'timestamp',
|
'deleted_at' => 'timestamp',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return new BillingSubscriptionService($this);
|
||||||
|
}
|
||||||
|
|
||||||
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Company::class);
|
return $this->belongsTo(Company::class);
|
||||||
@ -67,5 +73,4 @@ class BillingSubscription extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(Product::class);
|
return $this->belongsTo(Product::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -304,6 +304,10 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $this->company->settings->{$setting};
|
return $this->company->settings->{$setting};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elseif( property_exists(CompanySettings::defaults(), $setting) ) {
|
||||||
|
return CompanySettings::defaults()->{$setting};
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
// throw new \Exception("Settings corrupted", 1);
|
// throw new \Exception("Settings corrupted", 1);
|
||||||
|
@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
use Laracasts\Presenter\PresentableTrait;
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
class Company extends BaseModel
|
class Company extends BaseModel
|
||||||
{
|
{
|
||||||
@ -286,7 +287,7 @@ class Company extends BaseModel
|
|||||||
*/
|
*/
|
||||||
public function country()
|
public function country()
|
||||||
{
|
{
|
||||||
//return $this->belongsTo(Country::class);
|
// return $this->belongsTo(Country::class);
|
||||||
return Country::find($this->settings->country_id);
|
return Country::find($this->settings->country_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,12 +343,13 @@ class Company extends BaseModel
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return BelongsTo
|
|
||||||
*/
|
|
||||||
public function currency()
|
public function currency()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Currency::class);
|
$currencies = Cache::get('currencies');
|
||||||
|
|
||||||
|
return $currencies->filter(function ($item) {
|
||||||
|
return $item->id == $this->settings->currency_id;
|
||||||
|
})->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,9 +248,9 @@ class Credit extends BaseModel
|
|||||||
|
|
||||||
public function pdf_file_path($invitation = null)
|
public function pdf_file_path($invitation = null)
|
||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->client->credit_filepath().$this->number.'.pdf');
|
$storage_path = Storage::url($this->client->credit_filepath().$this->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (Storage::exists($this->client->credit_filepath().$this->number.'.pdf')) {
|
if (Storage::exists($this->client->credit_filepath().$this->numberFormatter().'.pdf')) {
|
||||||
return $storage_path;
|
return $storage_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,9 +126,9 @@ class CreditInvitation extends BaseModel
|
|||||||
|
|
||||||
public function pdf_file_path()
|
public function pdf_file_path()
|
||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->credit->client->quote_filepath().$this->credit->number.'.pdf');
|
$storage_path = Storage::url($this->credit->client->quote_filepath().$this->credit->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->credit->client->credit_filepath().$this->credit->number.'.pdf')) {
|
if (! Storage::exists($this->credit->client->credit_filepath().$this->credit->numberFormatter().'.pdf')) {
|
||||||
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars()));
|
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars()));
|
||||||
CreateEntityPdf::dispatchNow($this);
|
CreateEntityPdf::dispatchNow($this);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class Gateway extends StaticModel
|
|||||||
* Returns an array of methods and the gatewaytypes possible
|
* Returns an array of methods and the gatewaytypes possible
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*///todo remove methods replace with gatewaytype:: and then nest refund / token billing
|
*/
|
||||||
public function getMethods()
|
public function getMethods()
|
||||||
{
|
{
|
||||||
switch ($this->id) {
|
switch ($this->id) {
|
||||||
|
@ -388,9 +388,9 @@ class Invoice extends BaseModel
|
|||||||
$invitation = $this->invitations->first();
|
$invitation = $this->invitations->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
$storage_path = Storage::$type($this->client->invoice_filepath().$this->number.'.pdf');
|
$storage_path = Storage::$type($this->client->invoice_filepath().$this->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->client->invoice_filepath().$this->number.'.pdf')) {
|
if (! Storage::exists($this->client->invoice_filepath().$this->numberFormatter().'.pdf')) {
|
||||||
event(new InvoiceWasUpdated($this, $this->company, Ninja::eventVars()));
|
event(new InvoiceWasUpdated($this, $this->company, Ninja::eventVars()));
|
||||||
CreateEntityPdf::dispatchNow($invitation);
|
CreateEntityPdf::dispatchNow($invitation);
|
||||||
}
|
}
|
||||||
|
@ -140,9 +140,9 @@ class InvoiceInvitation extends BaseModel
|
|||||||
|
|
||||||
public function pdf_file_path()
|
public function pdf_file_path()
|
||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->invoice->client->invoice_filepath().$this->invoice->number.'.pdf');
|
$storage_path = Storage::url($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->invoice->client->invoice_filepath().$this->invoice->number.'.pdf')) {
|
if (! Storage::exists($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf')) {
|
||||||
event(new InvoiceWasUpdated($this->invoice, $this->company, Ninja::eventVars()));
|
event(new InvoiceWasUpdated($this->invoice, $this->company, Ninja::eventVars()));
|
||||||
CreateEntityPdf::dispatchNow($this);
|
CreateEntityPdf::dispatchNow($this);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ class Project extends BaseModel
|
|||||||
'custom_value4',
|
'custom_value4',
|
||||||
'assigned_user_id',
|
'assigned_user_id',
|
||||||
'color',
|
'color',
|
||||||
|
'number',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getEntityType()
|
public function getEntityType()
|
||||||
|
@ -208,11 +208,11 @@ class Quote extends BaseModel
|
|||||||
$invitation = $this->invitations->first();
|
$invitation = $this->invitations->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
$storage_path = Storage::$type($this->client->quote_filepath().$this->number.'.pdf');
|
$storage_path = Storage::$type($this->client->quote_filepath().$this->numberFormatter().'.pdf');
|
||||||
|
|
||||||
nlog($storage_path);
|
nlog($storage_path);
|
||||||
|
|
||||||
if (! Storage::exists($this->client->quote_filepath().$this->number.'.pdf')) {
|
if (! Storage::exists($this->client->quote_filepath().$this->numberFormatter().'.pdf')) {
|
||||||
event(new QuoteWasUpdated($this, $this->company, Ninja::eventVars()));
|
event(new QuoteWasUpdated($this, $this->company, Ninja::eventVars()));
|
||||||
CreateEntityPdf::dispatchNow($invitation);
|
CreateEntityPdf::dispatchNow($invitation);
|
||||||
}
|
}
|
||||||
|
@ -130,9 +130,9 @@ class QuoteInvitation extends BaseModel
|
|||||||
|
|
||||||
public function pdf_file_path()
|
public function pdf_file_path()
|
||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->quote->client->quote_filepath().$this->quote->number.'.pdf');
|
$storage_path = Storage::url($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->quote->client->quote_filepath().$this->quote->number.'.pdf')) {
|
if (! Storage::exists($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf')) {
|
||||||
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars()));
|
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars()));
|
||||||
CreateEntityPdf::dispatchNow($this);
|
CreateEntityPdf::dispatchNow($this);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class InvoiceObserver
|
|||||||
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath() . $invoice->number.'.pdf');
|
// UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath() . $invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ use App\Models\Invoice;
|
|||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\PaymentHash;
|
use App\Models\PaymentHash;
|
||||||
use App\Models\SystemLog;
|
use App\Models\SystemLog;
|
||||||
|
use App\Services\BillingSubscription\BillingSubscriptionService;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Utils\Traits\SystemLogTrait;
|
use App\Utils\Traits\SystemLogTrait;
|
||||||
@ -240,6 +241,8 @@ class BaseDriver extends AbstractPaymentDriver
|
|||||||
|
|
||||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
||||||
|
|
||||||
|
//(new BillingSubscriptionService)->completePurchase($this->payment_hash);
|
||||||
|
|
||||||
return $payment->service()->applyNumber()->save();
|
return $payment->service()->applyNumber()->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class DriverTemplate extends BaseDriver
|
|||||||
GatewayType::CREDIT_CARD => CreditCard::class, //maps GatewayType => Implementation class
|
GatewayType::CREDIT_CARD => CreditCard::class, //maps GatewayType => Implementation class
|
||||||
];
|
];
|
||||||
|
|
||||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE;
|
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
|
||||||
|
|
||||||
public function setPaymentMethod($payment_method_id)
|
public function setPaymentMethod($payment_method_id)
|
||||||
{
|
{
|
||||||
|
@ -136,7 +136,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
|||||||
$payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
|
$payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
|
||||||
|
|
||||||
SystemLogger::dispatch(
|
SystemLogger::dispatch(
|
||||||
['response' => $response, 'data' => $data],
|
['response' => (array)$response->getData(), 'data' => $data],
|
||||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||||
SystemLog::TYPE_PAYPAL,
|
SystemLog::TYPE_PAYPAL,
|
||||||
|
@ -37,6 +37,7 @@ class ClientContactRepository extends BaseRepository
|
|||||||
});
|
});
|
||||||
|
|
||||||
$this->is_primary = true;
|
$this->is_primary = true;
|
||||||
|
|
||||||
/* Set first record to primary - always */
|
/* Set first record to primary - always */
|
||||||
$contacts = $contacts->sortByDesc('is_primary')->map(function ($contact) {
|
$contacts = $contacts->sortByDesc('is_primary')->map(function ($contact) {
|
||||||
$contact['is_primary'] = $this->is_primary;
|
$contact['is_primary'] = $this->is_primary;
|
||||||
|
174
app/Services/BillingSubscription/BillingSubscriptionService.php
Normal file
174
app/Services/BillingSubscription/BillingSubscriptionService.php
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\BillingSubscription;
|
||||||
|
|
||||||
|
use App\DataMapper\InvoiceItem;
|
||||||
|
use App\Factory\InvoiceFactory;
|
||||||
|
use App\Models\BillingSubscription;
|
||||||
|
use App\Models\ClientSubscription;
|
||||||
|
use App\Models\PaymentHash;
|
||||||
|
use App\Models\Product;
|
||||||
|
use App\Repositories\InvoiceRepository;
|
||||||
|
|
||||||
|
class BillingSubscriptionService
|
||||||
|
{
|
||||||
|
/** @var BillingSubscription */
|
||||||
|
private $billing_subscription;
|
||||||
|
|
||||||
|
public function __construct(BillingSubscription $billing_subscription)
|
||||||
|
{
|
||||||
|
$this->billing_subscription = $billing_subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function completePurchase(PaymentHash $payment_hash)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!property_exists($payment_hash, 'billing_context')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we have some state carried from the billing page
|
||||||
|
// to this, available as $payment_hash->data->billing_context. Make something awesome ⭐
|
||||||
|
|
||||||
|
// create client subscription record
|
||||||
|
//
|
||||||
|
// create recurring invoice if is_recurring
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function startTrial(array $data)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createInvoice($data): ?\App\Models\Invoice
|
||||||
|
{
|
||||||
|
$invoice_repo = new InvoiceRepository();
|
||||||
|
|
||||||
|
$data['line_items'] = $this->createLineItems($data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
If trial_enabled -> return early
|
||||||
|
|
||||||
|
-- what we need to know that we don't already
|
||||||
|
-- Has a promo code been entered, and does it match
|
||||||
|
-- Is this a recurring subscription
|
||||||
|
--
|
||||||
|
|
||||||
|
1. Is this a recurring product?
|
||||||
|
2. What is the quantity? ie is this a multi seat product ( does this mean we need this value stored in the client sub?)
|
||||||
|
*/
|
||||||
|
|
||||||
|
return $invoice_repo->save($data, InvoiceFactory::create($this->billing_subscription->company_id, $this->billing_subscription->user_id));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createLineItems($data): array
|
||||||
|
{
|
||||||
|
$line_items = [];
|
||||||
|
|
||||||
|
$product = $this->billing_subscription->product;
|
||||||
|
|
||||||
|
$item = new InvoiceItem;
|
||||||
|
$item->quantity = $data['quantity'];
|
||||||
|
$item->product_key = $product->product_key;
|
||||||
|
$item->notes = $product->notes;
|
||||||
|
$item->cost = $product->price;
|
||||||
|
$item->tax_rate1 = $product->tax_rate1 ?: 0;
|
||||||
|
$item->tax_name1 = $product->tax_name1 ?: '';
|
||||||
|
$item->tax_rate2 = $product->tax_rate2 ?: 0;
|
||||||
|
$item->tax_name2 = $product->tax_name2 ?: '';
|
||||||
|
$item->tax_rate3 = $product->tax_rate3 ?: 0;
|
||||||
|
$item->tax_name3 = $product->tax_name3 ?: '';
|
||||||
|
$item->custom_value1 = $product->custom_value1 ?: '';
|
||||||
|
$item->custom_value2 = $product->custom_value2 ?: '';
|
||||||
|
$item->custom_value3 = $product->custom_value3 ?: '';
|
||||||
|
$item->custom_value4 = $product->custom_value4 ?: '';
|
||||||
|
|
||||||
|
//$item->type_id need to switch whether the subscription is a service or product
|
||||||
|
|
||||||
|
$line_items[] = $item;
|
||||||
|
|
||||||
|
|
||||||
|
//do we have a promocode? enter this as a line item.
|
||||||
|
if(strlen($data['coupon']) >=1 && ($data['coupon'] == $this->billing_subscription->promo_code) && $this->billing_subscription->promo_discount > 0)
|
||||||
|
$line_items[] = $this->createPromoLine($data);
|
||||||
|
|
||||||
|
return $line_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createPromoLine($data)
|
||||||
|
{
|
||||||
|
|
||||||
|
$product = $this->billing_subscription->product;
|
||||||
|
|
||||||
|
$discounted_amount = 0;
|
||||||
|
$discount = 0;
|
||||||
|
$amount = $data['quantity'] * $product->cost;
|
||||||
|
|
||||||
|
if ($this->billing_subscription->is_amount_discount == true) {
|
||||||
|
$discount = $this->billing_subscription->promo_discount;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$discount = round($amount * ($this->billing_subscription->promo_discount / 100), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$discounted_amount = $amount - $discount;
|
||||||
|
|
||||||
|
$item = new InvoiceItem;
|
||||||
|
$item->quantity = 1;
|
||||||
|
$item->product_key = ctrans('texts.promo_code');
|
||||||
|
$item->notes = ctrans('texts.promo_code');
|
||||||
|
$item->cost = $discounted_amount;
|
||||||
|
$item->tax_rate1 = $product->tax_rate1 ?: 0;
|
||||||
|
$item->tax_name1 = $product->tax_name1 ?: '';
|
||||||
|
$item->tax_rate2 = $product->tax_rate2 ?: 0;
|
||||||
|
$item->tax_name2 = $product->tax_name2 ?: '';
|
||||||
|
$item->tax_rate3 = $product->tax_rate3 ?: 0;
|
||||||
|
$item->tax_name3 = $product->tax_name3 ?: '';
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function convertInvoiceToRecurring()
|
||||||
|
{
|
||||||
|
//The first invoice is a plain invoice - the second is fired on the recurring schedule.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createClientSubscription($payment_hash, $recurring_invoice_id = null)
|
||||||
|
{
|
||||||
|
//create the client sub record
|
||||||
|
|
||||||
|
//?trial enabled?
|
||||||
|
$cs = new ClientSubscription();
|
||||||
|
$cs->subscription_id = $this->billing_subscription->id;
|
||||||
|
$cs->company_id = $this->billing_subscription->company_id;
|
||||||
|
|
||||||
|
// client_id
|
||||||
|
$cs->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function triggerWebhook($payment_hash)
|
||||||
|
{
|
||||||
|
//hit the webhook to after a successful onboarding
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fireNotifications()
|
||||||
|
{
|
||||||
|
//scan for any notification we are required to send
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -140,7 +140,7 @@ class CreditService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath() . $this->credit->number.'.pdf');
|
UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath() . $this->credit->numberFormatter().'.pdf');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class GetCreditPdf extends AbstractService
|
|||||||
|
|
||||||
$path = $this->credit->client->credit_filepath();
|
$path = $this->credit->client->credit_filepath();
|
||||||
|
|
||||||
$file_path = $path.$this->credit->number.'.pdf';
|
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$disk = config('filesystems.default');
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class GetInvoicePdf extends AbstractService
|
|||||||
|
|
||||||
$path = $this->invoice->client->invoice_filepath();
|
$path = $this->invoice->client->invoice_filepath();
|
||||||
|
|
||||||
$file_path = $path.$this->invoice->number.'.pdf';
|
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$disk = config('filesystems.default');
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
|
@ -274,8 +274,8 @@ class InvoiceService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
//UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->number.'.pdf');
|
//UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||||
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath() . $this->invoice->number.'.pdf');
|
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -302,7 +302,7 @@ class Design extends BaseDesign
|
|||||||
public function buildTableHeader(string $type): array
|
public function buildTableHeader(string $type): array
|
||||||
{
|
{
|
||||||
$this->processTaxColumns($type);
|
$this->processTaxColumns($type);
|
||||||
$this->processCustomColumns($type);
|
// $this->processCustomColumns($type);
|
||||||
|
|
||||||
$elements = [];
|
$elements = [];
|
||||||
|
|
||||||
|
@ -213,6 +213,7 @@ trait PdfMakerUtilities
|
|||||||
$css = <<<'EOT'
|
$css = <<<'EOT'
|
||||||
table.page-container {
|
table.page-container {
|
||||||
page-break-after: always;
|
page-break-after: always;
|
||||||
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead.page-header {
|
thead.page-header {
|
||||||
|
@ -36,7 +36,7 @@ class GetQuotePdf extends AbstractService
|
|||||||
|
|
||||||
$path = $this->quote->client->quote_filepath();
|
$path = $this->quote->client->quote_filepath();
|
||||||
|
|
||||||
$file_path = $path.$this->quote->number.'.pdf';
|
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$disk = config('filesystems.default');
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ class QuoteService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath() . $this->quote->number.'.pdf');
|
UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath() . $this->quote->numberFormatter().'.pdf');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ class RecurringService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath() . $this->recurring_entity->number.'.pdf');
|
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath() . $this->recurring_entity->numberFormatter().'.pdf');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@ class AccountTransformer extends EntityTransformer
|
|||||||
'is_docker' => (bool) config('ninja.is_docker'),
|
'is_docker' => (bool) config('ninja.is_docker'),
|
||||||
'is_scheduler_running' => (bool) $account->is_scheduler_running,
|
'is_scheduler_running' => (bool) $account->is_scheduler_running,
|
||||||
'default_company_id' => (string) $this->encodePrimaryKey($account->default_company_id),
|
'default_company_id' => (string) $this->encodePrimaryKey($account->default_company_id),
|
||||||
|
'disable_auto_update' => (bool) config('ninja.disable_auto_update'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ class BillingSubscriptionTransformer extends EntityTransformer
|
|||||||
'plan_map' => (string)$billing_subscription->plan_map,
|
'plan_map' => (string)$billing_subscription->plan_map,
|
||||||
'refund_period' => (int)$billing_subscription->refund_period,
|
'refund_period' => (int)$billing_subscription->refund_period,
|
||||||
'webhook_configuration' => (string)$billing_subscription->webhook_configuration,
|
'webhook_configuration' => (string)$billing_subscription->webhook_configuration,
|
||||||
|
'purchase_page' => (string)route('client.subscription.purchase', $billing_subscription->hashed_id),
|
||||||
'is_deleted' => (bool)$billing_subscription->is_deleted,
|
'is_deleted' => (bool)$billing_subscription->is_deleted,
|
||||||
'created_at' => (int)$billing_subscription->created_at,
|
'created_at' => (int)$billing_subscription->created_at,
|
||||||
'updated_at' => (int)$billing_subscription->updated_at,
|
'updated_at' => (int)$billing_subscription->updated_at,
|
||||||
|
@ -16,6 +16,7 @@ use App\Models\Client;
|
|||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\InvoiceInvitation;
|
use App\Models\InvoiceInvitation;
|
||||||
|
use App\Models\Payment;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class InvoiceTransformer extends EntityTransformer
|
class InvoiceTransformer extends EntityTransformer
|
||||||
@ -30,7 +31,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
protected $availableIncludes = [
|
protected $availableIncludes = [
|
||||||
// 'invitations',
|
// 'invitations',
|
||||||
'history',
|
'history',
|
||||||
// 'payments',
|
'payments',
|
||||||
'client',
|
'client',
|
||||||
// 'documents',
|
// 'documents',
|
||||||
];
|
];
|
||||||
@ -56,15 +57,15 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
return $this->includeItem($invoice->client, $transformer, Client::class);
|
return $this->includeItem($invoice->client, $transformer, Client::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function includePayments(Invoice $invoice)
|
||||||
|
{
|
||||||
|
$transformer = new PaymentTransformer( $this->serializer);
|
||||||
|
|
||||||
|
return $this->includeCollection($invoice->payments, $transformer, Payment::class);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
public function includePayments(Invoice $invoice)
|
|
||||||
{
|
|
||||||
$transformer = new PaymentTransformer($this->account, $this->serializer, $invoice);
|
|
||||||
|
|
||||||
return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function includeExpenses(Invoice $invoice)
|
public function includeExpenses(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$transformer = new ExpenseTransformer($this->account, $this->serializer);
|
$transformer = new ExpenseTransformer($this->account, $this->serializer);
|
||||||
|
37
app/Utils/HostedPDF/NinjaPdf.php
Normal file
37
app/Utils/HostedPDF/NinjaPdf.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Utils\HostedPDF;
|
||||||
|
|
||||||
|
use GuzzleHttp\RequestOptions;
|
||||||
|
|
||||||
|
class NinjaPdf
|
||||||
|
{
|
||||||
|
|
||||||
|
private $url = 'https://pdf.invoicing.co/api/';
|
||||||
|
|
||||||
|
public function build($html)
|
||||||
|
{
|
||||||
|
|
||||||
|
$client = new \GuzzleHttp\Client(['headers' =>
|
||||||
|
[
|
||||||
|
'X-Ninja-Token' => 'test_token_for_now',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $client->post($this->url,[
|
||||||
|
RequestOptions::JSON => ['html' => $html]
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response->getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -285,6 +285,8 @@ class HtmlEngine
|
|||||||
$data['$company.website'] = ['value' => $this->settings->website ?: ' ', 'label' => ctrans('texts.website')];
|
$data['$company.website'] = ['value' => $this->settings->website ?: ' ', 'label' => ctrans('texts.website')];
|
||||||
$data['$company.address'] = ['value' => $this->company->present()->address($this->settings) ?: ' ', 'label' => ctrans('texts.address')];
|
$data['$company.address'] = ['value' => $this->company->present()->address($this->settings) ?: ' ', 'label' => ctrans('texts.address')];
|
||||||
|
|
||||||
|
$data['$signature'] = ['value' => $this->settings->email_signature ?: ' ', 'label' => ''];
|
||||||
|
|
||||||
$data['$spc_qr_code'] = ['value' => $this->company->present()->getSpcQrCode($this->client->currency()->code, $this->entity->number, $this->entity->balance), 'label' => ''];
|
$data['$spc_qr_code'] = ['value' => $this->company->present()->getSpcQrCode($this->client->currency()->code, $this->entity->number, $this->entity->balance), 'label' => ''];
|
||||||
|
|
||||||
$logo = $this->company->present()->logo($this->settings);
|
$logo = $this->company->present()->logo($this->settings);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Utils;
|
namespace App\Utils;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Currency;
|
use App\Models\Currency;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,12 +89,12 @@ class Number
|
|||||||
* Formats a given value based on the clients currency AND country.
|
* Formats a given value based on the clients currency AND country.
|
||||||
*
|
*
|
||||||
* @param floatval $value The number to be formatted
|
* @param floatval $value The number to be formatted
|
||||||
* @param $client
|
* @param $entity
|
||||||
* @return string The formatted value
|
* @return string The formatted value
|
||||||
*/
|
*/
|
||||||
public static function formatMoney($value, $client) :string
|
public static function formatMoney($value, $entity) :string
|
||||||
{
|
{
|
||||||
$currency = $client->currency();
|
$currency = $entity->currency();
|
||||||
|
|
||||||
$thousand = $currency->thousand_separator;
|
$thousand = $currency->thousand_separator;
|
||||||
$decimal = $currency->decimal_separator;
|
$decimal = $currency->decimal_separator;
|
||||||
@ -101,29 +102,38 @@ class Number
|
|||||||
$code = $currency->code;
|
$code = $currency->code;
|
||||||
$swapSymbol = $currency->swap_currency_symbol;
|
$swapSymbol = $currency->swap_currency_symbol;
|
||||||
|
|
||||||
|
// App\Models\Client::country() returns instance of BelongsTo.
|
||||||
|
// App\Models\Company::country() returns record for the country, that's why we check for the instance.
|
||||||
|
|
||||||
|
if ($entity instanceof Company) {
|
||||||
|
$country = $entity->country();
|
||||||
|
} else {
|
||||||
|
$country = $entity->country;
|
||||||
|
}
|
||||||
|
|
||||||
/* Country settings override client settings */
|
/* Country settings override client settings */
|
||||||
if (isset($client->country->thousand_separator) && strlen($client->country->thousand_separator) >= 1) {
|
if (isset($country->thousand_separator) && strlen($country->thousand_separator) >= 1) {
|
||||||
$thousand = $client->country->thousand_separator;
|
$thousand = $country->thousand_separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($client->country->decimal_separator) && strlen($client->country->decimal_separator) >= 1) {
|
if (isset($country->decimal_separator) && strlen($country->decimal_separator) >= 1) {
|
||||||
$decimal = $client->country->decimal_separator;
|
$decimal = $country->decimal_separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($client->country->swap_currency_symbol) && strlen($client->country->swap_currency_symbol) >= 1) {
|
if (isset($country->swap_currency_symbol) && strlen($country->swap_currency_symbol) >= 1) {
|
||||||
$swapSymbol = $client->country->swap_currency_symbol;
|
$swapSymbol = $country->swap_currency_symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = number_format($value, $precision, $decimal, $thousand);
|
$value = number_format($value, $precision, $decimal, $thousand);
|
||||||
$symbol = $currency->symbol;
|
$symbol = $currency->symbol;
|
||||||
|
|
||||||
if ($client->getSetting('show_currency_code') === true && $currency->code == 'CHF') {
|
if ($entity->getSetting('show_currency_code') === true && $currency->code == 'CHF') {
|
||||||
return "{$code} {$value}";
|
return "{$code} {$value}";
|
||||||
} elseif ($client->getSetting('show_currency_code') === true) {
|
} elseif ($entity->getSetting('show_currency_code') === true) {
|
||||||
return "{$value} {$code}";
|
return "{$value} {$code}";
|
||||||
} elseif ($swapSymbol) {
|
} elseif ($swapSymbol) {
|
||||||
return "{$value} ".trim($symbol);
|
return "{$value} ".trim($symbol);
|
||||||
} elseif ($client->getSetting('show_currency_code') === false) {
|
} elseif ($entity->getSetting('show_currency_code') === false) {
|
||||||
return "{$symbol}{$value}";
|
return "{$symbol}{$value}";
|
||||||
} else {
|
} else {
|
||||||
return self::formatValue($value, $currency);
|
return self::formatValue($value, $currency);
|
||||||
|
@ -76,7 +76,7 @@ class Phantom
|
|||||||
$path = $entity_obj->client->recurring_invoice_filepath();
|
$path = $entity_obj->client->recurring_invoice_filepath();
|
||||||
}
|
}
|
||||||
|
|
||||||
$file_path = $path.$entity_obj->number.'.pdf';
|
$file_path = $path.$entity_obj->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$url = config('ninja.app_url').'/phantom/'.$entity.'/'.$invitation->key.'?phantomjs_secret='.config('ninja.phantomjs_secret');
|
$url = config('ninja.app_url').'/phantom/'.$entity.'/'.$invitation->key.'?phantomjs_secret='.config('ninja.phantomjs_secret');
|
||||||
info($url);
|
info($url);
|
||||||
@ -91,8 +91,8 @@ class Phantom
|
|||||||
|
|
||||||
$instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
|
$instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
|
||||||
|
|
||||||
nlog($instance);
|
// nlog($instance);
|
||||||
nlog($file_path);
|
// nlog($file_path);
|
||||||
return $file_path;
|
return $file_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +78,15 @@ class SystemHealth
|
|||||||
'phantom_enabled' => (bool)config('ninja.phantomjs_pdf_generation'),
|
'phantom_enabled' => (bool)config('ninja.phantomjs_pdf_generation'),
|
||||||
'exec' => (bool)self::checkExecWorks(),
|
'exec' => (bool)self::checkExecWorks(),
|
||||||
'open_basedir' => (bool)self::checkOpenBaseDir(),
|
'open_basedir' => (bool)self::checkOpenBaseDir(),
|
||||||
|
'mail_mailer' => (string)self::checkMailMailer(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function checkMailMailer()
|
||||||
|
{
|
||||||
|
return config('mail.default');
|
||||||
|
}
|
||||||
|
|
||||||
public static function checkOpenBaseDir()
|
public static function checkOpenBaseDir()
|
||||||
{
|
{
|
||||||
if (strlen(ini_get('open_basedir') == 0)) {
|
if (strlen(ini_get('open_basedir') == 0)) {
|
||||||
|
@ -134,6 +134,9 @@ trait GeneratesCounter
|
|||||||
return 'payment_number_counter';
|
return 'payment_number_counter';
|
||||||
break;
|
break;
|
||||||
case Credit::class:
|
case Credit::class:
|
||||||
|
if ($this->hasSharedCounter($client))
|
||||||
|
return 'invoice_number_counter';
|
||||||
|
|
||||||
return 'credit_number_counter';
|
return 'credit_number_counter';
|
||||||
break;
|
break;
|
||||||
case Project::class:
|
case Project::class:
|
||||||
@ -313,7 +316,7 @@ trait GeneratesCounter
|
|||||||
*/
|
*/
|
||||||
public function hasSharedCounter(Client $client) : bool
|
public function hasSharedCounter(Client $client) : bool
|
||||||
{
|
{
|
||||||
return (bool) $client->getSetting('shared_invoice_quote_counter');
|
return (bool) $client->getSetting('shared_invoice_quote_counter') || (bool) $client->getSetting('shared_invoice_credit_counter');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +34,7 @@ trait UserNotifies
|
|||||||
array_push($required_permissions, 'all_user_notifications');
|
array_push($required_permissions, 'all_user_notifications');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count(array_intersect($required_permissions, $notifications->email)) >= 1 || count(array_intersect($required_permissions, 'all_user_notifications')) >= 1 || count(array_intersect($required_permissions, 'all_notifications')) >= 1) {
|
if (count(array_intersect($required_permissions, $notifications->email)) >= 1 || count(array_intersect($required_permissions, ['all_user_notifications'])) >= 1 || count(array_intersect($required_permissions, ['all_notifications'])) >= 1) {
|
||||||
array_push($notifiable_methods, 'mail');
|
array_push($notifiable_methods, 'mail');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ trait SavesDocuments
|
|||||||
{
|
{
|
||||||
public function saveDocuments($document_array, $entity, $is_public = true)
|
public function saveDocuments($document_array, $entity, $is_public = true)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($entity instanceof Company) {
|
if ($entity instanceof Company) {
|
||||||
$account = $entity->account;
|
$account = $entity->account;
|
||||||
$company = $entity;
|
$company = $entity;
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
"coconutcraig/laravel-postmark": "^2.10",
|
"coconutcraig/laravel-postmark": "^2.10",
|
||||||
"composer/composer": "^2",
|
"composer/composer": "^2",
|
||||||
"czproject/git-php": "^3.17",
|
"czproject/git-php": "^3.17",
|
||||||
"dacastro4/laravel-gmail": "dev-master",
|
"dacastro4/laravel-gmail": "^5.1",
|
||||||
"doctrine/dbal": "^2.10",
|
"doctrine/dbal": "^2.10",
|
||||||
"fideloper/proxy": "^4.2",
|
"fideloper/proxy": "^4.2",
|
||||||
"fzaninotto/faker": "^1.4",
|
"fzaninotto/faker": "^1.4",
|
||||||
@ -65,12 +65,13 @@
|
|||||||
"predis/predis": "^1.1",
|
"predis/predis": "^1.1",
|
||||||
"sentry/sentry-laravel": "^2",
|
"sentry/sentry-laravel": "^2",
|
||||||
"stripe/stripe-php": "^7.50",
|
"stripe/stripe-php": "^7.50",
|
||||||
|
"symfony/http-client": "^5.2",
|
||||||
"turbo124/beacon": "^1.0",
|
"turbo124/beacon": "^1.0",
|
||||||
"webpatser/laravel-countries": "dev-master#75992ad",
|
"webpatser/laravel-countries": "dev-master#75992ad",
|
||||||
"wildbit/swiftmailer-postmark": "^3.3"
|
"wildbit/swiftmailer-postmark": "^3.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"php": "^7.4",
|
"php": "^7.3|^7.4",
|
||||||
"anahkiasen/former": "^4.2",
|
"anahkiasen/former": "^4.2",
|
||||||
"barryvdh/laravel-debugbar": "^3.4",
|
"barryvdh/laravel-debugbar": "^3.4",
|
||||||
"brianium/paratest": "^6.1",
|
"brianium/paratest": "^6.1",
|
||||||
|
158
composer.lock
generated
158
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "d9ca08ed1ffaa0ed07c40e014a96d52a",
|
"content-hash": "113acad46f6d9eea9f9f5bd4501428d1",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "authorizenet/authorizenet",
|
"name": "authorizenet/authorizenet",
|
||||||
@ -55,16 +55,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.173.25",
|
"version": "3.173.28",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "69e4653acf8cd855e9010ec4e0e0a7d074c77ee1"
|
"reference": "593baa5930896bb443c437032daf4016e1e3878d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/69e4653acf8cd855e9010ec4e0e0a7d074c77ee1",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/593baa5930896bb443c437032daf4016e1e3878d",
|
||||||
"reference": "69e4653acf8cd855e9010ec4e0e0a7d074c77ee1",
|
"reference": "593baa5930896bb443c437032daf4016e1e3878d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -139,9 +139,9 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.173.25"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.173.28"
|
||||||
},
|
},
|
||||||
"time": "2021-03-09T19:14:56+00:00"
|
"time": "2021-03-12T19:29:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@ -1011,16 +1011,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dacastro4/laravel-gmail",
|
"name": "dacastro4/laravel-gmail",
|
||||||
"version": "dev-master",
|
"version": "v5.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/dacastro4/laravel-gmail.git",
|
"url": "https://github.com/dacastro4/laravel-gmail.git",
|
||||||
"reference": "a7137e7e7aa0672ec963e4d24da6c5f22a0208c0"
|
"reference": "6d4cabe04f8cdd02b25ef73a1a489099b5e790bd"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/dacastro4/laravel-gmail/zipball/a7137e7e7aa0672ec963e4d24da6c5f22a0208c0",
|
"url": "https://api.github.com/repos/dacastro4/laravel-gmail/zipball/6d4cabe04f8cdd02b25ef73a1a489099b5e790bd",
|
||||||
"reference": "a7137e7e7aa0672ec963e4d24da6c5f22a0208c0",
|
"reference": "6d4cabe04f8cdd02b25ef73a1a489099b5e790bd",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1031,16 +1031,15 @@
|
|||||||
"illuminate/routing": "~5.8|^6.0|^7.0|^8.0",
|
"illuminate/routing": "~5.8|^6.0|^7.0|^8.0",
|
||||||
"illuminate/session": "~5.8|^6.0|^7.0|^8.0",
|
"illuminate/session": "~5.8|^6.0|^7.0|^8.0",
|
||||||
"illuminate/support": "~5.8|^6.0|^7.0|^8.0",
|
"illuminate/support": "~5.8|^6.0|^7.0|^8.0",
|
||||||
"php": "^7.3|^8.0",
|
"php": "^7.2",
|
||||||
"swiftmailer/swiftmailer": "~5.8|^6.0"
|
"swiftmailer/swiftmailer": "~5.8|^6.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"mockery/mockery": "^1.0",
|
"mockery/mockery": "^1.0",
|
||||||
"orchestra/testbench": "^4.0|^5.0|^6.0",
|
"orchestra/testbench": "^4.0",
|
||||||
"phpunit/phpunit": "^8.5|^9.0",
|
"phpunit/phpunit": "^8.5",
|
||||||
"squizlabs/php_codesniffer": "~3.4"
|
"squizlabs/php_codesniffer": "~3.4"
|
||||||
},
|
},
|
||||||
"default-branch": true,
|
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"laravel": {
|
"laravel": {
|
||||||
@ -1076,9 +1075,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/dacastro4/laravel-gmail/issues",
|
"issues": "https://github.com/dacastro4/laravel-gmail/issues",
|
||||||
"source": "https://github.com/dacastro4/laravel-gmail/tree/master"
|
"source": "https://github.com/dacastro4/laravel-gmail/tree/v5.1"
|
||||||
},
|
},
|
||||||
"time": "2021-03-02T17:18:12+00:00"
|
"time": "2020-12-13T19:17:07+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dasprid/enum",
|
"name": "dasprid/enum",
|
||||||
@ -5447,16 +5446,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpseclib/phpseclib",
|
"name": "phpseclib/phpseclib",
|
||||||
"version": "3.0.5",
|
"version": "3.0.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||||
"reference": "7c751ea006577e4c2e83326d90c8b1e8c11b8ede"
|
"reference": "906a5fafabe5e6ba51ef3dc65b2722a677908837"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7c751ea006577e4c2e83326d90c8b1e8c11b8ede",
|
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/906a5fafabe5e6ba51ef3dc65b2722a677908837",
|
||||||
"reference": "7c751ea006577e4c2e83326d90c8b1e8c11b8ede",
|
"reference": "906a5fafabe5e6ba51ef3dc65b2722a677908837",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -5538,7 +5537,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.5"
|
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.6"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -5554,7 +5553,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-12T16:18:16+00:00"
|
"time": "2021-03-10T13:58:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pragmarx/google2fa",
|
"name": "pragmarx/google2fa",
|
||||||
@ -6097,16 +6096,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psy/psysh",
|
"name": "psy/psysh",
|
||||||
"version": "v0.10.6",
|
"version": "v0.10.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/bobthecow/psysh.git",
|
"url": "https://github.com/bobthecow/psysh.git",
|
||||||
"reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3"
|
"reference": "a395af46999a12006213c0c8346c9445eb31640c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/6f990c19f91729de8b31e639d6e204ea59f19cf3",
|
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/a395af46999a12006213c0c8346c9445eb31640c",
|
||||||
"reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3",
|
"reference": "a395af46999a12006213c0c8346c9445eb31640c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -6167,9 +6166,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/bobthecow/psysh/issues",
|
"issues": "https://github.com/bobthecow/psysh/issues",
|
||||||
"source": "https://github.com/bobthecow/psysh/tree/v0.10.6"
|
"source": "https://github.com/bobthecow/psysh/tree/v0.10.7"
|
||||||
},
|
},
|
||||||
"time": "2021-01-18T15:53:43+00:00"
|
"time": "2021-03-14T02:14:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ralouphie/getallheaders",
|
"name": "ralouphie/getallheaders",
|
||||||
@ -7056,16 +7055,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v5.2.4",
|
"version": "v5.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/console.git",
|
"url": "https://github.com/symfony/console.git",
|
||||||
"reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556"
|
"reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/console/zipball/d6d0cc30d8c0fda4e7b213c20509b0159a8f4556",
|
"url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79",
|
||||||
"reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556",
|
"reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -7133,7 +7132,7 @@
|
|||||||
"terminal"
|
"terminal"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/console/tree/v5.2.4"
|
"source": "https://github.com/symfony/console/tree/v5.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -7149,7 +7148,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-23T10:08:49+00:00"
|
"time": "2021-03-06T13:42:15+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/css-selector",
|
"name": "symfony/css-selector",
|
||||||
@ -7879,16 +7878,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-kernel",
|
"name": "symfony/http-kernel",
|
||||||
"version": "v5.2.4",
|
"version": "v5.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-kernel.git",
|
"url": "https://github.com/symfony/http-kernel.git",
|
||||||
"reference": "c452dbe4f385f030c3957821bf921b13815d6140"
|
"reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/c452dbe4f385f030c3957821bf921b13815d6140",
|
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/b8c63ef63c2364e174c3b3e0ba0bf83455f97f73",
|
||||||
"reference": "c452dbe4f385f030c3957821bf921b13815d6140",
|
"reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -7971,7 +7970,7 @@
|
|||||||
"description": "Provides a structured process for converting a Request into a Response",
|
"description": "Provides a structured process for converting a Request into a Response",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-kernel/tree/v5.2.4"
|
"source": "https://github.com/symfony/http-kernel/tree/v5.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -7987,20 +7986,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-03-04T18:05:55+00:00"
|
"time": "2021-03-10T17:07:35+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/mime",
|
"name": "symfony/mime",
|
||||||
"version": "v5.2.4",
|
"version": "v5.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/mime.git",
|
"url": "https://github.com/symfony/mime.git",
|
||||||
"reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd"
|
"reference": "554ba128f1955038b45db5e1fa7e93bfc683b139"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/mime/zipball/5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd",
|
"url": "https://api.github.com/repos/symfony/mime/zipball/554ba128f1955038b45db5e1fa7e93bfc683b139",
|
||||||
"reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd",
|
"reference": "554ba128f1955038b45db5e1fa7e93bfc683b139",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -8011,12 +8010,13 @@
|
|||||||
"symfony/polyfill-php80": "^1.15"
|
"symfony/polyfill-php80": "^1.15"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
|
"egulias/email-validator": "~3.0.0",
|
||||||
"phpdocumentor/reflection-docblock": "<3.2.2",
|
"phpdocumentor/reflection-docblock": "<3.2.2",
|
||||||
"phpdocumentor/type-resolver": "<1.4.0",
|
"phpdocumentor/type-resolver": "<1.4.0",
|
||||||
"symfony/mailer": "<4.4"
|
"symfony/mailer": "<4.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"egulias/email-validator": "^2.1.10",
|
"egulias/email-validator": "^2.1.10|^3.1",
|
||||||
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
|
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
|
||||||
"symfony/dependency-injection": "^4.4|^5.0",
|
"symfony/dependency-injection": "^4.4|^5.0",
|
||||||
"symfony/property-access": "^4.4|^5.1",
|
"symfony/property-access": "^4.4|^5.1",
|
||||||
@ -8053,7 +8053,7 @@
|
|||||||
"mime-type"
|
"mime-type"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/mime/tree/v5.2.4"
|
"source": "https://github.com/symfony/mime/tree/v5.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -8069,7 +8069,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-15T18:55:04+00:00"
|
"time": "2021-03-07T16:08:20+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/options-resolver",
|
"name": "symfony/options-resolver",
|
||||||
@ -9352,16 +9352,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation",
|
"name": "symfony/translation",
|
||||||
"version": "v5.2.4",
|
"version": "v5.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/translation.git",
|
"url": "https://github.com/symfony/translation.git",
|
||||||
"reference": "74b0353ab34ff4cca827a2cf909e325d96815e60"
|
"reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/translation/zipball/74b0353ab34ff4cca827a2cf909e325d96815e60",
|
"url": "https://api.github.com/repos/symfony/translation/zipball/0947ab1e3aabd22a6bef393874b2555d2bb976da",
|
||||||
"reference": "74b0353ab34ff4cca827a2cf909e325d96815e60",
|
"reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9425,7 +9425,7 @@
|
|||||||
"description": "Provides tools to internationalize your application",
|
"description": "Provides tools to internationalize your application",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/translation/tree/v5.2.4"
|
"source": "https://github.com/symfony/translation/tree/v5.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -9441,7 +9441,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-03-04T15:41:09+00:00"
|
"time": "2021-03-06T07:59:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation-contracts",
|
"name": "symfony/translation-contracts",
|
||||||
@ -9523,16 +9523,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/var-dumper",
|
"name": "symfony/var-dumper",
|
||||||
"version": "v5.2.4",
|
"version": "v5.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/var-dumper.git",
|
"url": "https://github.com/symfony/var-dumper.git",
|
||||||
"reference": "6a81fec0628c468cf6d5c87a4d003725e040e223"
|
"reference": "002ab5a36702adf0c9a11e6d8836623253e9045e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/6a81fec0628c468cf6d5c87a4d003725e040e223",
|
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/002ab5a36702adf0c9a11e6d8836623253e9045e",
|
||||||
"reference": "6a81fec0628c468cf6d5c87a4d003725e040e223",
|
"reference": "002ab5a36702adf0c9a11e6d8836623253e9045e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9591,7 +9591,7 @@
|
|||||||
"dump"
|
"dump"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/var-dumper/tree/v5.2.4"
|
"source": "https://github.com/symfony/var-dumper/tree/v5.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -9607,7 +9607,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-18T23:11:19+00:00"
|
"time": "2021-03-06T07:59:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tijsverkoyen/css-to-inline-styles",
|
"name": "tijsverkoyen/css-to-inline-styles",
|
||||||
@ -11150,16 +11150,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
"version": "v2.18.2",
|
"version": "v2.18.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
|
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
|
||||||
"reference": "18f8c9d184ba777380794a389fabc179896ba913"
|
"reference": "ab99202fccff2a9f97592fbe1b5c76dd06df3513"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/18f8c9d184ba777380794a389fabc179896ba913",
|
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ab99202fccff2a9f97592fbe1b5c76dd06df3513",
|
||||||
"reference": "18f8c9d184ba777380794a389fabc179896ba913",
|
"reference": "ab99202fccff2a9f97592fbe1b5c76dd06df3513",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -11221,6 +11221,7 @@
|
|||||||
"tests/Test/IntegrationCaseFactoryInterface.php",
|
"tests/Test/IntegrationCaseFactoryInterface.php",
|
||||||
"tests/Test/InternalIntegrationCaseFactory.php",
|
"tests/Test/InternalIntegrationCaseFactory.php",
|
||||||
"tests/Test/IsIdenticalConstraint.php",
|
"tests/Test/IsIdenticalConstraint.php",
|
||||||
|
"tests/Test/TokensWithObservedTransformers.php",
|
||||||
"tests/TestCase.php"
|
"tests/TestCase.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -11241,7 +11242,7 @@
|
|||||||
"description": "A tool to automatically fix PHP code style",
|
"description": "A tool to automatically fix PHP code style",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues",
|
"issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues",
|
||||||
"source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.18.2"
|
"source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.18.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -11249,7 +11250,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-01-26T00:22:21+00:00"
|
"time": "2021-03-10T19:39:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "hamcrest/hamcrest-php",
|
"name": "hamcrest/hamcrest-php",
|
||||||
@ -13467,16 +13468,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "swagger-api/swagger-ui",
|
"name": "swagger-api/swagger-ui",
|
||||||
"version": "v3.44.1",
|
"version": "v3.45.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/swagger-api/swagger-ui.git",
|
"url": "https://github.com/swagger-api/swagger-ui.git",
|
||||||
"reference": "db830fbb51ba987dc9931172c891b318ba444d39"
|
"reference": "1ba7af074f97c872a64a415e0507c11cf8f3601b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/db830fbb51ba987dc9931172c891b318ba444d39",
|
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/1ba7af074f97c872a64a415e0507c11cf8f3601b",
|
||||||
"reference": "db830fbb51ba987dc9931172c891b318ba444d39",
|
"reference": "1ba7af074f97c872a64a415e0507c11cf8f3601b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -13522,9 +13523,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/swagger-api/swagger-ui/issues",
|
"issues": "https://github.com/swagger-api/swagger-ui/issues",
|
||||||
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.44.1"
|
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.45.0"
|
||||||
},
|
},
|
||||||
"time": "2021-03-04T18:13:06+00:00"
|
"time": "2021-03-11T17:20:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/debug",
|
"name": "symfony/debug",
|
||||||
@ -13727,16 +13728,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/yaml",
|
"name": "symfony/yaml",
|
||||||
"version": "v5.2.4",
|
"version": "v5.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/yaml.git",
|
"url": "https://github.com/symfony/yaml.git",
|
||||||
"reference": "7d6ae0cce3c33965af681a4355f1c4de326ed277"
|
"reference": "298a08ddda623485208506fcee08817807a251dd"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/7d6ae0cce3c33965af681a4355f1c4de326ed277",
|
"url": "https://api.github.com/repos/symfony/yaml/zipball/298a08ddda623485208506fcee08817807a251dd",
|
||||||
"reference": "7d6ae0cce3c33965af681a4355f1c4de326ed277",
|
"reference": "298a08ddda623485208506fcee08817807a251dd",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -13782,7 +13783,7 @@
|
|||||||
"description": "Loads and dumps YAML files",
|
"description": "Loads and dumps YAML files",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/yaml/tree/v5.2.4"
|
"source": "https://github.com/symfony/yaml/tree/v5.2.5"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -13798,7 +13799,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-22T15:48:39+00:00"
|
"time": "2021-03-06T07:59:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
@ -14080,7 +14081,6 @@
|
|||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"stability-flags": {
|
"stability-flags": {
|
||||||
"dacastro4/laravel-gmail": 20,
|
|
||||||
"webpatser/laravel-countries": 20
|
"webpatser/laravel-countries": 20
|
||||||
},
|
},
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
|
@ -13,7 +13,7 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', ''),
|
'app_domain' => env('APP_DOMAIN', ''),
|
||||||
'app_version' => '5.1.24',
|
'app_version' => '5.1.29',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', false),
|
'api_secret' => env('API_SECRET', false),
|
||||||
@ -143,4 +143,6 @@ return [
|
|||||||
'v4_migration_version' => '4.5.31',
|
'v4_migration_version' => '4.5.31',
|
||||||
'flutter_canvas_kit' => env('FLUTTER_CANVAS_KIT', false),
|
'flutter_canvas_kit' => env('FLUTTER_CANVAS_KIT', false),
|
||||||
'webcron_secret' => env('WEBCRON_SECRET', false),
|
'webcron_secret' => env('WEBCRON_SECRET', false),
|
||||||
|
'disable_auto_update' => env('DISABLE_AUTO_UPDATE', false),
|
||||||
|
'invoiceninja_hosted_pdf_generation' => env('NINJA_HOSTED_PDF', false),
|
||||||
];
|
];
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class Change2faColumnFromVarcharToText extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->text('google_2fa_secret')->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddUniqueConstraintsOnAllEntities extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('expenses', function (Blueprint $table) {
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('tasks', function (Blueprint $table) {
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('vendors', function (Blueprint $table) {
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('payments', function (Blueprint $table) {
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('projects', function (Blueprint $table) {
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('clients', function (Blueprint $table) {
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('payment_hashes', function (Blueprint $table) {
|
||||||
|
$table->unique(['hash']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('recurring_invoices', function (Blueprint $table) {
|
||||||
|
$table->string('number')->change();
|
||||||
|
$table->unique(['company_id', 'number']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('recurring_invoice_invitations', function (Blueprint $table) {
|
||||||
|
$table->unique(['client_contact_id', 'recurring_invoice_id'],'recur_invoice_client_unique');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddInvoiceIdToClientSubscriptionsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('client_subscriptions', function (Blueprint $table) {
|
||||||
|
$table->unsignedInteger('invoice_id')->nullable();
|
||||||
|
$table->unsignedInteger('quantity')->default(1);
|
||||||
|
$table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade')->onUpdate('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/flutter_service_worker.js
vendored
2
public/flutter_service_worker.js
vendored
@ -30,7 +30,7 @@ const RESOURCES = {
|
|||||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff",
|
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "3e722fd57a6db80ee119f0e2c230ccff",
|
||||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||||
"/": "23224b5e03519aaa87594403d54412cf",
|
"/": "23224b5e03519aaa87594403d54412cf",
|
||||||
"main.dart.js": "c11c4d2efa9e671a88eb792d18c2296e",
|
"main.dart.js": "114d8affe0f4b7576170753cf9fb4c0a",
|
||||||
"version.json": "b7c8971e1ab5b627fd2a4317c52b843e",
|
"version.json": "b7c8971e1ab5b627fd2a4317c52b843e",
|
||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
|
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
|
||||||
};
|
};
|
||||||
|
212263
public/main.dart.js
vendored
212263
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
|
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
|
||||||
"/css/app.css": "/css/app.css?id=745170b7d7a4dc7469f2",
|
"/css/app.css": "/css/app.css?id=e8d6d5e8cb60bc2f15b3",
|
||||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
|
"/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/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
|
||||||
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",
|
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",
|
||||||
|
@ -1976,7 +1976,7 @@ $LANG = array(
|
|||||||
'require_invoice_signature_help' => 'Require client to provide their signature.',
|
'require_invoice_signature_help' => 'Require client to provide their signature.',
|
||||||
'require_quote_signature' => 'Quote Signature',
|
'require_quote_signature' => 'Quote Signature',
|
||||||
'require_quote_signature_help' => 'Require client to provide their signature.',
|
'require_quote_signature_help' => 'Require client to provide their signature.',
|
||||||
'i_agree' => 'I Agree To',
|
'i_agree' => 'I Agree To The Terms',
|
||||||
'sign_here' => 'Please sign here:',
|
'sign_here' => 'Please sign here:',
|
||||||
'authorization' => 'Authorization',
|
'authorization' => 'Authorization',
|
||||||
'signed' => 'Signed',
|
'signed' => 'Signed',
|
||||||
@ -3449,10 +3449,10 @@ $LANG = array(
|
|||||||
'client_country' => 'Client Country',
|
'client_country' => 'Client Country',
|
||||||
'client_is_active' => 'Client is Active',
|
'client_is_active' => 'Client is Active',
|
||||||
'client_balance' => 'Client Balance',
|
'client_balance' => 'Client Balance',
|
||||||
'client_address1' => 'Client Address 1',
|
'client_address1' => 'Client Street',
|
||||||
'client_address2' => 'Client Address 2',
|
'client_address2' => 'Client Apt/Suite',
|
||||||
'client_shipping_address1' => 'Client Shipping Address 1',
|
'client_shipping_address1' => 'Client Shipping Street',
|
||||||
'client_shipping_address2' => 'Client Shipping Address 2',
|
'client_shipping_address2' => 'Client Shipping Apt/Suite',
|
||||||
'tax_rate1' => 'Tax Rate 1',
|
'tax_rate1' => 'Tax Rate 1',
|
||||||
'tax_rate2' => 'Tax Rate 2',
|
'tax_rate2' => 'Tax Rate 2',
|
||||||
'tax_rate3' => 'Tax Rate 3',
|
'tax_rate3' => 'Tax Rate 3',
|
||||||
@ -3532,8 +3532,8 @@ $LANG = array(
|
|||||||
'marked_credit_as_sent' => 'Successfully marked credit as sent',
|
'marked_credit_as_sent' => 'Successfully marked credit as sent',
|
||||||
'email_subject_payment_partial' => 'Email Partial Payment Subject',
|
'email_subject_payment_partial' => 'Email Partial Payment Subject',
|
||||||
'is_approved' => 'Is Approved',
|
'is_approved' => 'Is Approved',
|
||||||
'migration_went_wrong' => 'Oops, something went wrong! Make sure you did proper setup with V2 of Invoice Ninja, before starting migration.',
|
'migration_went_wrong' => 'Oops, something went wrong! Please make sure you have setup an Invoice Ninja v5 instance before starting the migration.',
|
||||||
'cross_migration_message' => 'Cross account migration is not allowed. Please read more about it here: <a href="https://invoiceninja.github.io/cross-site-migration.html">https://invoiceninja.github.io/cross-site-migration.html</a>',
|
'cross_migration_message' => 'Cross account migration is not allowed. Please read more about it here: <a href="https://invoiceninja.github.io/docs/migration/#troubleshooting">https://invoiceninja.github.io/docs/migration/#troubleshooting</a>',
|
||||||
'email_credit' => 'Email Credit',
|
'email_credit' => 'Email Credit',
|
||||||
'client_email_not_set' => 'Client does not have an email address set',
|
'client_email_not_set' => 'Client does not have an email address set',
|
||||||
'ledger' => 'Ledger',
|
'ledger' => 'Ledger',
|
||||||
@ -3912,7 +3912,7 @@ $LANG = array(
|
|||||||
'show' => 'Show',
|
'show' => 'Show',
|
||||||
'empty_columns' => 'Empty Columns',
|
'empty_columns' => 'Empty Columns',
|
||||||
'project_name' => 'Project Name',
|
'project_name' => 'Project Name',
|
||||||
'counter_pattern_error' => 'To use :client_counter please add either :number or :id_number to prevent conflicts',
|
'counter_pattern_error' => 'To use :client_counter please add either :client_number or :client_id_number to prevent conflicts',
|
||||||
'this_quarter' => 'This Quarter',
|
'this_quarter' => 'This Quarter',
|
||||||
'to_update_run' => 'To update run',
|
'to_update_run' => 'To update run',
|
||||||
'registration_url' => 'Registration URL',
|
'registration_url' => 'Registration URL',
|
||||||
@ -3968,8 +3968,8 @@ $LANG = array(
|
|||||||
'list_of_recurring_invoices' => 'List of recurring invoices',
|
'list_of_recurring_invoices' => 'List of recurring invoices',
|
||||||
'details_of_recurring_invoice' => 'Here are some details about recurring invoice',
|
'details_of_recurring_invoice' => 'Here are some details about recurring invoice',
|
||||||
'cancellation' => 'Cancellation',
|
'cancellation' => 'Cancellation',
|
||||||
'about_cancellation' => 'In case you want to stop the recurring invoice, please click the request the cancellation.',
|
'about_cancellation' => 'In case you want to stop the recurring invoice,\n please click the request the cancellation.',
|
||||||
'cancellation_warning' => 'Warning! You are requesting a cancellation of this service. Your service may be cancelled with no further notification to you.',
|
'cancellation_warning' => 'Warning! You are requesting a cancellation of this service.\n Your service may be cancelled with no further notification to you.',
|
||||||
'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!',
|
'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!',
|
||||||
'list_of_payments' => 'List of payments',
|
'list_of_payments' => 'List of payments',
|
||||||
'payment_details' => 'Details of the payment',
|
'payment_details' => 'Details of the payment',
|
||||||
@ -4135,19 +4135,26 @@ $LANG = array(
|
|||||||
'payment_message_extended' => 'Thank you for your payment of :amount for :invoice',
|
'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.',
|
'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',
|
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method',
|
||||||
|
'vendor_address1' => 'Vendor Street',
|
||||||
/////////////////////////////////////////////////
|
'vendor_address2' => 'Vendor Apt/Suite',
|
||||||
|
'partially_unapplied' => 'Partially Unapplied',
|
||||||
|
'select_a_gmail_user' => 'Please select a user authenticated with Gmail',
|
||||||
|
'list_long_press' => 'List Long Press',
|
||||||
|
'show_actions' => 'Show Actions',
|
||||||
|
'start_multiselect' => 'Start Multiselect',
|
||||||
|
'email_sent_to_confirm_email' => 'An email has been sent to confirm the email address',
|
||||||
|
'converted_paid_to_date' => 'Converted Paid to Date',
|
||||||
|
'converted_credit_balance' => 'Converted Credit Balance',
|
||||||
|
'converted_total' => 'Converted Total',
|
||||||
|
'reply_to_name' => 'Reply-To Name',
|
||||||
|
'payment_status_-2' => 'Partially Unapplied',
|
||||||
|
'color_theme' => 'Color Theme',
|
||||||
'start_migration' => 'Start Migration',
|
'start_migration' => 'Start Migration',
|
||||||
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
||||||
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
||||||
'hello' => 'Hello',
|
'hello' => 'Hello',
|
||||||
'group_documents' => 'Group documents',
|
'group_documents' => 'Group documents',
|
||||||
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
|
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
|
||||||
|
|
||||||
'click_agree_to_accept_terms' => 'Click "Agree" to Accept Terms.',
|
|
||||||
'agree' => 'Agree',
|
|
||||||
|
|
||||||
'pending_approval' => 'Pending Approval',
|
|
||||||
'migration_select_company_label' => 'Select companies to migrate',
|
'migration_select_company_label' => 'Select companies to migrate',
|
||||||
'force_migration' => 'Force migration',
|
'force_migration' => 'Force migration',
|
||||||
'require_password_with_social_login' => 'Require Password with Social Login',
|
'require_password_with_social_login' => 'Require Password with Social Login',
|
||||||
@ -4167,6 +4174,32 @@ $LANG = array(
|
|||||||
'zoho' => 'Zoho',
|
'zoho' => 'Zoho',
|
||||||
'accounting' => 'Accounting',
|
'accounting' => 'Accounting',
|
||||||
'required_files_missing' => 'Please provide all CSVs.',
|
'required_files_missing' => 'Please provide all CSVs.',
|
||||||
|
'migration_auth_label' => 'Let\'s continue by authenticating.',
|
||||||
|
'api_secret' => 'API secret',
|
||||||
|
'migration_api_secret_notice' => 'You can find API_SECRET in the .env file or Invoice Ninja v5. If property is missing, leave field blank.',
|
||||||
|
'use_last_email' => 'Use last email',
|
||||||
|
'activate_company' => 'Activate Company',
|
||||||
|
'activate_company_help' => 'Enable emails, recurring invoices and notifications',
|
||||||
|
'an_error_occurred_try_again' => 'An error occurred, please try again',
|
||||||
|
'please_first_set_a_password' => 'Please first set a password',
|
||||||
|
'changing_phone_disables_two_factor' => 'Warning: Changing your phone number will disable 2FA',
|
||||||
|
'help_translate' => 'Help Translate',
|
||||||
|
'please_select_a_country' => 'Please select a country',
|
||||||
|
'disabled_two_factor' => 'Successfully disabled 2FA',
|
||||||
|
'connected_google' => 'Successfully connected account',
|
||||||
|
'disconnected_google' => 'Successfully disconnected account',
|
||||||
|
'delivered' => 'Delivered',
|
||||||
|
'spam' => 'Spam',
|
||||||
|
'view_docs' => 'View Docs',
|
||||||
|
'enter_phone_to_enable_two_factor' => 'Please provide a mobile phone number to enable two factor authentication',
|
||||||
|
'send_sms' => 'Send SMS',
|
||||||
|
'sms_code' => 'SMS Code',
|
||||||
|
'connect_google' => 'Connect Google',
|
||||||
|
'disconnect_google' => 'Disconnect Google',
|
||||||
|
'disable_two_factor' => 'Disable Two Factor',
|
||||||
|
'invoice_task_datelog' => 'Invoice Task Datelog',
|
||||||
|
'invoice_task_datelog_help' => 'Add date details to the invoice line items',
|
||||||
|
'promo_code' => 'Promo code',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
17
resources/views/billing-portal/purchase.blade.php
Normal file
17
resources/views/billing-portal/purchase.blade.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@extends('portal.ninja2020.layout.clean')
|
||||||
|
@section('meta_title', $billing_subscription->product->product_key)
|
||||||
|
|
||||||
|
@section('body')
|
||||||
|
@livewire('billing-portal-purchase', ['billing_subscription' => $billing_subscription, 'contact' => auth('contact')->user(), 'hash' => $hash])
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@push('footer')
|
||||||
|
<script>
|
||||||
|
function updateGatewayFields(companyGatewayId, paymentMethodId) {
|
||||||
|
document.getElementById('company_gateway_id').value = companyGatewayId;
|
||||||
|
document.getElementById('payment_method_id').value = paymentMethodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Livewire.on('beforePaymentEventsCompleted', () => document.getElementById('payment-method-form').submit());
|
||||||
|
</script>
|
||||||
|
@endpush
|
@ -10,7 +10,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
{{ $signature }}
|
{!! $signature !!}
|
||||||
</p>
|
</p>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
{{ $signature }}
|
{!! $signature !!}
|
||||||
</p>
|
</p>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
@if($signature)
|
@if($signature)
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<p>{{ $signature }}</p>
|
<p>{!! $signature !!}</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endif
|
@endif
|
||||||
|
@ -262,7 +262,7 @@
|
|||||||
|
|
||||||
<div class="contacts-wrapper">
|
<div class="contacts-wrapper">
|
||||||
<div class="contact-wrapper-left-side">
|
<div class="contact-wrapper-left-side">
|
||||||
<p class="contact-label">$to_label:</p>
|
<p class="contact-label">$from_label:</p>
|
||||||
<div class="company-info">
|
<div class="company-info">
|
||||||
<div id="company-details"></div>
|
<div id="company-details"></div>
|
||||||
<div id="company-address"></div>
|
<div id="company-address"></div>
|
||||||
@ -270,7 +270,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contact-wrapper-right-side">
|
<div class="contact-wrapper-right-side">
|
||||||
<p class="contact-label">$from_label:</p>
|
<p class="contact-label">$to_label:</p>
|
||||||
<div id="client-details"></div>
|
<div id="client-details"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
@include('portal.ninja2020.components.general.sidebar.mobile')
|
@include('portal.ninja2020.components.general.sidebar.mobile')
|
||||||
|
|
||||||
<!-- Static sidebar for desktop -->
|
<!-- Static sidebar for desktop -->
|
||||||
@include('portal.ninja2020.components.general.sidebar.desktop')
|
@unless(request()->query('sidebar') === 'hidden')
|
||||||
|
@include('portal.ninja2020.components.general.sidebar.desktop')
|
||||||
|
@endunless
|
||||||
|
|
||||||
<div class="flex flex-col w-0 flex-1 overflow-hidden">
|
<div class="flex flex-col w-0 flex-1 overflow-hidden">
|
||||||
@include('portal.ninja2020.components.general.sidebar.header')
|
@include('portal.ninja2020.components.general.sidebar.header')
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
<div class="grid grid-cols-12">
|
||||||
|
<div class="col-span-12 lg:col-span-6 bg-gray-50 shadow-lg lg:h-screen flex flex-col items-center">
|
||||||
|
<div class="w-full p-10 lg:w-1/2 lg:mt-48 lg:p-0">
|
||||||
|
<img class="h-8" src="{{ $billing_subscription->company->present()->logo }}"
|
||||||
|
alt="{{ $billing_subscription->company->present()->name }}">
|
||||||
|
|
||||||
|
<h1 id="billing-page-company-logo" class="text-3xl font-bold tracking-wide mt-8">
|
||||||
|
{{ $billing_subscription->product->product_key }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="my-6">{{ $billing_subscription->product->notes }}</p>
|
||||||
|
|
||||||
|
<span class="text-sm uppercase font-bold">{{ ctrans('texts.total') }}:</span>
|
||||||
|
|
||||||
|
<h1 class="text-2xl font-bold tracking-wide">{{ App\Utils\Number::formatMoney($billing_subscription->product->price, $billing_subscription->company) }}</h1>
|
||||||
|
|
||||||
|
@if(auth('contact')->user())
|
||||||
|
<a href="{{ route('client.invoices.index') }}" class="block mt-16 inline-flex items-center space-x-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="feather feather-arrow-left">
|
||||||
|
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||||
|
<polyline points="12 19 5 12 12 5"></polyline>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<span>{{ ctrans('texts.client_portal') }}</span>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-span-12 lg:col-span-6 bg-white lg:shadow-lg lg:h-screen">
|
||||||
|
<div class="grid grid-cols-12 flex flex-col p-10 lg:mt-48 lg:ml-16">
|
||||||
|
<div class="col-span-12 w-full lg:col-span-6">
|
||||||
|
<h2 class="text-2xl font-bold tracking-wide">{{ $heading_text }}</h2>
|
||||||
|
@if (session()->has('message'))
|
||||||
|
@component('portal.ninja2020.components.message')
|
||||||
|
{{ session('message') }}
|
||||||
|
@endcomponent
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($this->steps['fetched_payment_methods'])
|
||||||
|
<div class="flex items-center mt-4 text-sm">
|
||||||
|
<form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}"
|
||||||
|
method="post"
|
||||||
|
id="payment-method-form">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
@if($invoice instanceof \App\Models\Invoice)
|
||||||
|
<input type="hidden" name="invoices[]" value="{{ $invoice->hashed_id }}">
|
||||||
|
<input type="hidden" name="payable_invoices[0][amount]"
|
||||||
|
value="{{ $invoice->partial > 0 ? \App\Utils\Number::formatValue($invoice->partial, $invoice->client->currency()) : \App\Utils\Number::formatValue($invoice->balance, $invoice->client->currency()) }}">
|
||||||
|
<input type="hidden" name="payable_invoices[0][invoice_id]"
|
||||||
|
value="{{ $invoice->hashed_id }}">
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<input type="hidden" name="action" value="payment">
|
||||||
|
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}"/>
|
||||||
|
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@foreach($this->methods as $method)
|
||||||
|
<button
|
||||||
|
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}')"
|
||||||
|
class="px-3 py-2 border rounded mr-4 hover:border-blue-600">
|
||||||
|
{{ $method['label'] }}
|
||||||
|
</button>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<form wire:submit.prevent="authenticate" class="mt-8">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<label for="email_address">
|
||||||
|
<span class="input-label">{{ ctrans('texts.email_address') }}</span>
|
||||||
|
<input wire:model.defer="email" type="email" class="input w-full"/>
|
||||||
|
|
||||||
|
@error('email')
|
||||||
|
<p class="validation validation-fail block w-full" role="alert">
|
||||||
|
{{ $message }}
|
||||||
|
</p>
|
||||||
|
@enderror
|
||||||
|
</label>
|
||||||
|
|
||||||
|
@if($steps['existing_user'])
|
||||||
|
<label for="password" class="block mt-2">
|
||||||
|
<span class="input-label">{{ ctrans('texts.password') }}</span>
|
||||||
|
<input wire:model.defer="password" type="password" class="input w-full" autofocus/>
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<p class="validation validation-fail block w-full" role="alert">
|
||||||
|
{{ $message }}
|
||||||
|
</p>
|
||||||
|
@enderror
|
||||||
|
</label>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<button type="submit"
|
||||||
|
class="button button-block bg-primary text-white mt-4">{{ ctrans('texts.next') }}</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="relative mt-8">
|
||||||
|
<div class="absolute inset-0 flex items-center">
|
||||||
|
<div class="w-full border-t border-gray-300"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative flex justify-center text-sm leading-5">
|
||||||
|
<span class="px-2 text-gray-700 bg-white">Have a coupon code?</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form wire:submit.prevent="applyCouponCode" class="mt-4">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<label class="w-full mr-2">
|
||||||
|
<input type="text" wire:model.defer="coupon" class="input w-full m-0" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button class="button bg-primary m-0 text-white">{{ ctrans('texts.apply') }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -63,6 +63,8 @@
|
|||||||
{{-- Feel free to push anything to header using @push('header') --}}
|
{{-- Feel free to push anything to header using @push('header') --}}
|
||||||
@stack('head')
|
@stack('head')
|
||||||
|
|
||||||
|
@livewireStyles
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.css" />
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -77,6 +79,8 @@
|
|||||||
|
|
||||||
@yield('body')
|
@yield('body')
|
||||||
|
|
||||||
|
@livewireScripts
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js" data-cfasync="false"></script>
|
<script src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js" data-cfasync="false"></script>
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener("load", function(){
|
window.addEventListener("load", function(){
|
||||||
|
@ -31,10 +31,13 @@
|
|||||||
<div>
|
<div>
|
||||||
@yield('gateway_content')
|
@yield('gateway_content')
|
||||||
</div>
|
</div>
|
||||||
<span class="block mx-4 mb-4 text-xs inline-flex items-center">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-green-600"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
@if(Request::isSecure())
|
||||||
<span class="ml-1">Secure 256-bit encryption</span>
|
<span class="block mx-4 mb-4 text-xs inline-flex items-center">
|
||||||
</span>
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-green-600"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
||||||
|
<span class="ml-1">Secure 256-bit encryption</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
-- Commands to create a MySQL database and user
|
-- Commands to create a MySQL database and user
|
||||||
CREATE SCHEMA `db-ninja-01` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
|
CREATE SCHEMA `db-ninja-01` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
CREATE USER 'ninja'@'localhost' IDENTIFIED BY 'ninja';
|
CREATE USER 'ninja'@'localhost' IDENTIFIED BY 'ninja';
|
||||||
GRANT ALL PRIVILEGES ON `ninja`.* TO 'ninja'@'localhost';
|
GRANT ALL PRIVILEGES ON `db-ninja-01`.* TO 'ninja'@'localhost';
|
||||||
FLUSH PRIVILEGES;
|
FLUSH PRIVILEGES;
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
|
@ -76,6 +76,8 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence
|
|||||||
Route::get('logout', 'Auth\ContactLoginController@logout')->name('logout');
|
Route::get('logout', 'Auth\ContactLoginController@logout')->name('logout');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::get('client/subscription/{billing_subscription}/purchase', 'ClientPortal\BillingSubscriptionPurchaseController@index')->name('client.subscription.purchase');
|
||||||
|
|
||||||
Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
|
Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
|
||||||
/*Invitation catches*/
|
/*Invitation catches*/
|
||||||
Route::get('recurring_invoice/{invitation_key}', 'ClientPortal\InvitationController@recurringRouter');
|
Route::get('recurring_invoice/{invitation_key}', 'ClientPortal\InvitationController@recurringRouter');
|
||||||
|
@ -54,6 +54,28 @@ class ClientApiTest extends TestCase
|
|||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDuplicateNumberCatch()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'name' => $this->faker->firstName,
|
||||||
|
'number' => 'iamaduplicate',
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/clients', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/clients', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
public function testClientPut()
|
public function testClientPut()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
@ -67,6 +89,20 @@ class ClientApiTest extends TestCase
|
|||||||
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
|
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/clients/', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(302);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClientGet()
|
public function testClientGet()
|
||||||
|
@ -131,4 +131,80 @@ class CreditTest extends TestCase
|
|||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDuplicateNumberCatch()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'status_id' => 1,
|
||||||
|
'number' => 'dfdfd',
|
||||||
|
'discount' => 0,
|
||||||
|
'is_amount_discount' => 1,
|
||||||
|
'number' => '3434343',
|
||||||
|
'public_notes' => 'notes',
|
||||||
|
'is_deleted' => 0,
|
||||||
|
'custom_value1' => 0,
|
||||||
|
'custom_value2' => 0,
|
||||||
|
'custom_value3' => 0,
|
||||||
|
'custom_value4' => 0,
|
||||||
|
'status' => 1,
|
||||||
|
'client_id' => $this->encodePrimaryKey($this->client->id),
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/credits', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/credits', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreditPut()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'status_id' => 1,
|
||||||
|
'number' => 'dfdfd',
|
||||||
|
'discount' => 0,
|
||||||
|
'is_amount_discount' => 1,
|
||||||
|
'number' => '3434343',
|
||||||
|
'public_notes' => 'notes',
|
||||||
|
'is_deleted' => 0,
|
||||||
|
'custom_value1' => 0,
|
||||||
|
'custom_value2' => 0,
|
||||||
|
'custom_value3' => 0,
|
||||||
|
'custom_value4' => 0,
|
||||||
|
'status' => 1,
|
||||||
|
'client_id' => $this->encodePrimaryKey($this->client->id),
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/credits/'.$this->encodePrimaryKey($this->credit->id), $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->put('/api/v1/credits/'.$this->encodePrimaryKey($this->credit->id), $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/credits/', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user