mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
b23df66eb6
@ -1 +1 @@
|
|||||||
5.1.25
|
5.1.26
|
@ -68,7 +68,7 @@ class Kernel extends ConsoleKernel
|
|||||||
/* Run queue's with this*/
|
/* Run queue's with this*/
|
||||||
if (Ninja::isSelfHost()) {
|
if (Ninja::isSelfHost()) {
|
||||||
|
|
||||||
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
$schedule->command('queue:work --daemon')->everyMinute()->withoutOverlapping();
|
||||||
|
|
||||||
//we need to add this as we are seeing cached queues mess up the system on first load.
|
//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->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
||||||
|
@ -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',
|
||||||
|
@ -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,40 @@ 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)
|
||||||
|
{
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
|
||||||
|
if(!$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'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$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 +206,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 +294,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,6 +309,7 @@ class LoginController extends BaseController
|
|||||||
->where('user_id', auth()->user()->id);
|
->where('user_id', auth()->user()->id);
|
||||||
|
|
||||||
return $this->listResponse($cu);
|
return $this->listResponse($cu);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -47,17 +47,27 @@ 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);
|
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' => 'No phone record or user is not confirmed'], 400);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,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) {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
@ -118,6 +119,9 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
|
|
||||||
$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);
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
try{
|
||||||
Mail::to($this->user->email, $this->user->name())
|
Mail::to($this->user->email, $this->user->name())
|
||||||
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
->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){
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
111
app/Services/BillingSubscription/BillingSubscriptionService.php
Normal file
111
app/Services/BillingSubscription/BillingSubscriptionService.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?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\Product;
|
||||||
|
use App\Repositories\InvoiceRepository;
|
||||||
|
|
||||||
|
class BillingSubscriptionService
|
||||||
|
{
|
||||||
|
|
||||||
|
private $billing_subscription;
|
||||||
|
|
||||||
|
public function __construct(BillingSubscription $billing_subscription)
|
||||||
|
{
|
||||||
|
$this->billing_subscription = $billing_subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createInvoice($data)
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice_repo = new InvoiceRepository();
|
||||||
|
|
||||||
|
// $data = [
|
||||||
|
// 'client_id' =>,
|
||||||
|
// 'date' => Y-m-d,
|
||||||
|
// 'invitations' => [
|
||||||
|
// 'client_contact_id' => hashed_id
|
||||||
|
// ],
|
||||||
|
// 'line_items' => [],
|
||||||
|
// ];
|
||||||
|
|
||||||
|
$invoice = $invoice_repo->save($data, InvoiceFactory::create($this->billing_subscription->company_id, $this->billing_subscription->user_id));
|
||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createLineItems($quantity)
|
||||||
|
{
|
||||||
|
$line_items = [];
|
||||||
|
|
||||||
|
$product = $this->billing_subscription->product;
|
||||||
|
|
||||||
|
$item = new InvoiceItem;
|
||||||
|
$item->quantity = $quantity;
|
||||||
|
$item->product_key = $product->product_key;
|
||||||
|
$item->notes = $product->notes;
|
||||||
|
$item->cost = $product->price;
|
||||||
|
//$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.
|
||||||
|
|
||||||
|
return $line_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -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'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
public function includePayments(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$transformer = new PaymentTransformer($this->account, $this->serializer, $invoice);
|
$transformer = new PaymentTransformer( $this->serializer);
|
||||||
|
|
||||||
return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT);
|
return $this->includeCollection($invoice->payments, $transformer, Payment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public function includeExpenses(Invoice $invoice)
|
public function includeExpenses(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$transformer = new ExpenseTransformer($this->account, $this->serializer);
|
$transformer = new ExpenseTransformer($this->account, $this->serializer);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
"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",
|
||||||
|
@ -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.25',
|
'app_version' => '5.1.26',
|
||||||
'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,5 @@ 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),
|
||||||
];
|
];
|
||||||
|
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": "1edd6ac83b22ed1c401a76cefe7eaa7d",
|
||||||
"version.json": "b7c8971e1ab5b627fd2a4317c52b843e",
|
"version.json": "b7c8971e1ab5b627fd2a4317c52b843e",
|
||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
|
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
|
||||||
};
|
};
|
||||||
|
195439
public/main.dart.js
vendored
195439
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -4167,6 +4167,9 @@ $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.',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user