mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-16 16:13:20 +01:00
commit
b244555fec
@ -1 +1 @@
|
|||||||
5.1.65
|
5.1.67
|
@ -65,7 +65,7 @@ class CheckData extends Command
|
|||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $name = 'ninja:check-data';
|
protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
|
@ -34,6 +34,7 @@ use App\Models\Project;
|
|||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
|
use App\Models\TaxRate;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Vendor;
|
use App\Models\Vendor;
|
||||||
use App\Models\VendorContact;
|
use App\Models\VendorContact;
|
||||||
@ -109,6 +110,14 @@ class CreateSingleAccount extends Command
|
|||||||
'portal_domain' => 'http://ninja.test:8000',
|
'portal_domain' => 'http://ninja.test:8000',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$settings = $company->settings;
|
||||||
|
$settings->invoice_terms = 'Default company invoice terms';
|
||||||
|
$settings->quote_terms = 'Default company quote terms';
|
||||||
|
$settings->invoice_footer = 'Default invoice footer';
|
||||||
|
|
||||||
|
$company->settings = $settings;
|
||||||
|
$company->save();
|
||||||
|
|
||||||
$account->default_company_id = $company->id;
|
$account->default_company_id = $company->id;
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
@ -146,6 +155,29 @@ class CreateSingleAccount extends Command
|
|||||||
'company_id' => $company->id,
|
'company_id' => $company->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
TaxRate::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'name' => 'GST',
|
||||||
|
'rate' => 10
|
||||||
|
]);
|
||||||
|
|
||||||
|
TaxRate::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'name' => 'VAT',
|
||||||
|
'rate' => 17.5
|
||||||
|
]);
|
||||||
|
|
||||||
|
TaxRate::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'name' => 'CA Sales Tax',
|
||||||
|
'rate' => 5
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
$this->info('Creating '.$this->count.' clients');
|
$this->info('Creating '.$this->count.' clients');
|
||||||
|
|
||||||
for ($x = 0; $x < $this->count; $x++) {
|
for ($x = 0; $x < $this->count; $x++) {
|
||||||
|
@ -12,12 +12,12 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Jobs\Ninja\SendReminders;
|
use App\Jobs\Ninja\SendReminders;
|
||||||
use App\Jobs\Util\WebHookHandler;
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Models\Webhook;
|
use App\Models\Webhook;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use App\Jobs\Util\WebhookHandler;
|
||||||
|
|
||||||
class SendRemindersCron extends Command
|
class SendRemindersCron extends Command
|
||||||
{
|
{
|
||||||
@ -90,6 +90,7 @@ class SendRemindersCron extends Command
|
|||||||
|
|
||||||
$invoices->each(function ($invoice) {
|
$invoices->each(function ($invoice) {
|
||||||
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$quotes = Quote::where('is_deleted', 0)
|
$quotes = Quote::where('is_deleted', 0)
|
||||||
|
@ -11,8 +11,9 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
use App\Jobs\Cron\SubscriptionCron;
|
use App\Jobs\Cron\AutoBillCron;
|
||||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||||
|
use App\Jobs\Cron\SubscriptionCron;
|
||||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||||
use App\Jobs\Ninja\CompanySizeCheck;
|
use App\Jobs\Ninja\CompanySizeCheck;
|
||||||
use App\Jobs\Util\ReminderJob;
|
use App\Jobs\Util\ReminderJob;
|
||||||
@ -46,7 +47,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
$schedule->job(new VersionCheck)->daily();
|
$schedule->job(new VersionCheck)->daily();
|
||||||
|
|
||||||
$schedule->command('ninja:check-data')->daily()->withoutOverlapping();
|
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->job(new ReminderJob)->daily()->withoutOverlapping();
|
$schedule->job(new ReminderJob)->daily()->withoutOverlapping();
|
||||||
|
|
||||||
@ -58,6 +59,8 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
||||||
|
|
||||||
|
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
||||||
|
|
||||||
/* Run hosted specific jobs */
|
/* Run hosted specific jobs */
|
||||||
@ -65,6 +68,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
$schedule->job(new AdjustEmailQuota)->daily()->withoutOverlapping();
|
$schedule->job(new AdjustEmailQuota)->daily()->withoutOverlapping();
|
||||||
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||||
|
$schedule->command('ninja:check-data --database=db-ninja-02')->daily()->withoutOverlapping();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $schedule_reminder2 = ''; // (enum: after_invoice_date, before_due_date, after_due_date) implmemented
|
public $schedule_reminder2 = ''; // (enum: after_invoice_date, before_due_date, after_due_date) implmemented
|
||||||
public $schedule_reminder3 = ''; // (enum: after_invoice_date, before_due_date, after_due_date) implmemented
|
public $schedule_reminder3 = ''; // (enum: after_invoice_date, before_due_date, after_due_date) implmemented
|
||||||
|
|
||||||
public $reminder_send_time = 32400; //number of seconds from UTC +0 to send reminders @TODO
|
public $reminder_send_time = 0; //number of seconds from UTC +0 to send reminders @TODO
|
||||||
|
|
||||||
public $late_fee_amount1 = 0; //@implemented
|
public $late_fee_amount1 = 0; //@implemented
|
||||||
public $late_fee_amount2 = 0; //@implemented
|
public $late_fee_amount2 = 0; //@implemented
|
||||||
@ -245,8 +245,8 @@ class CompanySettings extends BaseSettings
|
|||||||
|
|
||||||
public $hide_paid_to_date = false; //@TODO where?
|
public $hide_paid_to_date = false; //@TODO where?
|
||||||
public $embed_documents = false; //@TODO where?
|
public $embed_documents = false; //@TODO where?
|
||||||
public $all_pages_header = false; //@implemented
|
public $all_pages_header = false; //@deprecated 31-05-2021
|
||||||
public $all_pages_footer = false; //@implemented
|
public $all_pages_footer = false; //@deprecated 31-05-2021
|
||||||
public $pdf_variables = ''; //@implemented
|
public $pdf_variables = ''; //@implemented
|
||||||
|
|
||||||
public $portal_custom_head = ''; //@TODO @BEN
|
public $portal_custom_head = ''; //@TODO @BEN
|
||||||
@ -667,8 +667,9 @@ class CompanySettings extends BaseSettings
|
|||||||
'$custom_surcharge4',
|
'$custom_surcharge4',
|
||||||
'$total_taxes',
|
'$total_taxes',
|
||||||
'$line_taxes',
|
'$line_taxes',
|
||||||
'$paid_to_date',
|
|
||||||
'$total',
|
'$total',
|
||||||
|
'$paid_to_date',
|
||||||
|
'$outstanding',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
10
app/Exceptions/ImportCompanyFailed.php
Normal file
10
app/Exceptions/ImportCompanyFailed.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ImportCompanyFailed extends Exception
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
}
|
@ -12,7 +12,9 @@
|
|||||||
namespace App\Factory;
|
namespace App\Factory;
|
||||||
|
|
||||||
use App\DataMapper\CompanySettings;
|
use App\DataMapper\CompanySettings;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class CompanyFactory
|
class CompanyFactory
|
||||||
@ -33,7 +35,12 @@ class CompanyFactory
|
|||||||
$company->db = config('database.default');
|
$company->db = config('database.default');
|
||||||
//$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'];
|
//$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'];
|
||||||
$company->custom_fields = (object) [];
|
$company->custom_fields = (object) [];
|
||||||
|
|
||||||
|
if(Ninja::isHosted())
|
||||||
|
$company->subdomain = MultiDB::randomSubdomainGenerator();
|
||||||
|
else
|
||||||
$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 = 1800000;
|
$company->default_password_timeout = 1800000;
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ class AccountController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function store(CreateAccountRequest $request)
|
public function store(CreateAccountRequest $request)
|
||||||
{
|
{
|
||||||
$account = CreateAccount::dispatchNow($request->all());
|
$account = CreateAccount::dispatchNow($request->all(), $request->getClientIp());
|
||||||
|
|
||||||
if (! ($account instanceof Account)) {
|
if (! ($account instanceof Account)) {
|
||||||
return $account;
|
return $account;
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Account;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -50,11 +51,15 @@ class ContactForgotPasswordController extends Controller
|
|||||||
*
|
*
|
||||||
* @return Factory|View
|
* @return Factory|View
|
||||||
*/
|
*/
|
||||||
public function showLinkRequestForm()
|
public function showLinkRequestForm(Request $request)
|
||||||
{
|
{
|
||||||
|
$account_id = $request->get('account_id');
|
||||||
|
$account = Account::find($account_id);
|
||||||
|
|
||||||
return $this->render('auth.passwords.request', [
|
return $this->render('auth.passwords.request', [
|
||||||
'title' => 'Client Password Reset',
|
'title' => 'Client Password Reset',
|
||||||
'passwordEmailRoute' => 'client.password.email',
|
'passwordEmailRoute' => 'client.password.email',
|
||||||
|
'account' => $account
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use App\Events\Contact\ContactLoggedIn;
|
use App\Events\Contact\ContactLoggedIn;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Account;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use Auth;
|
use Auth;
|
||||||
@ -31,9 +32,13 @@ class ContactLoginController extends Controller
|
|||||||
$this->middleware('guest:contact', ['except' => ['logout']]);
|
$this->middleware('guest:contact', ['except' => ['logout']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showLoginForm()
|
public function showLoginForm(Request $request)
|
||||||
{
|
{
|
||||||
return $this->render('auth.login');
|
$account_id = $request->get('account_id');
|
||||||
|
$account = Account::find($account_id);
|
||||||
|
|
||||||
|
return $this->render('auth.login', ['account' => $account]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(Request $request)
|
public function login(Request $request)
|
||||||
|
@ -24,7 +24,7 @@ class ContactRegisterController extends Controller
|
|||||||
|
|
||||||
$company = Company::where('company_key', $key)->firstOrFail();
|
$company = Company::where('company_key', $key)->firstOrFail();
|
||||||
|
|
||||||
return render('auth.register', ['company' => $company]);
|
return render('auth.register', ['company' => $company, 'account' => $company->account]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(RegisterRequest $request)
|
public function register(RegisterRequest $request)
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Account;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -62,8 +63,11 @@ class ContactResetPasswordController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function showResetForm(Request $request, $token = null)
|
public function showResetForm(Request $request, $token = null)
|
||||||
{
|
{
|
||||||
|
$account_id = $request->get('account_id');
|
||||||
|
$account = Account::find($account_id);
|
||||||
|
|
||||||
return $this->render('auth.passwords.reset')->with(
|
return $this->render('auth.passwords.reset')->with(
|
||||||
['token' => $token, 'email' => $request->email]
|
['token' => $token, 'email' => $request->email, 'account' => $account]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Account;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Password;
|
use Illuminate\Support\Facades\Password;
|
||||||
@ -104,9 +105,8 @@ class ForgotPasswordController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function sendResetLinkEmail(Request $request)
|
public function sendResetLinkEmail(Request $request)
|
||||||
{
|
{
|
||||||
// MultiDB::userFindAndSetDb($request->input('email'));
|
MultiDB::userFindAndSetDb($request->input('email'));
|
||||||
|
$user = MultiDB::hasUser(['email' => $request->input('email')]);
|
||||||
// $user = MultiDB::hasUser(['email' => $request->input('email')]);
|
|
||||||
|
|
||||||
$this->validateEmail($request);
|
$this->validateEmail($request);
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ use App\Models\User;
|
|||||||
use App\Transformers\CompanyUserTransformer;
|
use App\Transformers\CompanyUserTransformer;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\UserSessionAttributes;
|
use App\Utils\Traits\UserSessionAttributes;
|
||||||
|
use App\Utils\Traits\User\LoginCache;
|
||||||
use Google_Client;
|
use Google_Client;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -55,6 +56,7 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
use AuthenticatesUsers;
|
use AuthenticatesUsers;
|
||||||
use UserSessionAttributes;
|
use UserSessionAttributes;
|
||||||
|
use LoginCache;
|
||||||
|
|
||||||
protected $entity_type = CompanyUser::class;
|
protected $entity_type = CompanyUser::class;
|
||||||
|
|
||||||
@ -178,8 +180,7 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
|
event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
|
||||||
|
|
||||||
//if user has 2fa enabled - lets check this now:
|
//2FA
|
||||||
|
|
||||||
if($user->google_2fa_secret && $request->has('one_time_password'))
|
if($user->google_2fa_secret && $request->has('one_time_password'))
|
||||||
{
|
{
|
||||||
$google2fa = new Google2FA();
|
$google2fa = new Google2FA();
|
||||||
@ -203,14 +204,7 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
$user->setCompany($user->account->default_company);
|
$user->setCompany($user->account->default_company);
|
||||||
|
|
||||||
$timeout = $user->company()->default_password_timeout;
|
$this->setLoginCache($user);
|
||||||
|
|
||||||
if($timeout == 0)
|
|
||||||
$timeout = 30*60*1000*1000;
|
|
||||||
else
|
|
||||||
$timeout = $timeout/1000;
|
|
||||||
|
|
||||||
Cache::put($user->hashed_id.'_'.$user->account_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);
|
||||||
@ -228,7 +222,7 @@ class LoginController extends BaseController
|
|||||||
});
|
});
|
||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
// return $this->listResponse($cu);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -351,6 +345,7 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
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',
|
||||||
@ -361,14 +356,7 @@ class LoginController extends BaseController
|
|||||||
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);
|
||||||
|
|
||||||
$timeout = $existing_user->company()->default_password_timeout;
|
$this->setLoginCache($existing_user);
|
||||||
|
|
||||||
if($timeout == 0)
|
|
||||||
$timeout = 30*60*1000*1000;
|
|
||||||
else
|
|
||||||
$timeout = $timeout/1000;
|
|
||||||
|
|
||||||
Cache::put($existing_user->hashed_id.'_'.$existing_user->account_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);
|
||||||
@ -384,10 +372,68 @@ class LoginController extends BaseController
|
|||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If this is a result user/email combo - lets add their OAuth details details
|
||||||
|
if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
|
||||||
|
{
|
||||||
|
Auth::login($existing_login_user, true);
|
||||||
|
$existing_login_user->setCompany($existing_login_user->account->default_company);
|
||||||
|
|
||||||
|
$this->setLoginCache($existing_login_user);
|
||||||
|
|
||||||
|
auth()->user()->update([
|
||||||
|
'oauth_user_id' => $google->harvestSubField($user),
|
||||||
|
'oauth_provider_id'=> 'google',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$cu = CompanyUser::query()
|
||||||
|
->where('user_id', auth()->user()->id);
|
||||||
|
|
||||||
|
$cu->first()->account->companies->each(function ($company) use($cu){
|
||||||
|
|
||||||
|
if($company->tokens()->where('is_system', true)->count() == 0)
|
||||||
|
{
|
||||||
|
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->timeConstrainedResponse($cu);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
|
|
||||||
|
//check the user doesn't already exist in some form
|
||||||
|
|
||||||
|
if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
|
||||||
|
{
|
||||||
|
Auth::login($existing_login_user, true);
|
||||||
|
$existing_login_user->setCompany($existing_login_user->account->default_company);
|
||||||
|
|
||||||
|
$this->setLoginCache($existing_login_user);
|
||||||
|
|
||||||
|
auth()->user()->update([
|
||||||
|
'oauth_user_id' => $google->harvestSubField($user),
|
||||||
|
'oauth_provider_id'=> 'google',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$cu = CompanyUser::query()
|
||||||
|
->where('user_id', auth()->user()->id);
|
||||||
|
|
||||||
|
$cu->first()->account->companies->each(function ($company) use($cu){
|
||||||
|
|
||||||
|
if($company->tokens()->where('is_system', true)->count() == 0)
|
||||||
|
{
|
||||||
|
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->timeConstrainedResponse($cu);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//user not found anywhere - lets sign them up.
|
||||||
$name = OAuth::splitName($google->harvestName($user));
|
$name = OAuth::splitName($google->harvestName($user));
|
||||||
|
|
||||||
$new_account = [
|
$new_account = [
|
||||||
@ -403,21 +449,14 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
MultiDB::setDefaultDatabase();
|
MultiDB::setDefaultDatabase();
|
||||||
|
|
||||||
$account = CreateAccount::dispatchNow($new_account);
|
$account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
|
||||||
|
|
||||||
Auth::login($account->default_company->owner(), true);
|
Auth::login($account->default_company->owner(), true);
|
||||||
|
|
||||||
auth()->user()->email_verified_at = now();
|
auth()->user()->email_verified_at = now();
|
||||||
auth()->user()->save();
|
auth()->user()->save();
|
||||||
|
|
||||||
$timeout = auth()->user()->company()->default_password_timeout;
|
$this->setLoginCache(auth()->user());
|
||||||
|
|
||||||
if($timeout == 0)
|
|
||||||
$timeout = 30*60*1000*1000;
|
|
||||||
else
|
|
||||||
$timeout = $timeout/1000;
|
|
||||||
|
|
||||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
|
||||||
|
|
||||||
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
||||||
|
|
||||||
@ -437,4 +476,62 @@ class LoginController extends BaseController
|
|||||||
->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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function redirectToProvider(string $provider)
|
||||||
|
{
|
||||||
|
//'https://www.googleapis.com/auth/gmail.send','email','profile','openid'
|
||||||
|
$scopes = [];
|
||||||
|
|
||||||
|
if($provider == 'google'){
|
||||||
|
$scopes = ['https://www.googleapis.com/auth/gmail.send','email','profile','openid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request()->has('code')) {
|
||||||
|
return $this->handleProviderCallback($provider);
|
||||||
|
} else {
|
||||||
|
return Socialite::driver($provider)->scopes($scopes)->redirect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleProviderCallback(string $provider)
|
||||||
|
{
|
||||||
|
$socialite_user = Socialite::driver($provider)
|
||||||
|
->stateless()
|
||||||
|
->user();
|
||||||
|
|
||||||
|
// if($user = OAuth::handleAuth($socialite_user, $provider))
|
||||||
|
// {
|
||||||
|
// Auth::login($user, true);
|
||||||
|
|
||||||
|
// return redirect($this->redirectTo);
|
||||||
|
// }
|
||||||
|
// else if(MultiDB::checkUserEmailExists($socialite_user->getEmail()))
|
||||||
|
// {
|
||||||
|
// Session::flash('error', 'User exists in system, but not with this authentication method'); //todo add translations
|
||||||
|
|
||||||
|
// return view('auth.login');
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// //todo
|
||||||
|
// $name = OAuth::splitName($socialite_user->getName());
|
||||||
|
|
||||||
|
// $new_account = [
|
||||||
|
// 'first_name' => $name[0],
|
||||||
|
// 'last_name' => $name[1],
|
||||||
|
// 'password' => '',
|
||||||
|
// 'email' => $socialite_user->getEmail(),
|
||||||
|
// 'oauth_user_id' => $socialite_user->getId(),
|
||||||
|
// 'oauth_provider_id' => $provider
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// $account = CreateAccount::dispatchNow($new_account);
|
||||||
|
|
||||||
|
// Auth::login($account->default_company->owner(), true);
|
||||||
|
|
||||||
|
// $cookie = cookie('db', $account->default_company->db);
|
||||||
|
|
||||||
|
// return redirect($this->redirectTo)->withCookie($cookie);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ class BaseController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function notFoundClient()
|
public function notFoundClient()
|
||||||
{
|
{
|
||||||
return abort(404);
|
abort(404, 'Page not found in client portal.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,10 +309,6 @@ class BaseController extends Controller
|
|||||||
},
|
},
|
||||||
'company.tax_rates' => function ($query) use ($updated_at, $user) {
|
'company.tax_rates' => function ($query) use ($updated_at, $user) {
|
||||||
$query->where('updated_at', '>=', $updated_at);
|
$query->where('updated_at', '>=', $updated_at);
|
||||||
|
|
||||||
if(!$user->isAdmin())
|
|
||||||
$query->where('tax_rates.user_id', $user->id);
|
|
||||||
|
|
||||||
},
|
},
|
||||||
'company.vendors'=> function ($query) use ($updated_at, $user) {
|
'company.vendors'=> function ($query) use ($updated_at, $user) {
|
||||||
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
||||||
@ -323,15 +319,9 @@ class BaseController extends Controller
|
|||||||
},
|
},
|
||||||
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
||||||
$query->where('updated_at', '>=', $updated_at);
|
$query->where('updated_at', '>=', $updated_at);
|
||||||
|
|
||||||
if(!$user->isAdmin())
|
|
||||||
$query->where('expense_categories.user_id', $user->id);
|
|
||||||
|
|
||||||
},
|
},
|
||||||
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
||||||
$query->where('updated_at', '>=', $updated_at);
|
$query->where('updated_at', '>=', $updated_at);
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
'company.activities'=> function ($query) use($user) {
|
'company.activities'=> function ($query) use($user) {
|
||||||
|
|
||||||
|
@ -585,4 +585,61 @@ class ClientController extends BaseController
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param UploadClientRequest $request
|
||||||
|
* @param Client $client
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @OA\Put(
|
||||||
|
* path="/api/v1/clients/{id}/adjust_ledger",
|
||||||
|
* operationId="adjustLedger",
|
||||||
|
* tags={"clients"},
|
||||||
|
* summary="Adjust the client ledger to rebalance",
|
||||||
|
* description="Adjust the client ledger to rebalance",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||||
|
* @OA\Parameter(
|
||||||
|
* name="id",
|
||||||
|
* in="path",
|
||||||
|
* description="The Client Hashed ID",
|
||||||
|
* example="D2J234DFA",
|
||||||
|
* required=true,
|
||||||
|
* @OA\Schema(
|
||||||
|
* type="string",
|
||||||
|
* format="string",
|
||||||
|
* ),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="Returns the client object",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Client"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
*
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function adjustLedger(Request $request, Client $client)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class DocumentController extends Controller
|
|||||||
|
|
||||||
$documents->map(function ($document) {
|
$documents->map(function ($document) {
|
||||||
if (auth()->user('contact')->client->id != $document->documentable->id) {
|
if (auth()->user('contact')->client->id != $document->documentable->id) {
|
||||||
abort(401);
|
abort(401, 'Permission denied');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class EntityViewController extends Controller
|
|||||||
public function index(string $entity_type, string $invitation_key)
|
public function index(string $entity_type, string $invitation_key)
|
||||||
{
|
{
|
||||||
if (! in_array($entity_type, $this->entity_types)) {
|
if (! in_array($entity_type, $this->entity_types)) {
|
||||||
abort(404);
|
abort(404, 'Entity not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type));
|
$invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type));
|
||||||
@ -91,7 +91,7 @@ class EntityViewController extends Controller
|
|||||||
public function handlePassword(string $entity_type, string $invitation_key)
|
public function handlePassword(string $entity_type, string $invitation_key)
|
||||||
{
|
{
|
||||||
if (! in_array($entity_type, $this->entity_types)) {
|
if (! in_array($entity_type, $this->entity_types)) {
|
||||||
abort(404);
|
abort(404, 'Entity not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type));
|
$invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type));
|
||||||
|
@ -57,7 +57,7 @@ class InvitationController extends Controller
|
|||||||
/* Return early if we have the correct client_hash embedded */
|
/* Return early if we have the correct client_hash embedded */
|
||||||
|
|
||||||
if (request()->has('client_hash') && request()->input('client_hash') == $invitation->contact->client->client_hash) {
|
if (request()->has('client_hash') && request()->input('client_hash') == $invitation->contact->client->client_hash) {
|
||||||
auth()->guard('contact')->login($invitation->contact, true);
|
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
||||||
|
|
||||||
} elseif ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) {
|
} elseif ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) {
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ class InvitationController extends Controller
|
|||||||
return redirect()->route('client.login');
|
return redirect()->route('client.login');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auth()->guard('contact')->login($invitation->contact, true);
|
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,6 +149,6 @@ class PaymentMethodController extends Controller
|
|||||||
return $gateway = auth()->user()->client->getBankTransferGateway();
|
return $gateway = auth()->user()->client->getBankTransferGateway();
|
||||||
}
|
}
|
||||||
|
|
||||||
return abort(404);
|
abort(404, 'Gateway not found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ use App\Models\CompanyUser;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Transformers\CompanyUserTransformer;
|
use App\Transformers\CompanyUserTransformer;
|
||||||
use App\Transformers\UserTransformer;
|
use App\Transformers\UserTransformer;
|
||||||
|
use App\Utils\Traits\User\LoginCache;
|
||||||
use Google_Client;
|
use Google_Client;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
@ -24,6 +25,7 @@ use Illuminate\Support\Str;
|
|||||||
|
|
||||||
class ConnectedAccountController extends BaseController
|
class ConnectedAccountController extends BaseController
|
||||||
{
|
{
|
||||||
|
use LoginCache;
|
||||||
|
|
||||||
protected $entity_type = User::class;
|
protected $entity_type = User::class;
|
||||||
|
|
||||||
@ -113,8 +115,7 @@ class ConnectedAccountController extends BaseController
|
|||||||
auth()->user()->email_verified_at = now();
|
auth()->user()->email_verified_at = now();
|
||||||
auth()->user()->save();
|
auth()->user()->save();
|
||||||
|
|
||||||
$timeout = auth()->user()->company()->default_password_timeout;
|
$this->setLoginCache(auth()->user());
|
||||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
|
||||||
|
|
||||||
return $this->itemResponse(auth()->user());
|
return $this->itemResponse(auth()->user());
|
||||||
|
|
||||||
|
@ -93,11 +93,16 @@ class LoginController extends BaseController
|
|||||||
public function redirectToProvider(string $provider)
|
public function redirectToProvider(string $provider)
|
||||||
{
|
{
|
||||||
//'https://www.googleapis.com/auth/gmail.send','email','profile','openid'
|
//'https://www.googleapis.com/auth/gmail.send','email','profile','openid'
|
||||||
//
|
$scopes = [];
|
||||||
|
|
||||||
|
if($provider == 'google'){
|
||||||
|
$scopes = ['https://www.googleapis.com/auth/gmail.send','email','profile','openid'];
|
||||||
|
}
|
||||||
|
|
||||||
if (request()->has('code')) {
|
if (request()->has('code')) {
|
||||||
return $this->handleProviderCallback($provider);
|
return $this->handleProviderCallback($provider);
|
||||||
} else {
|
} else {
|
||||||
return Socialite::driver($provider)->scopes()->redirect();
|
return Socialite::driver($provider)->scopes($scopes)->redirect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,43 +236,5 @@ class LoginController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Received the returning object from the provider
|
|
||||||
* which we will use to resolve the user, we return the response in JSON format
|
|
||||||
*
|
|
||||||
* @return json
|
|
||||||
|
|
||||||
public function handleProviderCallbackApiUser(string $provider)
|
|
||||||
{
|
|
||||||
$socialite_user = Socialite::driver($provider)->stateless()->user();
|
|
||||||
|
|
||||||
if($user = OAuth::handleAuth($socialite_user, $provider))
|
|
||||||
{
|
|
||||||
return $this->itemResponse($user);
|
|
||||||
}
|
|
||||||
else if(MultiDB::checkUserEmailExists($socialite_user->getEmail()))
|
|
||||||
{
|
|
||||||
|
|
||||||
return $this->errorResponse(['message'=>'User exists in system, but not with this authentication method'], 400);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
//todo
|
|
||||||
$name = OAuth::splitName($socialite_user->getName());
|
|
||||||
|
|
||||||
$new_account = [
|
|
||||||
'first_name' => $name[0],
|
|
||||||
'last_name' => $name[1],
|
|
||||||
'password' => '',
|
|
||||||
'email' => $socialite_user->getEmail(),
|
|
||||||
];
|
|
||||||
|
|
||||||
$account = CreateAccount::dispatchNow($new_account);
|
|
||||||
|
|
||||||
return $this->itemResponse($account->default_company->owner());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
96
app/Http/Controllers/ImportJsonController.php
Normal file
96
app/Http/Controllers/ImportJsonController.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use App\Http\Requests\Import\ImportJsonRequest;
|
||||||
|
use App\Jobs\Company\CompanyExport;
|
||||||
|
use App\Jobs\Company\CompanyImport;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class ImportJsonController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/import_json",
|
||||||
|
* operationId="getImportJson",
|
||||||
|
* tags={"import"},
|
||||||
|
* summary="Import data from the system",
|
||||||
|
* description="Import data from the system",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function index(ImportJsonRequest $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$import_file = $request->file('files');
|
||||||
|
|
||||||
|
$contents = $this->unzipFile($import_file->getPathname());
|
||||||
|
|
||||||
|
$hash = Str::random(32);
|
||||||
|
|
||||||
|
Cache::put( $hash, base64_encode( $contents ), 3600 );
|
||||||
|
|
||||||
|
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->all());
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Processing'], 200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function unzipFile($file_contents)
|
||||||
|
{
|
||||||
|
$zip = new ZipArchive();
|
||||||
|
$archive = $zip->open($file_contents);
|
||||||
|
|
||||||
|
$filename = pathinfo($file_contents, PATHINFO_FILENAME);
|
||||||
|
$zip->extractTo(public_path("storage/backups/{$filename}"));
|
||||||
|
$zip->close();
|
||||||
|
$file_location = public_path("storage/backups/$filename/backup.json");
|
||||||
|
|
||||||
|
if (! file_exists($file_location))
|
||||||
|
throw new NonExistingMigrationFile('Backup file does not exist, or it is corrupted.');
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents($file_location));
|
||||||
|
|
||||||
|
unlink($file_contents);
|
||||||
|
unlink($file_location);
|
||||||
|
|
||||||
|
return $data
|
||||||
|
}
|
||||||
|
}
|
@ -845,13 +845,11 @@ class InvoiceController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
|
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
|
||||||
{
|
{
|
||||||
|
|
||||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||||
|
|
||||||
try {
|
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
} catch (\Exception $e) {
|
|
||||||
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -631,6 +631,7 @@ class TaskController extends BaseController
|
|||||||
|
|
||||||
$task_status = TaskStatus::where('id', $this->decodePrimaryKey($task_status_hashed_id))
|
$task_status = TaskStatus::where('id', $this->decodePrimaryKey($task_status_hashed_id))
|
||||||
->where('company_id', auth()->user()->company()->id)
|
->where('company_id', auth()->user()->company()->id)
|
||||||
|
->withTrashed()
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
$task_status->status_order = $key;
|
$task_status->status_order = $key;
|
||||||
@ -643,19 +644,14 @@ class TaskController extends BaseController
|
|||||||
|
|
||||||
$sort_status_id = $this->decodePrimaryKey($key);
|
$sort_status_id = $this->decodePrimaryKey($key);
|
||||||
|
|
||||||
// nlog($task_list);
|
|
||||||
|
|
||||||
foreach ($task_list as $key => $task)
|
foreach ($task_list as $key => $task)
|
||||||
{
|
{
|
||||||
|
|
||||||
// nlog($task);
|
|
||||||
|
|
||||||
$task_record = Task::where('id', $this->decodePrimaryKey($task))
|
$task_record = Task::where('id', $this->decodePrimaryKey($task))
|
||||||
->where('company_id', auth()->user()->company()->id)
|
->where('company_id', auth()->user()->company()->id)
|
||||||
|
->withTrashed()
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
// nlog($task_record->id);
|
|
||||||
|
|
||||||
$task_record->status_order = $key;
|
$task_record->status_order = $key;
|
||||||
$task_record->status_id = $sort_status_id;
|
$task_record->status_id = $sort_status_id;
|
||||||
$task_record->save();
|
$task_record->save();
|
||||||
|
@ -211,11 +211,12 @@ class UserController extends BaseController
|
|||||||
|
|
||||||
$ct = CreateCompanyToken::dispatchNow($company, $user, $user_agent);
|
$ct = CreateCompanyToken::dispatchNow($company, $user, $user_agent);
|
||||||
|
|
||||||
nlog("in the store method of the usercontroller class");
|
|
||||||
|
|
||||||
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
|
|
||||||
return $this->itemResponse($user->fresh());
|
$user->setCompany($company);
|
||||||
|
$user->company_id = $company->id;
|
||||||
|
|
||||||
|
return $this->itemResponse($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,6 +16,7 @@ use App\Http\Middleware\Authenticate;
|
|||||||
use App\Http\Middleware\CheckClientExistence;
|
use App\Http\Middleware\CheckClientExistence;
|
||||||
use App\Http\Middleware\CheckForMaintenanceMode;
|
use App\Http\Middleware\CheckForMaintenanceMode;
|
||||||
use App\Http\Middleware\ClientPortalEnabled;
|
use App\Http\Middleware\ClientPortalEnabled;
|
||||||
|
use App\Http\Middleware\ContactAccount;
|
||||||
use App\Http\Middleware\ContactKeyLogin;
|
use App\Http\Middleware\ContactKeyLogin;
|
||||||
use App\Http\Middleware\ContactRegister;
|
use App\Http\Middleware\ContactRegister;
|
||||||
use App\Http\Middleware\ContactSetDb;
|
use App\Http\Middleware\ContactSetDb;
|
||||||
@ -141,6 +142,7 @@ class Kernel extends HttpKernel
|
|||||||
'api_secret_check' => ApiSecretCheck::class,
|
'api_secret_check' => ApiSecretCheck::class,
|
||||||
'contact_token_auth' => ContactTokenAuth::class,
|
'contact_token_auth' => ContactTokenAuth::class,
|
||||||
'contact_db' => ContactSetDb::class,
|
'contact_db' => ContactSetDb::class,
|
||||||
|
'contact_account' => ContactAccount::class,
|
||||||
'domain_db' => SetDomainNameDb::class,
|
'domain_db' => SetDomainNameDb::class,
|
||||||
'email_db' => SetEmailDb::class,
|
'email_db' => SetEmailDb::class,
|
||||||
'invite_db' => SetInviteDb::class,
|
'invite_db' => SetInviteDb::class,
|
||||||
@ -182,5 +184,6 @@ class Kernel extends HttpKernel
|
|||||||
PasswordProtection::class,
|
PasswordProtection::class,
|
||||||
Locale::class,
|
Locale::class,
|
||||||
SubstituteBindings::class,
|
SubstituteBindings::class,
|
||||||
|
ContactAccount::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -181,14 +181,16 @@ class BillingPortalPurchase extends Component
|
|||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
|
||||||
$contact = ClientContact::where('email', $this->email)->first();
|
$contact = ClientContact::where('email', $this->email)
|
||||||
|
->where('company_id', $this->subscription->company_id)
|
||||||
|
->first();
|
||||||
|
|
||||||
if ($contact && $this->steps['existing_user'] === false) {
|
if ($contact && $this->steps['existing_user'] === false) {
|
||||||
return $this->steps['existing_user'] = true;
|
return $this->steps['existing_user'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($contact && $this->steps['existing_user']) {
|
if ($contact && $this->steps['existing_user']) {
|
||||||
$attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password]);
|
$attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password, 'company_id' => $this->subscription->company_id]);
|
||||||
|
|
||||||
return $attempt
|
return $attempt
|
||||||
? $this->getPaymentMethods($contact)
|
? $this->getPaymentMethods($contact)
|
||||||
|
41
app/Http/Middleware/ContactAccount.php
Normal file
41
app/Http/Middleware/ContactAccount.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?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\Middleware;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Account;
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ContactAccount
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!Ninja::isHosted()) {
|
||||||
|
|
||||||
|
$account_id = Account::first()->id;
|
||||||
|
$request->attributes->add(['account_id' => $account_id]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ use Auth;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ContactKeyLogin
|
class ContactKeyLogin
|
||||||
{
|
{
|
||||||
@ -42,6 +43,9 @@ class ContactKeyLogin
|
|||||||
if (MultiDB::findAndSetDbByContactKey($request->segment(3))) {
|
if (MultiDB::findAndSetDbByContactKey($request->segment(3))) {
|
||||||
|
|
||||||
if($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()){
|
if($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()){
|
||||||
|
if(empty($client_contact->email))
|
||||||
|
$client_contact->email = Str::random(6) . "@example.com"; $client_contact->save();
|
||||||
|
|
||||||
Auth::guard('contact')->login($client_contact, true);
|
Auth::guard('contact')->login($client_contact, true);
|
||||||
return redirect()->to('client/dashboard');
|
return redirect()->to('client/dashboard');
|
||||||
}
|
}
|
||||||
@ -49,6 +53,10 @@ class ContactKeyLogin
|
|||||||
}
|
}
|
||||||
} elseif ($request->segment(2) && $request->segment(2) == 'key_login' && $request->segment(3)) {
|
} elseif ($request->segment(2) && $request->segment(2) == 'key_login' && $request->segment(3)) {
|
||||||
if ($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()) {
|
if ($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()) {
|
||||||
|
|
||||||
|
if(empty($client_contact->email))
|
||||||
|
$client_contact->email = Str::random(6) . "@example.com"; $client_contact->save();
|
||||||
|
|
||||||
auth()->guard('contact')->login($client_contact, true);
|
auth()->guard('contact')->login($client_contact, true);
|
||||||
return redirect()->to('client/dashboard');
|
return redirect()->to('client/dashboard');
|
||||||
}
|
}
|
||||||
@ -56,19 +64,36 @@ class ContactKeyLogin
|
|||||||
if (MultiDB::findAndSetDbByClientHash($request->input('client_hash'))) {
|
if (MultiDB::findAndSetDbByClientHash($request->input('client_hash'))) {
|
||||||
|
|
||||||
if($client = Client::where('client_hash', $request->input('client_hash'))->first()){
|
if($client = Client::where('client_hash', $request->input('client_hash'))->first()){
|
||||||
auth()->guard('contact')->login($client->primary_contact()->first(), true);
|
|
||||||
|
$primary_contact = $client->primary_contact()->first();
|
||||||
|
|
||||||
|
if(empty($primary_contact->email))
|
||||||
|
$primary_contact->email = Str::random(6) . "@example.com"; $primary_contact->save();
|
||||||
|
|
||||||
|
auth()->guard('contact')->login($primary_contact, true);
|
||||||
return redirect()->to('client/dashboard');
|
return redirect()->to('client/dashboard');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($request->has('client_hash')) {
|
} elseif ($request->has('client_hash')) {
|
||||||
if ($client = Client::where('client_hash', $request->input('client_hash'))->first()) {
|
if ($client = Client::where('client_hash', $request->input('client_hash'))->first()) {
|
||||||
Auth::guard('contact')->login($client->primary_contact()->first(), true);
|
|
||||||
|
$primary_contact = $client->primary_contact()->first();
|
||||||
|
|
||||||
|
if(empty($primary_contact->email))
|
||||||
|
$primary_contact->email = Str::random(6) . "@example.com"; $primary_contact->save();
|
||||||
|
|
||||||
|
auth()->guard('contact')->login($primary_contact, true);
|
||||||
|
|
||||||
return redirect()->to('client/dashboard');
|
return redirect()->to('client/dashboard');
|
||||||
}
|
}
|
||||||
} elseif ($request->segment(2) && $request->segment(2) == 'magic_link' && $request->segment(3)) {
|
} elseif ($request->segment(2) && $request->segment(2) == 'magic_link' && $request->segment(3)) {
|
||||||
$contact_email = Cache::get($request->segment(3));
|
$contact_email = Cache::get($request->segment(3));
|
||||||
if($client_contact = ClientContact::where('email', $contact_email)->first()){
|
if($client_contact = ClientContact::where('email', $contact_email)->first()){
|
||||||
Auth::guard('contact')->login($client_contact, true);
|
|
||||||
|
if(empty($client_contact->email))
|
||||||
|
$client_contact->email = Str::random(6) . "@example.com"; $client_contact->save();
|
||||||
|
|
||||||
|
auth()->guard('contact')->login($client_contact, true);
|
||||||
|
|
||||||
if ($request->query('redirect') && !empty($request->query('redirect'))) {
|
if ($request->query('redirect') && !empty($request->query('redirect'))) {
|
||||||
return redirect()->to($request->query('redirect'));
|
return redirect()->to($request->query('redirect'));
|
||||||
|
@ -52,6 +52,6 @@ class ContactRegister
|
|||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return abort(404);
|
abort(404, 'ContactRegister Middlware');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,11 @@ class SetDomainNameDb
|
|||||||
'portal_mode' => 'subdomain',
|
'portal_mode' => 'subdomain',
|
||||||
];
|
];
|
||||||
|
|
||||||
if(!MultiDB::findAndSetDbByDomain($query)){
|
if($company = MultiDB::findAndSetDbByDomain($query)){
|
||||||
|
$request->attributes->add(['account_id' => $company->account_id]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if ($request->json) {
|
if ($request->json) {
|
||||||
return response()->json($error, 403);
|
return response()->json($error, 403);
|
||||||
} else {
|
} else {
|
||||||
@ -66,7 +70,11 @@ class SetDomainNameDb
|
|||||||
'portal_mode' => 'domain',
|
'portal_mode' => 'domain',
|
||||||
];
|
];
|
||||||
|
|
||||||
if(!MultiDB::findAndSetDbByDomain($query)){
|
if($company = MultiDB::findAndSetDbByDomain($query)){
|
||||||
|
$request->attributes->add(['account_id' => $company->account_id]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if ($request->json) {
|
if ($request->json) {
|
||||||
return response()->json($error, 403);
|
return response()->json($error, 403);
|
||||||
} else {
|
} else {
|
||||||
|
@ -49,7 +49,6 @@ class TokenAuth
|
|||||||
| us to decouple a $user and their attached companies completely.
|
| us to decouple a $user and their attached companies completely.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
$user->setCompany($company_token->company);
|
|
||||||
|
|
||||||
app('queue')->createPayloadUsing(function () use ($company_token) {
|
app('queue')->createPayloadUsing(function () use ($company_token) {
|
||||||
return ['db' => $company_token->company->db];
|
return ['db' => $company_token->company->db];
|
||||||
@ -67,6 +66,7 @@ class TokenAuth
|
|||||||
|
|
||||||
//stateless, don't remember the user.
|
//stateless, don't remember the user.
|
||||||
auth()->login($user, false);
|
auth()->login($user, false);
|
||||||
|
auth()->user()->setCompany($company_token->company);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$error = [
|
$error = [
|
||||||
|
@ -40,13 +40,13 @@ class CreateAccountRequest extends Request
|
|||||||
'password' => 'required|string|min:6',
|
'password' => 'required|string|min:6',
|
||||||
'email' => 'bail|required|email:rfc,dns',
|
'email' => 'bail|required|email:rfc,dns',
|
||||||
'email' => new NewUniqueUserRule(),
|
'email' => new NewUniqueUserRule(),
|
||||||
'privacy_policy' => 'required',
|
'privacy_policy' => 'required|boolean',
|
||||||
'terms_of_service' => 'required',
|
'terms_of_service' => 'required|boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function prepareForValidation()
|
protected function prepareForValidation()
|
||||||
{
|
{nlog($this->all());
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
$input['user_agent'] = request()->server('HTTP_USER_AGENT');
|
$input['user_agent'] = request()->server('HTTP_USER_AGENT');
|
||||||
|
55
app/Http/Requests/Client/AdjustClientLedgerRequest.php
Normal file
55
app/Http/Requests/Client/AdjustClientLedgerRequest.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?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\Requests\Client;
|
||||||
|
|
||||||
|
use App\Http\Requests\Request;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class AdjustClientLedgerRequest extends Request
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize() : bool
|
||||||
|
{
|
||||||
|
return auth()->user()->can('edit', $this->client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
/* Ensure we have a client name, and that all emails are unique*/
|
||||||
|
|
||||||
|
$rules = [];
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepareForValidation()
|
||||||
|
{
|
||||||
|
$input = $this->all();
|
||||||
|
|
||||||
|
$this->replace($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -53,6 +53,6 @@ class RegisterRequest extends FormRequest
|
|||||||
return $company;
|
return $company;
|
||||||
}
|
}
|
||||||
|
|
||||||
abort(404);
|
abort(404, 'Register request not found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
app/Http/Requests/Import/ImportJsonRequest.php
Normal file
39
app/Http/Requests/Import/ImportJsonRequest.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?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\Requests\Import;
|
||||||
|
|
||||||
|
use App\Http\Requests\Request;
|
||||||
|
|
||||||
|
class ImportJsonRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize() : bool
|
||||||
|
{
|
||||||
|
return auth()->user()->isAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
// 'import_type' => 'required',
|
||||||
|
// 'files' => 'required_without:hash|array|min:1|max:6',
|
||||||
|
// 'hash' => 'nullable|string',
|
||||||
|
// 'column_map' => 'required_with:hash|array',
|
||||||
|
// 'skip_header' => 'required_with:hash|boolean',
|
||||||
|
// 'files.*' => 'file|mimes:csv,txt',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -45,9 +45,6 @@ class UpdateUserRequest extends Request
|
|||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
// if (isset($input['company_user']) && ! auth()->user()->isAdmin()) {
|
|
||||||
// unset($input['company_user']);
|
|
||||||
// }
|
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -57,13 +57,13 @@ class InvoiceBalanceSanity implements Rule
|
|||||||
private function checkIfInvoiceBalanceIsSane() : bool
|
private function checkIfInvoiceBalanceIsSane() : bool
|
||||||
{
|
{
|
||||||
|
|
||||||
|
DB::connection(config('database.default'))->beginTransaction();
|
||||||
|
|
||||||
|
$this->invoice = Invoice::on(config('database.default'))->find($this->invoice->id);
|
||||||
$this->invoice->line_items = $this->input['line_items'];
|
$this->invoice->line_items = $this->input['line_items'];
|
||||||
|
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
$temp_invoice = $this->invoice->calc()->getTempEntity();
|
$temp_invoice = $this->invoice->calc()->getTempEntity();
|
||||||
|
|
||||||
DB::rollBack();
|
DB::connection(config('database.default'))->rollBack();
|
||||||
|
|
||||||
if($temp_invoice->balance < 0){
|
if($temp_invoice->balance < 0){
|
||||||
$this->message = 'Invoice balance cannot go negative';
|
$this->message = 'Invoice balance cannot go negative';
|
||||||
|
@ -20,6 +20,7 @@ use Illuminate\Contracts\Validation\Rule;
|
|||||||
*/
|
*/
|
||||||
class AttachableUser implements Rule
|
class AttachableUser implements Rule
|
||||||
{
|
{
|
||||||
|
public $message;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -39,7 +40,7 @@ class AttachableUser implements Rule
|
|||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
return "Cannot add the same user to the same company";
|
return $this->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,9 +64,16 @@ class AttachableUser implements Rule
|
|||||||
->where('company_id', auth()->user()->company()->id)
|
->where('company_id', auth()->user()->company()->id)
|
||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if($user_already_attached)
|
//If the user is already attached or isn't link to this account - return false
|
||||||
|
if($user_already_attached) {
|
||||||
|
$this->message = ctrans('texts.user_duplicate_error');
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($user->account_id != auth()->user()->account_id){
|
||||||
|
$this->message = ctrans('texts.user_cross_linked_error');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,13 @@ use App\Jobs\Util\VersionCheck;
|
|||||||
use App\Mail\Admin\AccountCreatedObject;
|
use App\Mail\Admin\AccountCreatedObject;
|
||||||
use App\Mail\Admin\VerifyUserObject;
|
use App\Mail\Admin\VerifyUserObject;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
|
use App\Models\Timezone;
|
||||||
use App\Notifications\Ninja\NewAccountCreated;
|
use App\Notifications\Ninja\NewAccountCreated;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
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 Illuminate\Support\Str;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Turbo124\Beacon\Facades\LightLogs;
|
use Turbo124\Beacon\Facades\LightLogs;
|
||||||
@ -40,9 +42,12 @@ class CreateAccount
|
|||||||
|
|
||||||
protected $request;
|
protected $request;
|
||||||
|
|
||||||
public function __construct(array $sp660339)
|
protected $client_ip;
|
||||||
|
|
||||||
|
public function __construct(array $sp660339, $client_ip)
|
||||||
{
|
{
|
||||||
$this->request = $sp660339;
|
$this->request = $sp660339;
|
||||||
|
$this->client_ip = $client_ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@ -74,6 +79,7 @@ class CreateAccount
|
|||||||
|
|
||||||
$sp035a66 = CreateCompany::dispatchNow($this->request, $sp794f3f);
|
$sp035a66 = CreateCompany::dispatchNow($this->request, $sp794f3f);
|
||||||
$sp035a66->load('account');
|
$sp035a66->load('account');
|
||||||
|
$sp035a66->settings = $this->processSettings($sp035a66->settings);
|
||||||
$sp794f3f->default_company_id = $sp035a66->id;
|
$sp794f3f->default_company_id = $sp035a66->id;
|
||||||
$sp794f3f->save();
|
$sp794f3f->save();
|
||||||
|
|
||||||
@ -107,4 +113,53 @@ class CreateAccount
|
|||||||
|
|
||||||
return $sp794f3f;
|
return $sp794f3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function processSettings($settings)
|
||||||
|
{
|
||||||
|
if(Ninja::isHosted() && Cache::get('currencies') && $data = unserialize(@file_get_contents('http://www.geoplugin.net/php.gp?ip=' . $this->client_ip)))
|
||||||
|
{
|
||||||
|
|
||||||
|
$currency_code = strtolower($data['geoplugin_currencyCode']);
|
||||||
|
$country_code = strtolower($data['geoplugin_countryCode']);
|
||||||
|
|
||||||
|
$currency = Cache::get('currencies')->filter(function ($item) use ($currency_code) {
|
||||||
|
return strtolower($item->code) == $currency_code;
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($currency) {
|
||||||
|
$settings->currency_id = (string)$currency->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$country = Cache::get('countries')->filter(function ($item) use ($country_code) {
|
||||||
|
return strtolower($item->iso_3166_2) == $country_code || strtolower($item->iso_3166_3) == $country_code;
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($country) {
|
||||||
|
$settings->country_id = (string)$country->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$language = Cache::get('languages')->filter(function ($item) use ($currency_code) {
|
||||||
|
return strtolower($item->locale) == $currency_code;
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($language) {
|
||||||
|
$settings->language_id = (string)$language->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timezone = Timezone::where('name', $data['geoplugin_timezone'])->first();
|
||||||
|
|
||||||
|
if($timezone) {
|
||||||
|
$settings->timezone_id = (string)$timezone->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $activity;
|
return $activity;
|
||||||
|
|
||||||
})->makeHidden(['id'])->toArray();
|
})->makeHidden(['id'])->all();
|
||||||
|
|
||||||
$this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){
|
$this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){
|
||||||
|
|
||||||
@ -114,43 +114,56 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $backup;
|
return $backup;
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['users'] = $this->company->users()->withTrashed()->cursor()->map(function ($user){
|
$this->export_data['users'] = $this->company->users()->withTrashed()->cursor()->map(function ($user){
|
||||||
|
|
||||||
$user->account_id = $this->encodePrimaryKey($user->account_id);
|
$user->account_id = $this->encodePrimaryKey($user->account_id);
|
||||||
$user->id = $this->encodePrimaryKey($user->id);
|
// $user->id = $this->encodePrimaryKey($user->id);
|
||||||
|
|
||||||
return $user;
|
return $user->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact){
|
$this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact){
|
||||||
|
|
||||||
$client_contact = $this->transformArrayOfKeys($client_contact, ['id', 'company_id', 'user_id',' client_id']);
|
$client_contact = $this->transformArrayOfKeys($client_contact, ['company_id', 'user_id', 'client_id']);
|
||||||
|
|
||||||
return $client_contact;
|
return $client_contact->makeVisible([
|
||||||
|
'password',
|
||||||
|
'remember_token',
|
||||||
|
'user_id',
|
||||||
|
'company_id',
|
||||||
|
'client_id',
|
||||||
|
'google_2fa_secret',
|
||||||
|
'id',
|
||||||
|
'oauth_provider_id',
|
||||||
|
'oauth_user_id',
|
||||||
|
'token',
|
||||||
|
'hashed_id',
|
||||||
|
]);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token){
|
$this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token){
|
||||||
|
|
||||||
$client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['id', 'company_id', 'client_id']);
|
$client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['company_id', 'client_id']);
|
||||||
|
|
||||||
return $client_gateway_token;
|
return $client_gateway_token->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['clients'] = $this->company->clients->map(function ($client){
|
$this->export_data['clients'] = $this->company->clients->map(function ($client){
|
||||||
|
|
||||||
$client = $this->transformArrayOfKeys($client, ['id', 'company_id', 'user_id',' assigned_user_id', 'group_settings_id']);
|
$client = $this->transformArrayOfKeys($client, ['company_id', 'user_id', 'assigned_user_id', 'group_settings_id']);
|
||||||
|
|
||||||
return $client;
|
return $client->makeVisible(['id','private_notes','user_id','company_id','last_login','hashed_id']);
|
||||||
|
|
||||||
|
})->all();
|
||||||
|
|
||||||
})->toArray();
|
|
||||||
|
|
||||||
$this->export_data['company'] = $this->company->toArray();
|
$this->export_data['company'] = $this->company->toArray();
|
||||||
|
|
||||||
@ -159,9 +172,9 @@ class CompanyExport implements ShouldQueue
|
|||||||
$company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']);
|
$company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']);
|
||||||
$company_gateway->config = decrypt($company_gateway->config);
|
$company_gateway->config = decrypt($company_gateway->config);
|
||||||
|
|
||||||
return $company_gateway;
|
return $company_gateway->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['company_tokens'] = $this->company->tokens->map(function ($token){
|
$this->export_data['company_tokens'] = $this->company->tokens->map(function ($token){
|
||||||
|
|
||||||
@ -169,7 +182,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $token;
|
return $token;
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['company_ledger'] = $this->company->ledger->map(function ($ledger){
|
$this->export_data['company_ledger'] = $this->company->ledger->map(function ($ledger){
|
||||||
|
|
||||||
@ -177,7 +190,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $ledger;
|
return $ledger;
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['company_users'] = $this->company->company_users->map(function ($company_user){
|
$this->export_data['company_users'] = $this->company->company_users->map(function ($company_user){
|
||||||
|
|
||||||
@ -185,43 +198,44 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $company_user;
|
return $company_user;
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['credits'] = $this->company->credits->map(function ($credit){
|
$this->export_data['credits'] = $this->company->credits->map(function ($credit){
|
||||||
|
|
||||||
$credit = $this->transformBasicEntities($credit);
|
$credit = $this->transformBasicEntities($credit);
|
||||||
$credit = $this->transformArrayOfKeys($credit, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','invoice_id']);
|
$credit = $this->transformArrayOfKeys($credit, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','invoice_id']);
|
||||||
|
|
||||||
return $credit;
|
return $credit->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit){
|
$this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit){
|
||||||
|
|
||||||
$credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
$credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'credit_id']);
|
||||||
|
|
||||||
return $credit;
|
return $credit->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->toArray();
|
$this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->all();
|
||||||
|
|
||||||
$this->export_data['documents'] = $this->company->documents->map(function ($document){
|
$this->export_data['documents'] = $this->company->all_documents->map(function ($document){
|
||||||
|
|
||||||
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id']);
|
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id','documentable_id']);
|
||||||
|
$document->hashed_id = $this->encodePrimaryKey($document->id);
|
||||||
|
|
||||||
return $document;
|
return $document->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['expense_categories'] = $this->company->expenses->map(function ($expense_category){
|
$this->export_data['expense_categories'] = $this->company->expense_categories->map(function ($expense_category){
|
||||||
|
|
||||||
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
|
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
|
||||||
|
|
||||||
return $expense_category;
|
return $expense_category->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['expenses'] = $this->company->expenses->map(function ($expense){
|
$this->export_data['expenses'] = $this->company->expenses->map(function ($expense){
|
||||||
@ -229,17 +243,17 @@ class CompanyExport implements ShouldQueue
|
|||||||
$expense = $this->transformBasicEntities($expense);
|
$expense = $this->transformBasicEntities($expense);
|
||||||
$expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id','project_id']);
|
$expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id','project_id']);
|
||||||
|
|
||||||
return $expense;
|
return $expense->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs){
|
$this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs){
|
||||||
|
|
||||||
$gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
|
$gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
|
||||||
|
|
||||||
return $gs;
|
return $gs->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['invoices'] = $this->company->invoices->map(function ($invoice){
|
$this->export_data['invoices'] = $this->company->invoices->map(function ($invoice){
|
||||||
@ -247,18 +261,22 @@ class CompanyExport implements ShouldQueue
|
|||||||
$invoice = $this->transformBasicEntities($invoice);
|
$invoice = $this->transformBasicEntities($invoice);
|
||||||
$invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
$invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||||
|
|
||||||
return $invoice;
|
return $invoice->makeVisible(['id',
|
||||||
|
'private_notes',
|
||||||
|
'user_id',
|
||||||
|
'client_id',
|
||||||
|
'company_id',]);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice){
|
$this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice){
|
||||||
|
|
||||||
$invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
$invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'invoice_id']);
|
||||||
|
|
||||||
return $invoice;
|
return $invoice->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['payment_terms'] = $this->company->user_payment_terms->map(function ($term){
|
$this->export_data['payment_terms'] = $this->company->user_payment_terms->map(function ($term){
|
||||||
|
|
||||||
@ -266,61 +284,65 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $term;
|
return $term;
|
||||||
|
|
||||||
})->makeHidden(['id'])->toArray();
|
})->makeHidden(['id'])->all();
|
||||||
|
|
||||||
$this->export_data['paymentables'] = $this->company->payments()->with('paymentables')->cursor()->map(function ($paymentable){
|
|
||||||
|
|
||||||
$paymentable = $this->transformArrayOfKeys($paymentable, ['payment_id','paymentable_id']);
|
|
||||||
|
|
||||||
return $paymentable;
|
|
||||||
|
|
||||||
})->toArray();
|
|
||||||
|
|
||||||
$this->export_data['payments'] = $this->company->payments->map(function ($payment){
|
$this->export_data['payments'] = $this->company->payments->map(function ($payment){
|
||||||
|
|
||||||
$payment = $this->transformBasicEntities($payment);
|
$payment = $this->transformBasicEntities($payment);
|
||||||
$payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
|
$payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
|
||||||
|
|
||||||
return $payment;
|
$payment->paymentables = $this->transformPaymentable($payment);
|
||||||
|
|
||||||
})->toArray();
|
return $payment->makeVisible(['id']);
|
||||||
|
|
||||||
|
})->all();
|
||||||
|
|
||||||
|
$this->export_data['products'] = $this->company->products->map(function ($product){
|
||||||
|
|
||||||
|
$product = $this->transformBasicEntities($product);
|
||||||
|
$product = $this->transformArrayOfKeys($product, ['vendor_id','project_id']);
|
||||||
|
|
||||||
|
return $product->makeVisible(['id']);
|
||||||
|
|
||||||
|
})->all();
|
||||||
|
|
||||||
$this->export_data['projects'] = $this->company->projects->map(function ($project){
|
$this->export_data['projects'] = $this->company->projects->map(function ($project){
|
||||||
|
|
||||||
$project = $this->transformBasicEntities($project);
|
$project = $this->transformBasicEntities($project);
|
||||||
$project = $this->transformArrayOfKeys($project, ['client_id']);
|
$project = $this->transformArrayOfKeys($project, ['client_id']);
|
||||||
|
|
||||||
return $project;
|
return $project->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['quotes'] = $this->company->quotes->map(function ($quote){
|
$this->export_data['quotes'] = $this->company->quotes->map(function ($quote){
|
||||||
|
|
||||||
$quote = $this->transformBasicEntities($quote);
|
$quote = $this->transformBasicEntities($quote);
|
||||||
$quote = $this->transformArrayOfKeys($quote, ['invoice_id','recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
$quote = $this->transformArrayOfKeys($quote, ['invoice_id','recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||||
|
|
||||||
return $quote;
|
return $quote->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote){
|
$this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote){
|
||||||
|
|
||||||
$quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
$quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'quote_id']);
|
||||||
|
|
||||||
return $quote;
|
return $quote->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices->map(function ($ri){
|
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices->makeVisible(['id'])->map(function ($ri){
|
||||||
|
|
||||||
$ri = $this->transformBasicEntities($ri);
|
$ri = $this->transformBasicEntities($ri);
|
||||||
$ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
$ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||||
return $ri;
|
|
||||||
|
|
||||||
})->toArray();
|
return $ri->makeVisible(['id']);
|
||||||
|
|
||||||
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['recurring_invoice_invitations'] = RecurringInvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($ri){
|
$this->export_data['recurring_invoice_invitations'] = RecurringInvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($ri){
|
||||||
@ -329,16 +351,22 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $ri;
|
return $ri;
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription){
|
$this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription){
|
||||||
|
|
||||||
$subscription = $this->transformBasicEntities($subscription);
|
$subscription = $this->transformBasicEntities($subscription);
|
||||||
$subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
|
$subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
|
||||||
|
|
||||||
return $subscription;
|
return $subscription->makeVisible([ 'id',
|
||||||
|
'user_id',
|
||||||
|
'assigned_user_id',
|
||||||
|
'company_id',
|
||||||
|
'product_ids',
|
||||||
|
'recurring_product_ids',
|
||||||
|
'group_id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['system_logs'] = $this->company->system_logs->map(function ($log){
|
$this->export_data['system_logs'] = $this->company->system_logs->map(function ($log){
|
||||||
@ -348,16 +376,16 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $log;
|
return $log;
|
||||||
|
|
||||||
})->makeHidden(['id'])->toArray();
|
})->makeHidden(['id'])->all();
|
||||||
|
|
||||||
$this->export_data['tasks'] = $this->company->tasks->map(function ($task){
|
$this->export_data['tasks'] = $this->company->tasks->map(function ($task){
|
||||||
|
|
||||||
$task = $this->transformBasicEntities($task);
|
$task = $this->transformBasicEntities($task);
|
||||||
$task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']);
|
$task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']);
|
||||||
|
|
||||||
return $task;
|
return $task->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['task_statuses'] = $this->company->task_statuses->map(function ($status){
|
$this->export_data['task_statuses'] = $this->company->task_statuses->map(function ($status){
|
||||||
|
|
||||||
@ -367,7 +395,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $status;
|
return $status;
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['tax_rates'] = $this->company->tax_rates->map(function ($rate){
|
$this->export_data['tax_rates'] = $this->company->tax_rates->map(function ($rate){
|
||||||
|
|
||||||
@ -376,13 +404,13 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $rate;
|
return $rate;
|
||||||
|
|
||||||
})->makeHidden(['id'])->toArray();
|
})->makeHidden(['id'])->all();
|
||||||
|
|
||||||
$this->export_data['vendors'] = $this->company->vendors->map(function ($vendor){
|
$this->export_data['vendors'] = $this->company->vendors->map(function ($vendor){
|
||||||
|
|
||||||
return $this->transformBasicEntities($vendor);
|
return $this->transformBasicEntities($vendor)->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
|
|
||||||
$this->export_data['vendor_contacts'] = VendorContact::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($vendor){
|
$this->export_data['vendor_contacts'] = VendorContact::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($vendor){
|
||||||
@ -390,9 +418,9 @@ class CompanyExport implements ShouldQueue
|
|||||||
$vendor = $this->transformBasicEntities($vendor);
|
$vendor = $this->transformBasicEntities($vendor);
|
||||||
$vendor->vendor_id = $this->encodePrimaryKey($vendor->vendor_id);
|
$vendor->vendor_id = $this->encodePrimaryKey($vendor->vendor_id);
|
||||||
|
|
||||||
return $vendor;
|
return $vendor->makeVisible(['id']);
|
||||||
|
|
||||||
})->toArray();
|
})->all();
|
||||||
|
|
||||||
$this->export_data['webhooks'] = $this->company->webhooks->map(function ($hook){
|
$this->export_data['webhooks'] = $this->company->webhooks->map(function ($hook){
|
||||||
|
|
||||||
@ -401,7 +429,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
return $hook;
|
return $hook;
|
||||||
|
|
||||||
})->makeHidden(['id'])->toArray();
|
})->makeHidden(['id'])->all();
|
||||||
|
|
||||||
//write to tmp and email to owner();
|
//write to tmp and email to owner();
|
||||||
|
|
||||||
@ -413,7 +441,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
private function transformBasicEntities($model)
|
private function transformBasicEntities($model)
|
||||||
{
|
{
|
||||||
|
|
||||||
return $this->transformArrayOfKeys($model, ['id', 'user_id', 'assigned_user_id', 'company_id']);
|
return $this->transformArrayOfKeys($model, ['user_id', 'assigned_user_id', 'company_id']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,40 +456,48 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function transformPaymentable($payment)
|
||||||
|
{
|
||||||
|
|
||||||
|
$new_arr = [];
|
||||||
|
|
||||||
|
foreach($payment->paymentables as $paymentable)
|
||||||
|
{
|
||||||
|
|
||||||
|
$paymentable->payment_id = $this->encodePrimaryKey($paymentable->payment_id);
|
||||||
|
$paymentable->paymentable_id = $this->encodePrimaryKey($paymentable->paymentable_id);
|
||||||
|
|
||||||
|
$new_arr[] = $paymentable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new_arr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private function zipAndSend()
|
private function zipAndSend()
|
||||||
{
|
{
|
||||||
|
|
||||||
$tempStream = fopen('php://memory', 'w+');
|
|
||||||
|
|
||||||
$options = new Archive();
|
|
||||||
$options->setOutputStream($tempStream);
|
|
||||||
|
|
||||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||||
|
|
||||||
$zip = new ZipStream($file_name, $options);
|
$zip_path = public_path('storage/backups/'.$file_name);
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
|
||||||
$fp = tmpfile();
|
if ($zip->open($zip_path, \ZipArchive::CREATE)!==TRUE) {
|
||||||
fwrite($fp, json_encode($this->export_data));
|
nlog("cannot open {$zip_path}");
|
||||||
rewind($fp);
|
}
|
||||||
$zip->addFileFromStream('backup.json', $fp);
|
|
||||||
|
|
||||||
$zip->finish();
|
$zip->addFromString("backup.json", json_encode($this->export_data));
|
||||||
|
$zip->close();
|
||||||
$path = 'backups/';
|
|
||||||
|
|
||||||
Storage::disk(config('filesystems.default'))->put($path.$file_name, $tempStream);
|
|
||||||
|
|
||||||
fclose($tempStream);
|
|
||||||
|
|
||||||
$nmo = new NinjaMailerObject;
|
$nmo = new NinjaMailerObject;
|
||||||
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url($path.$file_name), $this->company);
|
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url('backups/'.$file_name), $this->company);
|
||||||
$nmo->to_user = $this->user;
|
$nmo->to_user = $this->user;
|
||||||
$nmo->company = $this->company;
|
$nmo->company = $this->company;
|
||||||
$nmo->settings = $this->company->settings;
|
$nmo->settings = $this->company->settings;
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
UnlinkFile::dispatch(config('filesystems.default'), 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,9 @@
|
|||||||
namespace App\Jobs\Company;
|
namespace App\Jobs\Company;
|
||||||
|
|
||||||
use App\DataMapper\CompanySettings;
|
use App\DataMapper\CompanySettings;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -60,6 +62,12 @@ class CreateCompany
|
|||||||
$company->subdomain = isset($this->request['subdomain']) ? $this->request['subdomain'] : '';
|
$company->subdomain = isset($this->request['subdomain']) ? $this->request['subdomain'] : '';
|
||||||
$company->custom_fields = new \stdClass;
|
$company->custom_fields = new \stdClass;
|
||||||
$company->default_password_timeout = 1800000;
|
$company->default_password_timeout = 1800000;
|
||||||
|
|
||||||
|
if(Ninja::isHosted())
|
||||||
|
$company->subdomain = MultiDB::randomSubdomainGenerator();
|
||||||
|
else
|
||||||
|
$company->subdomain = '';
|
||||||
|
|
||||||
$company->save();
|
$company->save();
|
||||||
|
|
||||||
return $company;
|
return $company;
|
||||||
|
101
app/Jobs/Cron/AutoBillCron.php
Normal file
101
app/Jobs/Cron/AutoBillCron.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?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\Jobs\Cron;
|
||||||
|
|
||||||
|
use App\Jobs\RecurringInvoice\SendRecurring;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
class AutoBillCron
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
|
||||||
|
public $tries = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle() : void
|
||||||
|
{
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
/* Get all invoices where the send date is less than NOW + 30 minutes() */
|
||||||
|
nlog("Performing Autobilling ".Carbon::now()->format('Y-m-d h:i:s'));
|
||||||
|
|
||||||
|
if (! config('ninja.db.multi_db_enabled')) {
|
||||||
|
|
||||||
|
$auto_bill_partial_invoices = Invoice::whereDate('partial_due_date', '<=', now())
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('auto_bill_enabled', true)
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->with('company')
|
||||||
|
->cursor()->each(function ($invoice){
|
||||||
|
$this->runAutoBiller($invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('auto_bill_enabled', true)
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->with('company')
|
||||||
|
->cursor()->each(function ($invoice){
|
||||||
|
$this->runAutoBiller($invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//multiDB environment, need to
|
||||||
|
foreach (MultiDB::$dbs as $db) {
|
||||||
|
MultiDB::setDB($db);
|
||||||
|
|
||||||
|
$auto_bill_partial_invoices = Invoice::whereDate('partial_due_date', '<=', now())
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('auto_bill_enabled', true)
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->with('company')
|
||||||
|
->cursor()->each(function ($invoice){
|
||||||
|
$this->runAutoBiller($invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('auto_bill_enabled', true)
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->with('company')
|
||||||
|
->cursor()->each(function ($invoice){
|
||||||
|
$this->runAutoBiller($invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runAutoBiller(Invoice $invoice)
|
||||||
|
{
|
||||||
|
nlog("Firing autobill for {$invoice->company_id} - {$invoice->number}");
|
||||||
|
$invoice->service()->autoBill()->save();
|
||||||
|
}
|
||||||
|
}
|
@ -293,6 +293,9 @@ class CSVImport implements ShouldQueue {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/* Make sure we don't apply any payments to invoices with a Zero Amount*/
|
||||||
|
if($invoice->amount > 0)
|
||||||
|
{
|
||||||
$payment_repository->save(
|
$payment_repository->save(
|
||||||
$payment_data,
|
$payment_data,
|
||||||
PaymentFactory::create( $this->company->id, $invoice->user_id, $invoice->client_id )
|
PaymentFactory::create( $this->company->id, $invoice->user_id, $invoice->client_id )
|
||||||
@ -300,6 +303,7 @@ class CSVImport implements ShouldQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->actionInvoiceStatus( $invoice, $invoice_data, $invoice_repository );
|
$this->actionInvoiceStatus( $invoice, $invoice_data, $invoice_repository );
|
||||||
}
|
}
|
||||||
|
@ -47,9 +47,9 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
|
||||||
|
|
||||||
public $tries = 5; //number of retries
|
public $tries = 3; //number of retries
|
||||||
|
|
||||||
public $backoff = 5; //seconds to wait until retry
|
public $backoff = 10; //seconds to wait until retry
|
||||||
|
|
||||||
public $deleteWhenMissingModels = true;
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
|
|
||||||
$this->nmo = $nmo;
|
$this->nmo = $nmo;
|
||||||
|
$this->override = $override;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
//send email
|
//send email
|
||||||
try {
|
try {
|
||||||
nlog("trying to send");
|
nlog("trying to send to {$this->nmo->to_user->email} ". now()->toDateTimeString());
|
||||||
|
|
||||||
Mail::to($this->nmo->to_user->email)
|
Mail::to($this->nmo->to_user->email)
|
||||||
->send($this->nmo->mailable);
|
->send($this->nmo->mailable);
|
||||||
|
@ -30,6 +30,7 @@ use App\Factory\TaxRateFactory;
|
|||||||
use App\Factory\UserFactory;
|
use App\Factory\UserFactory;
|
||||||
use App\Factory\VendorFactory;
|
use App\Factory\VendorFactory;
|
||||||
use App\Http\Requests\Company\UpdateCompanyRequest;
|
use App\Http\Requests\Company\UpdateCompanyRequest;
|
||||||
|
use App\Http\ValidationRules\User\AttachableUser;
|
||||||
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
|
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
|
||||||
use App\Http\ValidationRules\ValidUserForCompany;
|
use App\Http\ValidationRules\ValidUserForCompany;
|
||||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||||
@ -210,8 +211,8 @@ class Import implements ShouldQueue
|
|||||||
$this->{$method}($data[$import]);
|
$this->{$method}($data[$import]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Ninja::isHosted())
|
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
|
||||||
$this->processNinjaTokens($data['ninja_tokens']);
|
// $this->processNinjaTokens($data['ninja_tokens']);
|
||||||
|
|
||||||
$this->setInitialCompanyLedgerBalances();
|
$this->setInitialCompanyLedgerBalances();
|
||||||
|
|
||||||
@ -225,6 +226,7 @@ class Import implements ShouldQueue
|
|||||||
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
||||||
}
|
}
|
||||||
catch(\Exception $e) {
|
catch(\Exception $e) {
|
||||||
|
|
||||||
nlog($e->getMessage());
|
nlog($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +298,12 @@ class Import implements ShouldQueue
|
|||||||
|
|
||||||
$data = $this->transformCompanyData($data);
|
$data = $this->transformCompanyData($data);
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && strlen($data['subdomain']) > 1) {
|
||||||
|
|
||||||
|
if(!MultiDB::checkDomainAvailable($data['subdomain']))
|
||||||
|
$data['subdomain'] = MultiDB::randomSubdomainGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
$rules = (new UpdateCompanyRequest())->rules();
|
$rules = (new UpdateCompanyRequest())->rules();
|
||||||
|
|
||||||
$validator = Validator::make($data, $rules);
|
$validator = Validator::make($data, $rules);
|
||||||
@ -419,13 +427,10 @@ class Import implements ShouldQueue
|
|||||||
$rules = [
|
$rules = [
|
||||||
'*.first_name' => ['string'],
|
'*.first_name' => ['string'],
|
||||||
'*.last_name' => ['string'],
|
'*.last_name' => ['string'],
|
||||||
'*.email' => ['distinct'],
|
//'*.email' => ['distinct'],
|
||||||
|
'*.email' => ['distinct', 'email', new ValidUserForCompany()],
|
||||||
];
|
];
|
||||||
|
|
||||||
// if (config('ninja.db.multi_db_enabled')) {
|
|
||||||
// array_push($rules['*.email'], new ValidUserForCompany());
|
|
||||||
// }
|
|
||||||
|
|
||||||
$validator = Validator::make($data, $rules);
|
$validator = Validator::make($data, $rules);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
@ -1647,53 +1652,65 @@ class Import implements ShouldQueue
|
|||||||
|
|
||||||
private function buildNewUserPlan()
|
private function buildNewUserPlan()
|
||||||
{
|
{
|
||||||
$local_company = Company::find($this->company->id);
|
$current_db = config('database.default');
|
||||||
$owner = $local_company->owner();
|
|
||||||
|
|
||||||
$ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id'));
|
nlog($this->company);
|
||||||
|
|
||||||
|
$local_company = Company::on($current_db)->where('company_key', $this->company->company_key)->first();
|
||||||
|
|
||||||
|
MultiDB::setDb('db-ninja-01');
|
||||||
|
$ninja_company = Company::find(config('ninja.ninja_default_company_id'));
|
||||||
|
|
||||||
/* If we already have a record of this user - move along. */
|
/* If we already have a record of this user - move along. */
|
||||||
if($client_contact = ClientContact::on('db-ninja-01')->where(['email' => $owner->email, 'company_id' => $ninja_company->id])->exists())
|
if($client_contact = ClientContact::where(['email' => $this->user->email, 'company_id' => $ninja_company->id])->first())
|
||||||
return $client_contact->client;
|
return $client_contact->client;
|
||||||
|
|
||||||
$ninja_client = ClientFactory::create($ninja_company->id, $ninja_company->owner()->id);
|
$ninja_client = ClientFactory::create($ninja_company->id, $ninja_company->owner()->id);
|
||||||
$ninja_client->setConnection('db-ninja-01');
|
$ninja_client->name = $this->user->present()->name();
|
||||||
$ninja_client->name = $owner->present()->name();
|
|
||||||
$ninja_client->address1 = $local_company->settings->address1;
|
$ninja_client->address1 = $local_company->settings->address1;
|
||||||
$ninja_client->address2 = $local_company->settings->address2;
|
$ninja_client->address2 = $local_company->settings->address2;
|
||||||
$ninja_client->city = $local_company->settings->city;
|
$ninja_client->city = $local_company->settings->city;
|
||||||
$ninja_client->postal_code = $local_company->settings->postal_code;
|
$ninja_client->postal_code = $local_company->settings->postal_code;
|
||||||
$ninja_client->state = $local_company->settings->state;
|
$ninja_client->state = $local_company->settings->state;
|
||||||
$ninja_client->country_id = $local_company->settings->country_id;
|
$ninja_client->country_id = $local_company->settings->country_id;
|
||||||
|
$ninja_client->custom_value1 = $local_company->company_key;
|
||||||
|
|
||||||
$ninja_client->save();
|
$ninja_client->save();
|
||||||
|
|
||||||
$ninja_client_contact = ClientContactFactory::create($ninja_company->id, $ninja_company->owner()->id);
|
$ninja_client_contact = ClientContactFactory::create($ninja_company->id, $ninja_company->owner()->id);
|
||||||
$ninja_client_contact->setConnection('db-ninja-01');
|
$ninja_client_contact->first_name = $this->user->first_name;
|
||||||
$ninja_client_contact->first_name = $owner->first_name;
|
$ninja_client_contact->last_name = $this->user->last_name;
|
||||||
$ninja_client_contact->last_name = $owner->last_name;
|
|
||||||
$ninja_client_contact->client_id = $ninja_client->id;
|
$ninja_client_contact->client_id = $ninja_client->id;
|
||||||
$ninja_client_contact->email = $owner->email;
|
$ninja_client_contact->email = $this->user->email;
|
||||||
$ninja_client_contact->phone = $owner->phone;
|
$ninja_client_contact->phone = $this->user->phone;
|
||||||
$ninja_client_contact->save();
|
$ninja_client_contact->save();
|
||||||
|
|
||||||
|
|
||||||
|
MultiDB::setDb($current_db);
|
||||||
|
|
||||||
return $ninja_client;
|
return $ninja_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processNinjaTokens(array $data)
|
private function processNinjaTokens(array $data)
|
||||||
{
|
{
|
||||||
if(count($data) == 0)
|
$current_db = config('database.default');
|
||||||
|
$local_company = Company::on($current_db)->where('company_key', $this->company->company_key)->first();
|
||||||
|
|
||||||
|
MultiDB::setDb('db-ninja-01');
|
||||||
|
|
||||||
|
if($existing_client = Client::where('custom_value1', $local_company->company_key)->first())
|
||||||
|
$ninja_client = $existing_client;
|
||||||
|
else
|
||||||
$ninja_client = $this->buildNewUserPlan();
|
$ninja_client = $this->buildNewUserPlan();
|
||||||
|
|
||||||
foreach($data as $token)
|
foreach($data as $token)
|
||||||
{
|
{
|
||||||
//get invoiceninja company_id
|
//get invoiceninja company_id
|
||||||
$ninja_company = Company::on('db-ninja-01')->where('id', config('ninja.ninja_default_company_id'))->first();
|
$ninja_company = Company::where('id', config('ninja.ninja_default_company_id'))->first();
|
||||||
|
|
||||||
$token['company_id'] = $ninja_client->company_id;
|
$token['company_id'] = $ninja_company->id;
|
||||||
$token['client_id'] = $ninja_client->id;
|
$token['client_id'] = $ninja_client->id;/////
|
||||||
$token['user_id'] = $ninja_client->user_id;
|
$token['user_id'] = $ninja_company->owner()->id;
|
||||||
$token['company_gateway_id'] = config('ninja.ninja_default_company_gateway_id');
|
$token['company_gateway_id'] = config('ninja.ninja_default_company_gateway_id');
|
||||||
//todo
|
//todo
|
||||||
|
|
||||||
@ -1702,8 +1719,10 @@ class Import implements ShouldQueue
|
|||||||
ClientGatewayToken::reguard();
|
ClientGatewayToken::reguard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultiDB::setDb($current_db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* In V4 we use negative invoices (credits) and add then into the client balance. In V5, these sit off ledger and are applied later.
|
/* In V4 we use negative invoices (credits) and add then into the client balance. In V5, these sit off ledger and are applied later.
|
||||||
This next section will check for credit balances and reduce the client balance so that the V5 balances are correct
|
This next section will check for credit balances and reduce the client balance so that the V5 balances are correct
|
||||||
*/
|
*/
|
||||||
|
@ -16,6 +16,7 @@ use App\Jobs\Entity\EmailEntity;
|
|||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
|
use App\Utils\Traits\MakesReminders;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@ -25,7 +26,7 @@ use Illuminate\Support\Carbon;
|
|||||||
|
|
||||||
class ReminderJob implements ShouldQueue
|
class ReminderJob implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesReminders;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -39,40 +40,41 @@ class ReminderJob implements ShouldQueue
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|
||||||
//always make sure you have set the company as this command is being
|
|
||||||
//run from the console so we have no awareness of the DB.
|
|
||||||
|
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled')) {
|
||||||
$this->processReminders();
|
$this->processReminders();
|
||||||
} else {
|
} else {
|
||||||
//multiDB environment, need to
|
//multiDB environment, need to
|
||||||
foreach (MultiDB::$dbs as $db) {
|
foreach (MultiDB::$dbs as $db) {
|
||||||
MultiDB::setDB($db);
|
MultiDB::setDB($db);
|
||||||
|
$this->processReminders();
|
||||||
$this->processReminders($db);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processReminders($db = null)
|
private function processReminders()
|
||||||
{
|
{
|
||||||
Invoice::where('next_send_date', Carbon::today()->format('Y-m-d'))->with('invitations')->cursor()->each(function ($invoice) {
|
Invoice::where('next_send_date', Carbon::today()->format('Y-m-d'))->with('invitations')->cursor()->each(function ($invoice) {
|
||||||
|
|
||||||
if ($invoice->isPayable()) {
|
if ($invoice->isPayable()) {
|
||||||
$reminder_template = $invoice->calculateTemplate('invoice');
|
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||||
$invoice->service()->touchReminder($reminder_template)->save();
|
$invoice->service()->touchReminder($reminder_template)->save();
|
||||||
|
|
||||||
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
|
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
|
||||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
|
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
|
||||||
nlog("Firing email for invoice {$invoice->number}");
|
nlog("Firing reminder email for invoice {$invoice->number}");
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($invoice->invitations->count() > 0) {
|
if ($invoice->invitations->count() > 0) {
|
||||||
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
|
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$invoice->service()->setReminder()->save();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$invoice->next_send_date = null;
|
$invoice->next_send_date = null;
|
||||||
$invoice->save();
|
$invoice->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use App\Libraries\MultiDB;
|
|||||||
use App\Mail\MigrationFailed;
|
use App\Mail\MigrationFailed;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@ -139,7 +140,11 @@ class StartMigration implements ShouldQueue
|
|||||||
$this->company->update_products = $update_product_flag;
|
$this->company->update_products = $update_product_flag;
|
||||||
$this->company->save();
|
$this->company->save();
|
||||||
|
|
||||||
Mail::to($this->user->email, $this->user->name())->send(new MigrationFailed($e, $e->getMessage()));
|
|
||||||
|
if(Ninja::isHosted())
|
||||||
|
app('sentry')->captureException($e);
|
||||||
|
|
||||||
|
Mail::to($this->user->email, $this->user->name())->send(new MigrationFailed($e, $this->company, $e->getMessage()));
|
||||||
|
|
||||||
if (app()->environment() !== 'production') {
|
if (app()->environment() !== 'production') {
|
||||||
info($e->getMessage());
|
info($e->getMessage());
|
||||||
|
@ -35,9 +35,9 @@ class WebhookHandler implements ShouldQueue
|
|||||||
|
|
||||||
private $company;
|
private $company;
|
||||||
|
|
||||||
public $tries = 5; //number of retries
|
public $tries = 3; //number of retries
|
||||||
|
|
||||||
public $backoff = 5; //seconds to wait until retry
|
public $backoff = 10; //seconds to wait until retry
|
||||||
|
|
||||||
public $deleteWhenMissingModels = true;
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
|
@ -52,37 +52,38 @@ class MultiDB
|
|||||||
|
|
||||||
public static function checkDomainAvailable($subdomain) : bool
|
public static function checkDomainAvailable($subdomain) : bool
|
||||||
{
|
{
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled'))
|
||||||
return Company::whereSubdomain($subdomain)->get()->count() == 0;
|
return Company::whereSubdomain($subdomain)->get()->count() == 0;
|
||||||
}
|
|
||||||
|
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
//multi-db active
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if (Company::on($db)->whereSubdomain($subdomain)->get()->count() >= 1) {
|
if (Company::on($db)->whereSubdomain($subdomain)->get()->count() >= 1) {
|
||||||
|
self::setDb($current_db);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//self::setDefaultDatabase();
|
self::setDb($current_db);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function checkUserEmailExists($email) : bool
|
public static function checkUserEmailExists($email) : bool
|
||||||
{
|
{
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled'))
|
||||||
return User::where(['email' => $email])->get()->count() >= 1 ?? false; // true >= 1 emails found / false -> == emails found
|
return User::where(['email' => $email])->get()->count() >= 1 ?? false; // true >= 1 emails found / false -> == emails found
|
||||||
}
|
|
||||||
|
|
||||||
//multi-db active
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if (User::on($db)->where(['email' => $email])->get()->count() >= 1) { // if user already exists, validation will fail
|
if (User::on($db)->where(['email' => $email])->get()->count() >= 1) { // if user already exists, validation will fail
|
||||||
|
self::setDb($current_db);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDb($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -102,19 +103,21 @@ class MultiDB
|
|||||||
*/
|
*/
|
||||||
public static function checkUserAndCompanyCoExist($email, $company_key) :bool
|
public static function checkUserAndCompanyCoExist($email, $company_key) :bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if (User::on($db)->where(['email' => $email])->get()->count() >= 1) { // if user already exists, validation will fail
|
if (User::on($db)->where(['email' => $email])->exists()) {
|
||||||
if (Company::on($db)->where(['company_key' => $company_key])->get()->count() >= 1) {
|
if (Company::on($db)->where(['company_key' => $company_key])->exists()) {
|
||||||
|
self::setDb($current_db);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
self::setDefaultDatabase();
|
self::setDb($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDb($current_db);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -125,20 +128,21 @@ class MultiDB
|
|||||||
*/
|
*/
|
||||||
public static function hasUser(array $data) : ?User
|
public static function hasUser(array $data) : ?User
|
||||||
{
|
{
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled'))
|
||||||
return User::where($data)->withTrashed()->first();
|
return User::where($data)->withTrashed()->first();
|
||||||
}
|
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
|
|
||||||
self::setDB($db);
|
self::setDB($db);
|
||||||
|
if ($user = User::where($data)->withTrashed()->first()) {
|
||||||
if ($user = User::where($data)->withTrashed()->first())
|
|
||||||
return $user;
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDb($current_db);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -149,125 +153,139 @@ class MultiDB
|
|||||||
*/
|
*/
|
||||||
public static function hasContact(string $email) : ?ClientContact
|
public static function hasContact(string $email) : ?ClientContact
|
||||||
{
|
{
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled'))
|
||||||
return ClientContact::where('email', $email)->withTrashed()->first();
|
return ClientContact::where('email', $email)->withTrashed()->first();
|
||||||
}
|
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
|
|
||||||
$user = ClientContact::on($db)->where('email', $email)->withTrashed()->first();
|
$user = ClientContact::on($db)->where('email', $email)->withTrashed()->first();
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
self::setDB($db);
|
self::setDb($db);
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDB($current_db);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function contactFindAndSetDb($token) :bool
|
public static function contactFindAndSetDb($token) :bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($ct = ClientContact::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) {
|
if ($ct = ClientContact::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) {
|
||||||
self::setDb($ct->company->db);
|
self::setDb($db);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function userFindAndSetDb($email) : bool
|
public static function userFindAndSetDb($email) : bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
//multi-db active
|
//multi-db active
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
|
|
||||||
if (User::on($db)->where('email', $email)->count() >= 1){
|
if (User::on($db)->where('email', $email)->count() >= 1){
|
||||||
nlog("setting db {$db}");
|
|
||||||
self::setDb($db);
|
self::setDb($db);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDB($current_db);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function findAndSetDb($token) :bool
|
public static function findAndSetDb($token) :bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($ct = CompanyToken::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) {
|
if ($ct = CompanyToken::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) {
|
||||||
self::setDb($ct->company->db);
|
self::setDb($ct->company->db);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self::setDefaultDatabase();
|
|
||||||
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function findAndSetDbByCompanyKey($company_key) :bool
|
public static function findAndSetDbByCompanyKey($company_key) :bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($company = Company::on($db)->where('company_key', $company_key)->first()) {
|
if ($company = Company::on($db)->where('company_key', $company_key)->first()) {
|
||||||
self::setDb($company->db);
|
self::setDb($company->db);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self::setDefaultDatabase();
|
|
||||||
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function findAndSetDbByContactKey($contact_key) :bool
|
public static function findAndSetDbByContactKey($contact_key) :bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($client_contact = ClientContact::on($db)->where('contact_key', $contact_key)->first()) {
|
if ($client_contact = ClientContact::on($db)->where('contact_key', $contact_key)->first()) {
|
||||||
self::setDb($client_contact->company->db);
|
self::setDb($client_contact->company->db);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self::setDefaultDatabase();
|
|
||||||
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function findAndSetDbByClientHash($client_hash) :bool
|
public static function findAndSetDbByClientHash($client_hash) :bool
|
||||||
{
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($client = Client::on($db)->where('client_hash', $client_hash)->first()) {
|
if ($client = Client::on($db)->where('client_hash', $client_hash)->first()) {
|
||||||
self::setDb($client->company->db);
|
self::setDb($client->company->db);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self::setDefaultDatabase();
|
|
||||||
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function findAndSetDbByDomain($query_array) :bool
|
public static function findAndSetDbByDomain($query_array)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (! config('ninja.db.multi_db_enabled'))
|
if (! config('ninja.db.multi_db_enabled'))
|
||||||
return (Company::where($query_array)->exists() === true);
|
return (Company::where($query_array)->first());
|
||||||
|
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($company = Company::on($db)->where($query_array)->first()) {
|
if ($company = Company::on($db)->where($query_array)->first()) {
|
||||||
self::setDb($company->db);
|
self::setDb($company->db);
|
||||||
return true;
|
return $company;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -275,20 +293,48 @@ class MultiDB
|
|||||||
public static function findAndSetDbByInvitation($entity, $invitation_key)
|
public static function findAndSetDbByInvitation($entity, $invitation_key)
|
||||||
{
|
{
|
||||||
$class = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
|
$class = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
foreach (self::$dbs as $db) {
|
foreach (self::$dbs as $db) {
|
||||||
if ($invite = $class::on($db)->whereRaw('BINARY `key`= ?', [$invitation_key])->first()) {
|
if ($invite = $class::on($db)->whereRaw('BINARY `key`= ?', [$invitation_key])->first()) {
|
||||||
self::setDb($db);
|
self::setDb($db);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setDefaultDatabase();
|
self::setDB($current_db);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function randomSubdomainGenerator()
|
||||||
|
{
|
||||||
|
$current_db = config('database.default');
|
||||||
|
|
||||||
|
do {
|
||||||
|
$length = 8;
|
||||||
|
$string = '';
|
||||||
|
$vowels = array("a","e","i","o","u");
|
||||||
|
$consonants = array(
|
||||||
|
'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'p', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
|
||||||
|
);
|
||||||
|
|
||||||
|
$max = $length / 2;
|
||||||
|
for ($i = 1; $i <= $max; $i++)
|
||||||
|
{
|
||||||
|
$string .= $consonants[rand(0,19)];
|
||||||
|
$string .= $vowels[rand(0,4)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(!self::checkDomainAvailable($string));
|
||||||
|
|
||||||
|
self::setDb($current_db);
|
||||||
|
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $database
|
* @param $database
|
||||||
*/
|
*/
|
||||||
|
@ -8,21 +8,23 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
|
|
||||||
class MigrationFailed extends Mailable
|
class MigrationFailed extends Mailable
|
||||||
{
|
{
|
||||||
// use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public $exception;
|
public $exception;
|
||||||
public $content;
|
public $content;
|
||||||
|
public $settings;
|
||||||
|
public $company;
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
* @param $content
|
* @param $content
|
||||||
* @param $exception
|
* @param $exception
|
||||||
*/
|
*/
|
||||||
public function __construct($exception, $content = null)
|
public function __construct($exception, $company, $content = null)
|
||||||
{
|
{
|
||||||
$this->exception = $exception;
|
$this->exception = $exception;
|
||||||
$this->content = $content;
|
$this->content = $content;
|
||||||
|
$this->settings = $company->settings;
|
||||||
|
$this->company = $company;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,6 +35,6 @@ class MigrationFailed extends Mailable
|
|||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
->view('email.migration.failed');
|
->view('email.migration.failed', ['settings' => $this->settings, 'company' => $this->company]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,8 @@ class SupportMessageSent extends Mailable
|
|||||||
|
|
||||||
$subject = "Customer MSG {$user->present()->name} - [{$plan} - DB:{$company->db}]";
|
$subject = "Customer MSG {$user->present()->name} - [{$plan} - DB:{$company->db}]";
|
||||||
|
|
||||||
return $this->from(config('mail.from.address'), config('mail.from.name')) //todo this needs to be fixed to handle the hosted version
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
|
->replyTo($user->email, $user->present()->name())
|
||||||
->subject($subject)
|
->subject($subject)
|
||||||
->markdown('email.support.message', [
|
->markdown('email.support.message', [
|
||||||
'message' => $this->message,
|
'message' => $this->message,
|
||||||
|
@ -103,6 +103,15 @@ class Activity extends StaticModel
|
|||||||
'deleted_at' => 'timestamp',
|
'deleted_at' => 'timestamp',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $appends = [
|
||||||
|
'hashed_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function getHashedIdAttribute()
|
||||||
|
{
|
||||||
|
return $this->encodePrimaryKey($this->id);
|
||||||
|
}
|
||||||
|
|
||||||
public function getEntityType()
|
public function getEntityType()
|
||||||
{
|
{
|
||||||
return self::class;
|
return self::class;
|
||||||
|
@ -36,10 +36,6 @@ class BaseModel extends Model
|
|||||||
use UserSessionAttributes;
|
use UserSessionAttributes;
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
//todo customise names of archived_at / updated_at columns
|
|
||||||
///const CREATED_AT = 'creation_date';
|
|
||||||
//const UPDATED_AT = 'last_update';
|
|
||||||
|
|
||||||
protected $appends = [
|
protected $appends = [
|
||||||
'hashed_id',
|
'hashed_id',
|
||||||
];
|
];
|
||||||
|
@ -40,7 +40,6 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
'private_notes',
|
'private_notes',
|
||||||
'user_id',
|
'user_id',
|
||||||
'company_id',
|
'company_id',
|
||||||
// 'settings',
|
|
||||||
'last_login',
|
'last_login',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -131,6 +131,11 @@ class Company extends BaseModel
|
|||||||
return $this->morphMany(Document::class, 'documentable');
|
return $this->morphMany(Document::class, 'documentable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function all_documents()
|
||||||
|
{
|
||||||
|
return $this->HasMany(Document::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function getEntityType()
|
public function getEntityType()
|
||||||
{
|
{
|
||||||
return self::class;
|
return self::class;
|
||||||
|
@ -71,10 +71,6 @@ class Quote extends BaseModel
|
|||||||
'custom_surcharge2',
|
'custom_surcharge2',
|
||||||
'custom_surcharge3',
|
'custom_surcharge3',
|
||||||
'custom_surcharge4',
|
'custom_surcharge4',
|
||||||
// 'custom_surcharge_tax1',
|
|
||||||
// 'custom_surcharge_tax2',
|
|
||||||
// 'custom_surcharge_tax3',
|
|
||||||
// 'custom_surcharge_tax4',
|
|
||||||
'design_id',
|
'design_id',
|
||||||
'assigned_user_id',
|
'assigned_user_id',
|
||||||
'exchange_rate',
|
'exchange_rate',
|
||||||
|
@ -159,8 +159,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
*/
|
*/
|
||||||
public function setCompany($company)
|
public function setCompany($company)
|
||||||
{
|
{
|
||||||
// config(['ninja.company_id' => $company->id]);
|
|
||||||
|
|
||||||
$this->company = $company;
|
$this->company = $company;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,16 +168,17 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
public function getCompany()
|
public function getCompany()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (request()->header('X-API-TOKEN')) {
|
if ($this->company){
|
||||||
$company_token = CompanyToken::with(['company'])->whereRaw('BINARY `token`= ?', [request()->header('X-API-TOKEN')])->first();
|
|
||||||
|
|
||||||
return $company_token->company;
|
|
||||||
}
|
|
||||||
elseif ($this->company){
|
|
||||||
|
|
||||||
return $this->company;
|
return $this->company;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
elseif (request()->header('X-API-TOKEN')) {
|
||||||
|
$company_token = CompanyToken::with(['company'])->whereRaw('BINARY `token`= ?', [request()->header('X-API-TOKEN')])->first();
|
||||||
|
|
||||||
|
return $company_token->company;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// return false;
|
// return false;
|
||||||
throw new \Exception('No Company Found');
|
throw new \Exception('No Company Found');
|
||||||
@ -408,7 +407,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
$nmo->settings = $this->account->default_company->settings;
|
$nmo->settings = $this->account->default_company->settings;
|
||||||
$nmo->company = $this->account->default_company;
|
$nmo->company = $this->account->default_company;
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
NinjaMailerJob::dispatch($nmo, true);
|
||||||
|
|
||||||
//$this->notify(new ResetPasswordNotification($token));
|
//$this->notify(new ResetPasswordNotification($token));
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,6 @@ class CompanyObserver
|
|||||||
|
|
||||||
if(Ninja::isHosted() && $company->portal_mode == 'domain' && $company->isDirty('portal_domain'))
|
if(Ninja::isHosted() && $company->portal_mode == 'domain' && $company->isDirty('portal_domain'))
|
||||||
{
|
{
|
||||||
nlog('company observer - updated');
|
|
||||||
nlog($company->portal_domain);
|
|
||||||
nlog($company->getOriginal('portal_domain'));
|
|
||||||
|
|
||||||
//fire event to build new custom portal domain
|
//fire event to build new custom portal domain
|
||||||
\Modules\Admin\Jobs\Domain\CustomDomain::dispatch($company->getOriginal('portal_domain'), $company)->onQueue('domain');
|
\Modules\Admin\Jobs\Domain\CustomDomain::dispatch($company->getOriginal('portal_domain'), $company)->onQueue('domain');
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,9 @@ class InvoiceObserver
|
|||||||
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if($invoice->isDirty('date') || $invoice->isDirty('due_date'))
|
||||||
|
// $invoice->service()->setReminder()->save();
|
||||||
|
|
||||||
// UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath() . $invoice->numberFormatter().'.pdf');
|
// UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath() . $invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
|||||||
$this->initializeOmnipayGateway();
|
$this->initializeOmnipayGateway();
|
||||||
|
|
||||||
$response = $this->omnipay_gateway
|
$response = $this->omnipay_gateway
|
||||||
->completePurchase(['amount' => $this->payment_hash->data->amount])
|
->completePurchase(['amount' => $this->payment_hash->data->amount, 'currency' => $this->client->getCurrencyCode()])
|
||||||
->send();
|
->send();
|
||||||
|
|
||||||
if ($response->isCancelled()) {
|
if ($response->isCancelled()) {
|
||||||
@ -187,7 +187,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
|||||||
'cancelUrl' => $this->client->company->domain() . '/client/invoices',
|
'cancelUrl' => $this->client->company->domain() . '/client/invoices',
|
||||||
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
||||||
->map(function ($invoice) {
|
->map(function ($invoice) {
|
||||||
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number);
|
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->number);
|
||||||
})->toArray()),
|
})->toArray()),
|
||||||
'transactionId' => $this->payment_hash->hash . '-' . time(),
|
'transactionId' => $this->payment_hash->hash . '-' . time(),
|
||||||
'ButtonSource' => 'InvoiceNinja_SP',
|
'ButtonSource' => 'InvoiceNinja_SP',
|
||||||
|
@ -193,7 +193,8 @@ class ACH
|
|||||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||||
SystemLog::TYPE_STRIPE,
|
SystemLog::TYPE_STRIPE,
|
||||||
$this->stripe->client
|
$this->stripe->client,
|
||||||
|
$this->stripe->client->company,
|
||||||
);
|
);
|
||||||
|
|
||||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||||
|
@ -82,7 +82,7 @@ class ImportCustomers
|
|||||||
}
|
}
|
||||||
|
|
||||||
nlog("inserting a customer");
|
nlog("inserting a customer");
|
||||||
nlog($customer);
|
//nlog($customer);
|
||||||
|
|
||||||
$client = ClientFactory::create($this->stripe->company_gateway->company_id, $this->stripe->company_gateway->user_id);
|
$client = ClientFactory::create($this->stripe->company_gateway->company_id, $this->stripe->company_gateway->user_id);
|
||||||
|
|
||||||
|
@ -21,6 +21,6 @@ trait Utilities
|
|||||||
|
|
||||||
public function convertToStripeAmount($amount, $precision)
|
public function convertToStripeAmount($amount, $precision)
|
||||||
{
|
{
|
||||||
return $amount * pow(10, $precision);
|
return (int)($amount * pow(10, $precision));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
if ($this->company_gateway->require_billing_address) {
|
if ($this->company_gateway->require_billing_address) {
|
||||||
$fields[] = ['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required'];
|
||||||
$fields[] = ['name' => 'client_address_line_2', 'label' => ctrans('texts.address2'), 'type' => 'text', 'validation' => 'sometimes'];
|
// $fields[] = ['name' => 'client_address_line_2', 'label' => ctrans('texts.address2'), 'type' => 'text', 'validation' => 'nullable'];
|
||||||
$fields[] = ['name' => 'client_city', 'label' => ctrans('texts.city'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_city', 'label' => ctrans('texts.city'), 'type' => 'text', 'validation' => 'required'];
|
||||||
$fields[] = ['name' => 'client_state', 'label' => ctrans('texts.state'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_state', 'label' => ctrans('texts.state'), 'type' => 'text', 'validation' => 'required'];
|
||||||
$fields[] = ['name' => 'client_country_id', 'label' => ctrans('texts.country'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_country_id', 'label' => ctrans('texts.country'), 'type' => 'text', 'validation' => 'required'];
|
||||||
@ -197,7 +197,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
if ($this->company_gateway->require_shipping_address) {
|
if ($this->company_gateway->require_shipping_address) {
|
||||||
$fields[] = ['name' => 'client_shipping_address_line_1', 'label' => ctrans('texts.shipping_address1'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_shipping_address_line_1', 'label' => ctrans('texts.shipping_address1'), 'type' => 'text', 'validation' => 'required'];
|
||||||
$fields[] = ['name' => 'client_shipping_address_line_2', 'label' => ctrans('texts.shipping_address2'), 'type' => 'text', 'validation' => 'sometimes'];
|
// $fields[] = ['name' => 'client_shipping_address_line_2', 'label' => ctrans('texts.shipping_address2'), 'type' => 'text', 'validation' => 'sometimes'];
|
||||||
$fields[] = ['name' => 'client_shipping_city', 'label' => ctrans('texts.shipping_city'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_shipping_city', 'label' => ctrans('texts.shipping_city'), 'type' => 'text', 'validation' => 'required'];
|
||||||
$fields[] = ['name' => 'client_shipping_state', 'label' => ctrans('texts.shipping_state'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_shipping_state', 'label' => ctrans('texts.shipping_state'), 'type' => 'text', 'validation' => 'required'];
|
||||||
$fields[] = ['name' => 'client_shipping_postal_code', 'label' => ctrans('texts.shipping_postal_code'), 'type' => 'text', 'validation' => 'required'];
|
$fields[] = ['name' => 'client_shipping_postal_code', 'label' => ctrans('texts.shipping_postal_code'), 'type' => 'text', 'validation' => 'required'];
|
||||||
@ -390,6 +390,13 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
$payment->save();
|
$payment->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($request->type == 'charge.succeeded') {
|
||||||
|
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||||
|
$payment->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// charge.failed, charge.refunded
|
||||||
|
|
||||||
return response([], 200);
|
return response([], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Http\Middleware\SetDomainNameDb;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\Subscription;
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\CompanyGateway;
|
use App\Models\CompanyGateway;
|
||||||
@ -24,10 +24,10 @@ use App\Models\Payment;
|
|||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\Proposal;
|
use App\Models\Proposal;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
|
use App\Models\Subscription;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Observers\AccountObserver;
|
use App\Observers\AccountObserver;
|
||||||
use App\Observers\SubscriptionObserver;
|
|
||||||
use App\Observers\ClientObserver;
|
use App\Observers\ClientObserver;
|
||||||
use App\Observers\CompanyGatewayObserver;
|
use App\Observers\CompanyGatewayObserver;
|
||||||
use App\Observers\CompanyObserver;
|
use App\Observers\CompanyObserver;
|
||||||
@ -39,8 +39,10 @@ use App\Observers\PaymentObserver;
|
|||||||
use App\Observers\ProductObserver;
|
use App\Observers\ProductObserver;
|
||||||
use App\Observers\ProposalObserver;
|
use App\Observers\ProposalObserver;
|
||||||
use App\Observers\QuoteObserver;
|
use App\Observers\QuoteObserver;
|
||||||
|
use App\Observers\SubscriptionObserver;
|
||||||
use App\Observers\TaskObserver;
|
use App\Observers\TaskObserver;
|
||||||
use App\Observers\UserObserver;
|
use App\Observers\UserObserver;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Cache\RateLimiting\Limit;
|
use Illuminate\Cache\RateLimiting\Limit;
|
||||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
use Illuminate\Queue\Events\JobProcessing;
|
use Illuminate\Queue\Events\JobProcessing;
|
||||||
@ -49,6 +51,7 @@ use Illuminate\Support\Facades\Queue;
|
|||||||
use Illuminate\Support\Facades\RateLimiter;
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@ -93,6 +96,15 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
Task::observe(TaskObserver::class);
|
Task::observe(TaskObserver::class);
|
||||||
User::observe(UserObserver::class);
|
User::observe(UserObserver::class);
|
||||||
|
|
||||||
|
|
||||||
|
/* Handles setting the correct database with livewire classes */
|
||||||
|
if(Ninja::isHosted())
|
||||||
|
{
|
||||||
|
Livewire::addPersistentMiddleware([
|
||||||
|
SetDomainNameDb::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Queue::before(function (JobProcessing $event) {
|
// Queue::before(function (JobProcessing $event) {
|
||||||
// // \Log::info('Event Job '.$event->connectionName);
|
// // \Log::info('Event Job '.$event->connectionName);
|
||||||
// \Log::error('Event Job '.$event->job->getJobId);
|
// \Log::error('Event Job '.$event->job->getJobId);
|
||||||
|
@ -169,10 +169,14 @@ class BaseRepository
|
|||||||
*/
|
*/
|
||||||
protected function alternativeSave($data, $model)
|
protected function alternativeSave($data, $model)
|
||||||
{
|
{
|
||||||
|
//forces the client_id if it doesn't exist
|
||||||
if (array_key_exists('client_id', $data)) //forces the client_id if it doesn't exist
|
if(array_key_exists('client_id', $data))
|
||||||
$model->client_id = $data['client_id'];
|
$model->client_id = $data['client_id'];
|
||||||
|
|
||||||
|
//pickup changes here to recalculate reminders
|
||||||
|
if($model instanceof Invoice && ($model->isDirty('date') || $model->isDirty('due_date')))
|
||||||
|
$model->service()->setReminder()->save();
|
||||||
|
|
||||||
$client = Client::where('id', $model->client_id)->withTrashed()->first();
|
$client = Client::where('id', $model->client_id)->withTrashed()->first();
|
||||||
|
|
||||||
$state = [];
|
$state = [];
|
||||||
@ -189,7 +193,7 @@ class BaseRepository
|
|||||||
$data = array_merge($company_defaults, $data);
|
$data = array_merge($company_defaults, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmp_data = $data; //preserves the $data arrayss
|
$tmp_data = $data; //preserves the $data array
|
||||||
|
|
||||||
/* We need to unset some variable as we sometimes unguard the model */
|
/* We need to unset some variable as we sometimes unguard the model */
|
||||||
if (isset($tmp_data['invitations']))
|
if (isset($tmp_data['invitations']))
|
||||||
@ -302,6 +306,10 @@ class BaseRepository
|
|||||||
/* Perform model specific tasks */
|
/* Perform model specific tasks */
|
||||||
if ($model instanceof Invoice) {
|
if ($model instanceof Invoice) {
|
||||||
|
|
||||||
|
nlog("Finished amount = " . $state['finished_amount']);
|
||||||
|
nlog("Starting amount = " . $state['starting_amount']);
|
||||||
|
nlog("Diff = " . ($state['finished_amount'] - $state['starting_amount']));
|
||||||
|
|
||||||
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
|
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
|
||||||
|
|
||||||
$model->service()->updateStatus()->save();
|
$model->service()->updateStatus()->save();
|
||||||
|
@ -114,7 +114,7 @@ class UserRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
$user->restore();
|
$user->restore();
|
||||||
|
|
||||||
return $user;
|
return $user->fresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(array $data, User $user)
|
public function destroy(array $data, User $user)
|
||||||
|
@ -40,6 +40,8 @@ class AutoBillInvoice extends AbstractService
|
|||||||
|
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
$is_partial = false;
|
||||||
|
|
||||||
/* Is the invoice payable? */
|
/* Is the invoice payable? */
|
||||||
if (! $this->invoice->isPayable())
|
if (! $this->invoice->isPayable())
|
||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
@ -57,6 +59,8 @@ class AutoBillInvoice extends AbstractService
|
|||||||
|
|
||||||
/* Determine $amount */
|
/* Determine $amount */
|
||||||
if ($this->invoice->partial > 0) {
|
if ($this->invoice->partial > 0) {
|
||||||
|
$is_partial = true;
|
||||||
|
$invoice_total = $this->invoice->amount;
|
||||||
$amount = $this->invoice->partial;
|
$amount = $this->invoice->partial;
|
||||||
} elseif ($this->invoice->balance > 0) {
|
} elseif ($this->invoice->balance > 0) {
|
||||||
$amount = $this->invoice->balance;
|
$amount = $this->invoice->balance;
|
||||||
@ -77,6 +81,9 @@ class AutoBillInvoice extends AbstractService
|
|||||||
//$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
|
//$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
|
||||||
$this->invoice = $this->invoice->service()->addGatewayFee($gateway_token->gateway, $gateway_token->gateway_type_id, $amount)->save();
|
$this->invoice = $this->invoice->service()->addGatewayFee($gateway_token->gateway, $gateway_token->gateway_type_id, $amount)->save();
|
||||||
|
|
||||||
|
if($is_partial)
|
||||||
|
$fee = $this->invoice->amount - $invoice_total;
|
||||||
|
else
|
||||||
$fee = $this->invoice->amount - $amount;
|
$fee = $this->invoice->amount - $amount;
|
||||||
|
|
||||||
/* Build payment hash */
|
/* Build payment hash */
|
||||||
@ -340,68 +347,4 @@ class AutoBillInvoice extends AbstractService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes any existing unpaid gateway fees
|
|
||||||
* due to previous payment failure.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
// private function purgeStaleGatewayFees()
|
|
||||||
// {
|
|
||||||
// $starting_amount = $this->invoice->amount;
|
|
||||||
|
|
||||||
// $line_items = $this->invoice->line_items;
|
|
||||||
|
|
||||||
// $new_items = [];
|
|
||||||
|
|
||||||
// foreach($line_items as $item)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// if($item->type_id != 3)
|
|
||||||
// $new_items[] = $item;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $this->invoice->line_items = $new_items;
|
|
||||||
// $this->invoice->save();
|
|
||||||
|
|
||||||
// $this->invoice = $this->invoice->calc()->getInvoice();
|
|
||||||
|
|
||||||
// if($starting_amount != $this->invoice->amount && $this->invoice->status_id != Invoice::STATUS_DRAFT){
|
|
||||||
// $this->invoice->client->service()->updateBalance($this->invoice->amount - $starting_amount)->save();
|
|
||||||
// $this->invoice->ledger()->updateInvoiceBalance($this->invoice->amount - $starting_amount, 'Invoice balance updated after stale gateway fee removed')->save();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return $this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Checks whether a given gateway token is able
|
|
||||||
// * to process the payment after passing through the
|
|
||||||
// * fees and limits check.
|
|
||||||
// *
|
|
||||||
// * @param CompanyGateway $cg The CompanyGateway instance
|
|
||||||
// * @param float $amount The amount to be paid
|
|
||||||
// * @return bool
|
|
||||||
// */
|
|
||||||
// public function validGatewayLimits($cg, $amount) : bool
|
|
||||||
// {
|
|
||||||
// if (isset($cg->fees_and_limits)) {
|
|
||||||
// $fees_and_limits = $cg->fees_and_limits->{'1'};
|
|
||||||
// } else {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $amount < $fees_and_limits->min_limit) {
|
|
||||||
// info("amount {$amount} less than ".$fees_and_limits->min_limit);
|
|
||||||
// $passes = false;
|
|
||||||
// } elseif ((property_exists($fees_and_limits, 'max_limit')) && $fees_and_limits->max_limit !== null && $amount > $fees_and_limits->max_limit) {
|
|
||||||
// info("amount {$amount} greater than ".$fees_and_limits->max_limit);
|
|
||||||
// $passes = false;
|
|
||||||
// } else {
|
|
||||||
// $passes = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return $passes;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ use App\Factory\InvoiceInvitationFactory;
|
|||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\InvoiceInvitation;
|
use App\Models\InvoiceInvitation;
|
||||||
use App\Services\AbstractService;
|
use App\Services\AbstractService;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class CreateInvitations extends AbstractService
|
class CreateInvitations extends AbstractService
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,8 @@ use App\Models\Invoice;
|
|||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
use App\Services\Client\ClientService;
|
use App\Services\Client\ClientService;
|
||||||
|
use App\Services\Invoice\UpdateReminder;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -244,6 +246,13 @@ class InvoiceService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setReminder($settings = null)
|
||||||
|
{
|
||||||
|
$this->invoice = (new UpdateReminder($this->invoice, $settings))->run();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setStatus($status)
|
public function setStatus($status)
|
||||||
{
|
{
|
||||||
$this->invoice->status_id = $status;
|
$this->invoice->status_id = $status;
|
||||||
@ -301,6 +310,10 @@ class InvoiceService
|
|||||||
//UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.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->numberFormatter().'.pdf');
|
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
|
if(Ninja::isHosted()) {
|
||||||
|
Storage::disk('public')->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,8 +39,6 @@ class MarkSent extends AbstractService
|
|||||||
|
|
||||||
$this->invoice->markInvitationsSent();
|
$this->invoice->markInvitationsSent();
|
||||||
|
|
||||||
$this->invoice->setReminder();
|
|
||||||
|
|
||||||
$this->invoice
|
$this->invoice
|
||||||
->service()
|
->service()
|
||||||
->setStatus(Invoice::STATUS_SENT)
|
->setStatus(Invoice::STATUS_SENT)
|
||||||
@ -48,6 +46,7 @@ class MarkSent extends AbstractService
|
|||||||
->setDueDate()
|
->setDueDate()
|
||||||
->updateBalance($this->invoice->amount)
|
->updateBalance($this->invoice->amount)
|
||||||
->deletePdf()
|
->deletePdf()
|
||||||
|
->setReminder()
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
$this->client->service()->updateBalance($this->invoice->balance)->save();
|
$this->client->service()->updateBalance($this->invoice->balance)->save();
|
||||||
|
131
app/Services/Invoice/UpdateReminder.php
Normal file
131
app/Services/Invoice/UpdateReminder.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?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\Invoice;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Services\AbstractService;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class UpdateReminder extends AbstractService
|
||||||
|
{
|
||||||
|
public $invoice;
|
||||||
|
|
||||||
|
public $settings;
|
||||||
|
|
||||||
|
public function __construct(Invoice $invoice, $settings = null)
|
||||||
|
{
|
||||||
|
$this->invoice = $invoice;
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (! $this->settings) {
|
||||||
|
$this->settings = $this->invoice->client->getMergedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->invoice->isPayable()) {
|
||||||
|
$this->invoice->next_send_date = null;
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
return $this->invoice; //exit early
|
||||||
|
}
|
||||||
|
|
||||||
|
$date_collection = collect();
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder1_sent) &&
|
||||||
|
$this->settings->schedule_reminder1 == 'after_invoice_date' &&
|
||||||
|
$this->settings->num_days_reminder1 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->date)->addDays($this->settings->num_days_reminder1);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder1_sent) &&
|
||||||
|
$this->settings->schedule_reminder1 == 'before_due_date' &&
|
||||||
|
$this->settings->num_days_reminder1 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->due_date)->subDays($this->settings->num_days_reminder1);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder1_sent) &&
|
||||||
|
$this->settings->schedule_reminder1 == 'after_due_date' &&
|
||||||
|
$this->settings->num_days_reminder1 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->due_date)->addDays($this->settings->num_days_reminder1);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder2_sent) &&
|
||||||
|
$this->settings->schedule_reminder2 == 'after_invoice_date' &&
|
||||||
|
$this->settings->num_days_reminder2 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->date)->addDays($this->settings->num_days_reminder2);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder2_sent) &&
|
||||||
|
$this->settings->schedule_reminder2 == 'before_due_date' &&
|
||||||
|
$this->settings->num_days_reminder2 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->due_date)->subDays($this->settings->num_days_reminder2);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder2_sent) &&
|
||||||
|
$this->settings->schedule_reminder2 == 'after_due_date' &&
|
||||||
|
$this->settings->num_days_reminder2 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->due_date)->addDays($this->settings->num_days_reminder2);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder3_sent) &&
|
||||||
|
$this->settings->schedule_reminder3 == 'after_invoice_date' &&
|
||||||
|
$this->settings->num_days_reminder3 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->date)->addDays($this->settings->num_days_reminder3);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder3_sent) &&
|
||||||
|
$this->settings->schedule_reminder3 == 'before_due_date' &&
|
||||||
|
$this->settings->num_days_reminder3 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->due_date)->subDays($this->settings->num_days_reminder3);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($this->invoice->reminder3_sent) &&
|
||||||
|
$this->settings->schedule_reminder3 == 'after_due_date' &&
|
||||||
|
$this->settings->num_days_reminder3 > 0) {
|
||||||
|
$reminder_date = Carbon::parse($this->invoice->due_date)->addDays($this->settings->num_days_reminder3);
|
||||||
|
|
||||||
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
|
$date_collection->push($reminder_date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->invoice->next_send_date = $date_collection->sort()->first();
|
||||||
|
|
||||||
|
return $this->invoice;
|
||||||
|
}
|
||||||
|
}
|
@ -41,7 +41,7 @@ class GetInvoicePdf extends AbstractService
|
|||||||
|
|
||||||
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
||||||
|
|
||||||
$disk = config('filesystems.default');
|
$disk = 'public';
|
||||||
|
|
||||||
$file = Storage::disk($disk)->exists($file_path);
|
$file = Storage::disk($disk)->exists($file_path);
|
||||||
|
|
||||||
@ -49,12 +49,6 @@ class GetInvoicePdf extends AbstractService
|
|||||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Copy from remote disk to local when using cloud file storage. */
|
|
||||||
if(config('filesystems.default') == 's3')
|
|
||||||
return TempFile::path(Storage::disk($disk)->url($file_path));
|
|
||||||
|
|
||||||
// return Storage::disk($disk)->url($file_path);
|
|
||||||
return Storage::disk($disk)->path($file_path);
|
return Storage::disk($disk)->path($file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ class CreditTransformer extends EntityTransformer
|
|||||||
'po_number' => $credit->po_number ?: '',
|
'po_number' => $credit->po_number ?: '',
|
||||||
'date' => $credit->date ?: '',
|
'date' => $credit->date ?: '',
|
||||||
'last_sent_date' => $credit->last_sent_date ?: '',
|
'last_sent_date' => $credit->last_sent_date ?: '',
|
||||||
'next_send_date' => $credit->date ?: '',
|
'next_send_date' => $credit->next_send_date ?: '',
|
||||||
'reminder1_sent' => $credit->reminder1_sent ?: '',
|
'reminder1_sent' => $credit->reminder1_sent ?: '',
|
||||||
'reminder2_sent' => $credit->reminder2_sent ?: '',
|
'reminder2_sent' => $credit->reminder2_sent ?: '',
|
||||||
'reminder3_sent' => $credit->reminder3_sent ?: '',
|
'reminder3_sent' => $credit->reminder3_sent ?: '',
|
||||||
|
@ -114,7 +114,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'po_number' => $invoice->po_number ?: '',
|
'po_number' => $invoice->po_number ?: '',
|
||||||
'date' => $invoice->date ?: '',
|
'date' => $invoice->date ?: '',
|
||||||
'last_sent_date' => $invoice->last_sent_date ?: '',
|
'last_sent_date' => $invoice->last_sent_date ?: '',
|
||||||
'next_send_date' => $invoice->date ?: '',
|
'next_send_date' => $invoice->next_send_date ?: '',
|
||||||
'due_date' => $invoice->due_date ?: '',
|
'due_date' => $invoice->due_date ?: '',
|
||||||
'terms' => $invoice->terms ?: '',
|
'terms' => $invoice->terms ?: '',
|
||||||
'public_notes' => $invoice->public_notes ?: '',
|
'public_notes' => $invoice->public_notes ?: '',
|
||||||
|
@ -108,7 +108,7 @@ class QuoteTransformer extends EntityTransformer
|
|||||||
'po_number' => $quote->po_number ?: '',
|
'po_number' => $quote->po_number ?: '',
|
||||||
'date' => $quote->date ?: '',
|
'date' => $quote->date ?: '',
|
||||||
'last_sent_date' => $quote->last_sent_date ?: '',
|
'last_sent_date' => $quote->last_sent_date ?: '',
|
||||||
'next_send_date' => $quote->date ?: '',
|
'next_send_date' => $quote->next_send_date ?: '',
|
||||||
'reminder1_sent' => $quote->reminder1_sent ?: '',
|
'reminder1_sent' => $quote->reminder1_sent ?: '',
|
||||||
'reminder2_sent' => $quote->reminder2_sent ?: '',
|
'reminder2_sent' => $quote->reminder2_sent ?: '',
|
||||||
'reminder3_sent' => $quote->reminder3_sent ?: '',
|
'reminder3_sent' => $quote->reminder3_sent ?: '',
|
||||||
|
@ -118,6 +118,8 @@ class Phantom
|
|||||||
|
|
||||||
$finfo = new \finfo(FILEINFO_MIME);
|
$finfo = new \finfo(FILEINFO_MIME);
|
||||||
|
|
||||||
|
nlog($pdf);
|
||||||
|
|
||||||
if($finfo->buffer($pdf) != 'application/pdf; charset=binary')
|
if($finfo->buffer($pdf) != 'application/pdf; charset=binary')
|
||||||
{
|
{
|
||||||
SystemLogger::dispatch(
|
SystemLogger::dispatch(
|
||||||
|
@ -25,7 +25,7 @@ use Illuminate\Support\Facades\Queue;
|
|||||||
class SystemHealth
|
class SystemHealth
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
'mysqli',
|
// 'mysqli',
|
||||||
'gd',
|
'gd',
|
||||||
'curl',
|
'curl',
|
||||||
'zip',
|
'zip',
|
||||||
@ -34,7 +34,7 @@ class SystemHealth
|
|||||||
'mbstring',
|
'mbstring',
|
||||||
'xml',
|
'xml',
|
||||||
'bcmath',
|
'bcmath',
|
||||||
'mysqlnd',
|
// 'mysqlnd',
|
||||||
//'intl', //todo double check whether we need this for email dns validation
|
//'intl', //todo double check whether we need this for email dns validation
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -19,97 +19,6 @@ use Illuminate\Support\Carbon;
|
|||||||
*/
|
*/
|
||||||
trait MakesReminders
|
trait MakesReminders
|
||||||
{
|
{
|
||||||
public function setReminder($settings = null)
|
|
||||||
{
|
|
||||||
if (! $settings) {
|
|
||||||
$settings = $this->client->getMergedSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $this->isPayable()) {
|
|
||||||
$this->next_send_date = null;
|
|
||||||
$this->save();
|
|
||||||
|
|
||||||
return; //exit early
|
|
||||||
}
|
|
||||||
|
|
||||||
$date_collection = collect();
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder1 == 'after_invoice_date' &&
|
|
||||||
$settings->num_days_reminder1 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->date)->addDays($settings->num_days_reminder1);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder1 == 'before_due_date' &&
|
|
||||||
$settings->num_days_reminder1 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->due_date)->subDays($settings->num_days_reminder1);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder1 == 'after_due_date' &&
|
|
||||||
$settings->num_days_reminder1 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->due_date)->addDays($settings->num_days_reminder1);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder2 == 'after_invoice_date' &&
|
|
||||||
$settings->num_days_reminder2 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->date)->addDays($settings->num_days_reminder2);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder2 == 'before_due_date' &&
|
|
||||||
$settings->num_days_reminder2 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->due_date)->subDays($settings->num_days_reminder2);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder2 == 'after_due_date' &&
|
|
||||||
$settings->num_days_reminder2 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->due_date)->addDays($settings->num_days_reminder2);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder3 == 'after_invoice_date' &&
|
|
||||||
$settings->num_days_reminder3 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->date)->addDays($settings->num_days_reminder3);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder3 == 'before_due_date' &&
|
|
||||||
$settings->num_days_reminder3 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->due_date)->subDays($settings->num_days_reminder3);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($settings->schedule_reminder3 == 'after_due_date' &&
|
|
||||||
$settings->num_days_reminder3 > 0) {
|
|
||||||
$reminder_date = Carbon::parse($this->due_date)->addDays($settings->num_days_reminder3);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->next_send_date)));
|
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->next_send_date = $date_collection->sort()->first();
|
|
||||||
|
|
||||||
$this->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function inReminderWindow($schedule_reminder, $num_days_reminder)
|
public function inReminderWindow($schedule_reminder, $num_days_reminder)
|
||||||
{
|
{
|
||||||
@ -177,10 +86,8 @@ trait MakesReminders
|
|||||||
|
|
||||||
private function addTimeInterval($date, $endless_reminder_frequency_id) :?Carbon
|
private function addTimeInterval($date, $endless_reminder_frequency_id) :?Carbon
|
||||||
{
|
{
|
||||||
if (!$date) {
|
if (!$date)
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch ($endless_reminder_frequency_id) {
|
switch ($endless_reminder_frequency_id) {
|
||||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||||
|
35
app/Utils/Traits/User/LoginCache.php
Normal file
35
app/Utils/Traits/User/LoginCache.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?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\Traits\User;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
trait LoginCache
|
||||||
|
{
|
||||||
|
|
||||||
|
public function setLoginCache($user)
|
||||||
|
{
|
||||||
|
|
||||||
|
$timeout = $user->company()->default_password_timeout;
|
||||||
|
|
||||||
|
if($timeout == 0)
|
||||||
|
$timeout = 30*60*1000*1000;
|
||||||
|
else
|
||||||
|
$timeout = $timeout/1000;
|
||||||
|
|
||||||
|
Cache::put($user->hashed_id.'_'.$user->account_id.'_logged_in', Str::random(64), $timeout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
113
config/livewire.php
Normal file
113
config/livewire.php
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Class Namespace
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the root namespace for Livewire component classes in
|
||||||
|
| your application. This value affects component auto-discovery and
|
||||||
|
| any Livewire file helper commands, like `artisan make:livewire`.
|
||||||
|
|
|
||||||
|
| After changing this item, run: `php artisan livewire:discover`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'class_namespace' => 'App\\Http\\Livewire',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| View Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path for Livewire component views. This affects
|
||||||
|
| file manipulation helper commands like `artisan make:livewire`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'view_path' => resource_path('views/livewire'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Layout
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| The default layout view that will be used when rendering a component via
|
||||||
|
| Route::get('/some-endpoint', SomeComponent::class);. In this case the
|
||||||
|
| the view returned by SomeComponent will be wrapped in "layouts.app"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'layout' => 'layouts.app',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Assets URL
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path to Livewire JavaScript assets, for cases where
|
||||||
|
| your app's domain root is not the correct path. By default, Livewire
|
||||||
|
| will load its JavaScript assets from the app's "relative root".
|
||||||
|
|
|
||||||
|
| Examples: "/assets", "myurl.com/app".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'asset_url' => env('ASSETS_URL', config('app.url')),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Endpoint Middleware Group
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the middleware group that will be applied to the main
|
||||||
|
| Livewire "message" endpoint (the endpoint that gets hit everytime
|
||||||
|
| a Livewire component updates). It is set to "web" by default.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware_group' => 'web',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Temporary File Uploads Endpoint Configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Livewire handles file uploads by storing uploads in a temporary directory
|
||||||
|
| before the file is validated and stored permanently. All file uploads
|
||||||
|
| are directed to a global endpoint for temporary storage. The config
|
||||||
|
| items below are used for customizing the way the endpoint works.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'temporary_file_upload' => [
|
||||||
|
'disk' => null, // Example: 'local', 's3' Default: 'default'
|
||||||
|
'rules' => null, // Example: ['file', 'mimes:png,jpg'] Default: ['required', 'file', 'max:12288'] (12MB)
|
||||||
|
'directory' => null, // Example: 'tmp' Default 'livewire-tmp'
|
||||||
|
'middleware' => null, // Example: 'throttle:5,1' Default: 'throttle:60,1'
|
||||||
|
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs.
|
||||||
|
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
|
||||||
|
'mov', 'avi', 'wmv', 'mp3', 'm4a',
|
||||||
|
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
|
||||||
|
],
|
||||||
|
'max_upload_time' => 5, // Max duration (in minutes) before an upload gets invalidated.
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Manifest File Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path to the Livewire manifest file.
|
||||||
|
| The default should work for most cases (which is
|
||||||
|
| "<app_root>/bootstrap/cache/livewire-components.php)", but for specific
|
||||||
|
| cases like when hosting on Laravel Vapor, it could be set to a different value.
|
||||||
|
|
|
||||||
|
| Example: for Laravel Vapor, it would be "/tmp/storage/bootstrap/cache/livewire-components.php".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'manifest_path' => null,
|
||||||
|
|
||||||
|
];
|
@ -14,8 +14,8 @@ 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', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.1.65',
|
'app_version' => '5.1.67',
|
||||||
'app_tag' => '5.1.65-release',
|
'app_tag' => '5.1.67-release',
|
||||||
'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', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
38
database/factories/TaxRateFactory.php
Normal file
38
database/factories/TaxRateFactory.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?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 Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\TaxRate;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class TaxRateFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the factory's corresponding model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model = TaxRate::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->faker->word(3),
|
||||||
|
'rate' => rand(1,20)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Document;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class MakeDocumentsAssignedUserNullable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('documents', function (Blueprint $table){
|
||||||
|
$table->unsignedInteger('assigned_user_id')->nullable()->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
Document::where('assigned_user_id', 0)->update(['assigned_user_id' => null]);
|
||||||
|
|
||||||
|
if(config('ninja.db.multi_db_enabled')){
|
||||||
|
foreach (MultiDB::$dbs as $db) {
|
||||||
|
Document::on($db)->where('assigned_user_id', 0)->update(['assigned_user_id' => null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Document::where('assigned_user_id', 0)->update(['assigned_user_id' => null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
1
public/css/admin.css
vendored
Normal file
1
public/css/admin.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
4
public/flutter_service_worker.js
vendored
4
public/flutter_service_worker.js
vendored
@ -3,9 +3,9 @@ const MANIFEST = 'flutter-app-manifest';
|
|||||||
const TEMP = 'flutter-temp-cache';
|
const TEMP = 'flutter-temp-cache';
|
||||||
const CACHE_NAME = 'flutter-app-cache';
|
const CACHE_NAME = 'flutter-app-cache';
|
||||||
const RESOURCES = {
|
const RESOURCES = {
|
||||||
"version.json": "ea1781094b87723b953889a712b1feba",
|
"version.json": "9fe5b22a16f39b766c8fdc35a24b3efa",
|
||||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||||
"main.dart.js": "f46c892fb39ce4151ac09e5794745701",
|
"main.dart.js": "55df523d1b81bba3d88e7b77511c7a87",
|
||||||
"/": "23224b5e03519aaa87594403d54412cf",
|
"/": "23224b5e03519aaa87594403d54412cf",
|
||||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||||
|
1
public/js/admin.js
vendored
Normal file
1
public/js/admin.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(()=>{var r,e={847:()=>{},113:()=>{}},o={};function n(r){var t=o[r];if(void 0!==t)return t.exports;var a=o[r]={exports:{}};return e[r](a,a.exports,n),a.exports}n.m=e,r=[],n.O=(e,o,t,a)=>{if(!o){var v=1/0;for(p=0;p<r.length;p++){for(var[o,t,a]=r[p],l=!0,i=0;i<o.length;i++)(!1&a||v>=a)&&Object.keys(n.O).every((r=>n.O[r](o[i])))?o.splice(i--,1):(l=!1,a<v&&(v=a));l&&(r.splice(p--,1),e=t())}return e}a=a||0;for(var p=r.length;p>0&&r[p-1][2]>a;p--)r[p]=r[p-1];r[p]=[o,t,a]},n.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),(()=>{var r={467:0,703:0};n.O.j=e=>0===r[e];var e=(e,o)=>{var t,a,[v,l,i]=o,p=0;for(t in l)n.o(l,t)&&(n.m[t]=l[t]);if(i)var f=i(n);for(e&&e(o);p<v.length;p++)a=v[p],n.o(r,a)&&r[a]&&r[a][0](),r[v[p]]=0;return n.O(f)},o=self.webpackChunk=self.webpackChunk||[];o.forEach(e.bind(null,0)),o.push=e.bind(null,o.push.bind(o))})(),n.O(void 0,[703],(()=>n(847)));var t=n.O(void 0,[703],(()=>n(113)));t=n.O(t)})();
|
214752
public/main.dart.js
vendored
214752
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
216566
public/main.foss.dart.js
vendored
216566
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
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=987a5ab343fc0d5c6cba",
|
"/css/app.css": "/css/app.css?id=14a824656f32eec8c2b1",
|
||||||
"/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",
|
||||||
|
4
public/vendor/livewire/livewire.js
vendored
4
public/vendor/livewire/livewire.js
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/livewire.js.map
vendored
2
public/vendor/livewire/livewire.js.map
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user