mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-16 16:13:20 +01:00
5.1.68
This commit is contained in:
commit
7fb5f8abe2
@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased (daily channel)](https://github.com/invoiceninja/invoiceninja/tree/v5-develop)
|
||||
- Add Cache-control: no-cache to prevent overaggressive caching of assets
|
||||
- Improved labelling in the settings (client portal)
|
||||
- Client portal: Multiple accounts access improvements (#5703)
|
||||
- Client portal: "Credits" updates (#5734)
|
||||
- Client portal: Make sidebar white color, in order to make logo displaying more simple. (#5753)
|
||||
|
||||
## [v5.1.56-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.56-release)
|
||||
## Fixed:
|
||||
|
@ -1 +1 @@
|
||||
5.1.62
|
||||
5.1.68
|
||||
|
@ -65,7 +65,7 @@ class CheckData extends Command
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:check-data';
|
||||
protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -105,6 +105,9 @@ class CreateAccount extends Command
|
||||
'password' => Hash::make($password),
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
'email_verified_at' => now(),
|
||||
'first_name' => 'New',
|
||||
'last_name' => 'User',
|
||||
'phone' => '',
|
||||
]);
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
|
@ -34,6 +34,7 @@ use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
@ -105,8 +106,18 @@ class CreateSingleAccount extends Command
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
'default_password_timeout' => 30*60000,
|
||||
'portal_mode' => 'domain',
|
||||
'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->save();
|
||||
|
||||
@ -144,6 +155,29 @@ class CreateSingleAccount extends Command
|
||||
'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');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
|
@ -128,7 +128,7 @@ class DemoMode extends Command
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$this->count = 50;
|
||||
$this->count = 25;
|
||||
|
||||
$this->info('Creating Small Account and Company');
|
||||
|
||||
@ -486,7 +486,7 @@ class DemoMode extends Command
|
||||
if (rand(0, 1)) {
|
||||
$invoice->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$invoice->number = $this->getNextRecurringInvoiceNumber($client);
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,12 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Ninja\SendReminders;
|
||||
use App\Jobs\Util\WebHookHandler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Webhook;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
|
||||
class SendRemindersCron extends Command
|
||||
{
|
||||
@ -54,8 +54,8 @@ class SendRemindersCron extends Command
|
||||
{
|
||||
SendReminders::dispatchNow();
|
||||
|
||||
$this->webHookOverdueInvoices();
|
||||
$this->webHookExpiredQuotes();
|
||||
$this->webHookOverdueInvoices();
|
||||
$this->webHookExpiredQuotes();
|
||||
}
|
||||
|
||||
private function webHookOverdueInvoices()
|
||||
@ -90,6 +90,7 @@ class SendRemindersCron extends Command
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||
|
||||
});
|
||||
|
||||
$quotes = Quote::where('is_deleted', 0)
|
||||
|
@ -11,8 +11,9 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Cron\AutoBillCron;
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\CompanySizeCheck;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
@ -46,7 +47,7 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$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();
|
||||
|
||||
@ -58,6 +59,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
||||
|
||||
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
||||
|
||||
/* Run hosted specific jobs */
|
||||
@ -65,6 +68,7 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new AdjustEmailQuota)->daily()->withoutOverlapping();
|
||||
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->daily()->withoutOverlapping();
|
||||
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ class CompanySettings extends BaseSettings
|
||||
public $inclusive_taxes = false; //@implemented
|
||||
public $quote_footer = ''; //@implmented
|
||||
|
||||
public $translations; //@TODO not used anywhere
|
||||
public $translations;
|
||||
|
||||
public $counter_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
|
||||
public $quote_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
|
||||
@ -202,7 +202,7 @@ class CompanySettings extends BaseSettings
|
||||
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 $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_amount2 = 0; //@implemented
|
||||
@ -245,8 +245,8 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $hide_paid_to_date = false; //@TODO where?
|
||||
public $embed_documents = false; //@TODO where?
|
||||
public $all_pages_header = false; //@implemented
|
||||
public $all_pages_footer = false; //@implemented
|
||||
public $all_pages_header = false; //@deprecated 31-05-2021
|
||||
public $all_pages_footer = false; //@deprecated 31-05-2021
|
||||
public $pdf_variables = ''; //@implemented
|
||||
|
||||
public $portal_custom_head = ''; //@TODO @BEN
|
||||
@ -667,8 +667,9 @@ class CompanySettings extends BaseSettings
|
||||
'$custom_surcharge4',
|
||||
'$total_taxes',
|
||||
'$line_taxes',
|
||||
'$paid_to_date',
|
||||
'$total',
|
||||
'$paid_to_date',
|
||||
'$outstanding',
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace App\Exceptions;
|
||||
use App\Exceptions\FilePermissionsFailure;
|
||||
use App\Exceptions\InternalPDFFailure;
|
||||
use App\Exceptions\PhantomPDFFailure;
|
||||
use App\Utils\Ninja;
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
@ -75,7 +76,28 @@ class Handler extends ExceptionHandler
|
||||
return;
|
||||
}
|
||||
|
||||
if (app()->bound('sentry') && $this->shouldReport($exception)) {
|
||||
if(Ninja::isHosted()){
|
||||
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
|
||||
if(auth()->guard('contact') && auth()->guard('contact')->user())
|
||||
$key = auth()->guard('contact')->user()->company->account->key;
|
||||
elseif (auth()->guard('user') && auth()->guard('user')->user())
|
||||
$key = auth()->user()->account->key;
|
||||
else
|
||||
$key = 'Anonymous';
|
||||
|
||||
$scope->setUser([
|
||||
'id' => 'Hosted_User',
|
||||
'email' => 'hosted@invoiceninja.com',
|
||||
'name' => $key,
|
||||
]);
|
||||
});
|
||||
|
||||
app('sentry')->captureException($exception);
|
||||
|
||||
}
|
||||
elseif (app()->bound('sentry') && $this->shouldReport($exception)) {
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
if (auth()->guard('contact') && auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) {
|
||||
$scope->setUser([
|
||||
|
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
|
||||
{
|
||||
// ..
|
||||
}
|
10
app/Exceptions/NonExistingBackupFile.php
Normal file
10
app/Exceptions/NonExistingBackupFile.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class NonExistingBackupFile extends Exception
|
||||
{
|
||||
// ..
|
||||
}
|
@ -33,9 +33,6 @@ class ClientFactory
|
||||
$client->client_hash = Str::random(40);
|
||||
$client->settings = ClientSettings::defaults();
|
||||
|
||||
// $client_contact = ClientContactFactory::create($company_id, $user_id);
|
||||
// $client->contacts->add($client_contact);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,9 @@
|
||||
namespace App\Factory;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class CompanyFactory
|
||||
@ -33,7 +35,12 @@ class CompanyFactory
|
||||
$company->db = config('database.default');
|
||||
//$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'];
|
||||
$company->custom_fields = (object) [];
|
||||
$company->subdomain = '';
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$company->subdomain = MultiDB::randomSubdomainGenerator();
|
||||
else
|
||||
$company->subdomain = '';
|
||||
|
||||
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
|
||||
$company->default_password_timeout = 1800000;
|
||||
|
||||
|
@ -21,6 +21,8 @@ class CompanyGatewayFactory
|
||||
$company_gateway = new CompanyGateway;
|
||||
$company_gateway->company_id = $company_id;
|
||||
$company_gateway->user_id = $user_id;
|
||||
$company_gateway->require_billing_address = false;
|
||||
$company_gateway->require_shipping_address = false;
|
||||
// $company_gateway->fees_and_limits = new FeesAndLimits;
|
||||
|
||||
return $company_gateway;
|
||||
|
@ -37,10 +37,10 @@ class CreditFactory
|
||||
$credit->tax_rate1 = 0;
|
||||
$credit->tax_name2 = '';
|
||||
$credit->tax_rate2 = 0;
|
||||
$credit->custom_value1 = 0;
|
||||
$credit->custom_value2 = 0;
|
||||
$credit->custom_value3 = 0;
|
||||
$credit->custom_value4 = 0;
|
||||
$credit->custom_value1 = '';
|
||||
$credit->custom_value2 = '';
|
||||
$credit->custom_value3 = '';
|
||||
$credit->custom_value4 = '';
|
||||
$credit->amount = 0;
|
||||
$credit->balance = 0;
|
||||
$credit->partial = 0;
|
||||
|
@ -38,10 +38,10 @@ class InvoiceFactory
|
||||
$invoice->tax_rate2 = 0;
|
||||
$invoice->tax_name3 = '';
|
||||
$invoice->tax_rate3 = 0;
|
||||
$invoice->custom_value1 = 0;
|
||||
$invoice->custom_value2 = 0;
|
||||
$invoice->custom_value3 = 0;
|
||||
$invoice->custom_value4 = 0;
|
||||
$invoice->custom_value1 = '';
|
||||
$invoice->custom_value2 = '';
|
||||
$invoice->custom_value3 = '';
|
||||
$invoice->custom_value4 = '';
|
||||
$invoice->amount = 0;
|
||||
$invoice->balance = 0;
|
||||
$invoice->paid_to_date = 0;
|
||||
|
@ -36,10 +36,10 @@ class RecurringInvoiceFactory
|
||||
$invoice->tax_rate1 = 0;
|
||||
$invoice->tax_name2 = '';
|
||||
$invoice->tax_rate2 = 0;
|
||||
$invoice->custom_value1 = 0;
|
||||
$invoice->custom_value2 = 0;
|
||||
$invoice->custom_value3 = 0;
|
||||
$invoice->custom_value4 = 0;
|
||||
$invoice->custom_value1 = '';
|
||||
$invoice->custom_value2 = '';
|
||||
$invoice->custom_value3 = '';
|
||||
$invoice->custom_value4 = '';
|
||||
$invoice->amount = 0;
|
||||
$invoice->balance = 0;
|
||||
$invoice->partial = 0;
|
||||
|
@ -35,10 +35,10 @@ class RecurringQuoteFactory
|
||||
$quote->tax_rate1 = 0;
|
||||
$quote->tax_name2 = '';
|
||||
$quote->tax_rate2 = 0;
|
||||
$quote->custom_value1 = 0;
|
||||
$quote->custom_value2 = 0;
|
||||
$quote->custom_value3 = 0;
|
||||
$quote->custom_value4 = 0;
|
||||
$quote->custom_value1 = '';
|
||||
$quote->custom_value2 = '';
|
||||
$quote->custom_value3 = '';
|
||||
$quote->custom_value4 = '';
|
||||
$quote->amount = 0;
|
||||
$quote->balance = 0;
|
||||
$quote->partial = 0;
|
||||
|
@ -169,4 +169,27 @@ abstract class QueryFilters
|
||||
|
||||
return $this->builder->where('created_at', '>=', $created_at);
|
||||
}
|
||||
|
||||
public function is_deleted($value)
|
||||
{
|
||||
|
||||
return $this->builder->where('is_deleted', $value);
|
||||
|
||||
}
|
||||
|
||||
public function filter_deleted_clients($value)
|
||||
{
|
||||
|
||||
if($value == 'true'){
|
||||
|
||||
return $this->builder->whereHas('client', function (Builder $query) {
|
||||
|
||||
$query->where('is_deleted', 0);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ function isActive($page, bool $boolean = false)
|
||||
}
|
||||
|
||||
if ($page == $current_page) {
|
||||
return 'bg-primary-darken';
|
||||
return 'bg-gray-200';
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -142,7 +142,7 @@ class AccountController extends BaseController
|
||||
*/
|
||||
public function store(CreateAccountRequest $request)
|
||||
{
|
||||
$account = CreateAccount::dispatchNow($request->all());
|
||||
$account = CreateAccount::dispatchNow($request->all(), $request->getClientIp());
|
||||
|
||||
if (! ($account instanceof Account)) {
|
||||
return $account;
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
@ -50,11 +51,15 @@ class ContactForgotPasswordController extends Controller
|
||||
*
|
||||
* @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', [
|
||||
'title' => 'Client Password Reset',
|
||||
'passwordEmailRoute' => 'client.password.email',
|
||||
'account' => $account
|
||||
]);
|
||||
}
|
||||
|
||||
@ -72,7 +77,7 @@ class ContactForgotPasswordController extends Controller
|
||||
{
|
||||
//MultiDB::userFindAndSetDb($request->input('email'));
|
||||
|
||||
$user = MultiDB::hasContact(['email' => $request->input('email')]);
|
||||
$user = MultiDB::hasContact($request->input('email'));
|
||||
|
||||
$this->validateEmail($request);
|
||||
|
||||
@ -84,6 +89,10 @@ class ContactForgotPasswordController extends Controller
|
||||
);
|
||||
|
||||
if ($request->ajax()) {
|
||||
|
||||
if($response == Password::RESET_THROTTLED)
|
||||
return response()->json(['message' => ctrans('passwords.throttled'), 'status' => false], 429);
|
||||
|
||||
return $response == Password::RESET_LINK_SENT
|
||||
? response()->json(['message' => 'Reset link sent to your email.', 'status' => true], 201)
|
||||
: response()->json(['message' => 'Email not found', 'status' => false], 401);
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Events\Contact\ContactLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Account;
|
||||
use App\Models\ClientContact;
|
||||
use App\Utils\Ninja;
|
||||
use Auth;
|
||||
@ -31,9 +32,13 @@ class ContactLoginController extends Controller
|
||||
$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)
|
||||
|
@ -24,7 +24,7 @@ class ContactRegisterController extends Controller
|
||||
|
||||
$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)
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
@ -62,8 +63,11 @@ class ContactResetPasswordController extends Controller
|
||||
*/
|
||||
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(
|
||||
['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\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
@ -104,8 +105,7 @@ class ForgotPasswordController extends Controller
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
//MultiDB::userFindAndSetDb($request->input('email'));
|
||||
|
||||
MultiDB::userFindAndSetDb($request->input('email'));
|
||||
$user = MultiDB::hasUser(['email' => $request->input('email')]);
|
||||
|
||||
$this->validateEmail($request);
|
||||
@ -115,9 +115,13 @@ class ForgotPasswordController extends Controller
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
$response = $this->broker()->sendResetLink(
|
||||
$this->credentials($request)
|
||||
);
|
||||
);
|
||||
|
||||
if ($request->ajax()) {
|
||||
|
||||
if($response == Password::RESET_THROTTLED)
|
||||
return response()->json(['message' => ctrans('passwords.throttled'), 'status' => false], 429);
|
||||
|
||||
return $response == Password::RESET_LINK_SENT
|
||||
? response()->json(['message' => 'Reset link sent to your email.', 'status' => true], 201)
|
||||
: response()->json(['message' => 'Email not found', 'status' => false], 401);
|
||||
|
@ -23,6 +23,7 @@ use App\Libraries\MultiDB;
|
||||
use App\Libraries\OAuth\OAuth;
|
||||
use App\Libraries\OAuth\Providers\Google;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\SystemLog;
|
||||
@ -30,6 +31,7 @@ use App\Models\User;
|
||||
use App\Transformers\CompanyUserTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use App\Utils\Traits\User\LoginCache;
|
||||
use Google_Client;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
@ -54,6 +56,7 @@ class LoginController extends BaseController
|
||||
|
||||
use AuthenticatesUsers;
|
||||
use UserSessionAttributes;
|
||||
use LoginCache;
|
||||
|
||||
protected $entity_type = CompanyUser::class;
|
||||
|
||||
@ -177,8 +180,7 @@ class LoginController extends BaseController
|
||||
|
||||
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'))
|
||||
{
|
||||
$google2fa = new Google2FA();
|
||||
@ -202,14 +204,7 @@ class LoginController extends BaseController
|
||||
|
||||
$user->setCompany($user->account->default_company);
|
||||
|
||||
$timeout = $user->company()->default_password_timeout;
|
||||
|
||||
if($timeout == 0)
|
||||
$timeout = 30*60*1000*1000;
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
Cache::put($user->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
$this->setLoginCache($user);
|
||||
|
||||
$cu = CompanyUser::query()
|
||||
->where('user_id', auth()->user()->id);
|
||||
@ -227,7 +222,7 @@ class LoginController extends BaseController
|
||||
});
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
// return $this->listResponse($cu);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
@ -236,11 +231,12 @@ class LoginController extends BaseController
|
||||
->batch();
|
||||
|
||||
SystemLogger::dispatch(
|
||||
request()->getClientIp(),
|
||||
json_encode(['ip' => request()->getClientIp()]),
|
||||
SystemLog::CATEGORY_SECURITY,
|
||||
SystemLog::EVENT_USER,
|
||||
SystemLog::TYPE_LOGIN_FAILURE,
|
||||
Client::first(),
|
||||
null,
|
||||
Company::first(),
|
||||
);
|
||||
|
||||
$this->incrementLoginAttempts($request);
|
||||
@ -349,6 +345,7 @@ class LoginController extends BaseController
|
||||
|
||||
if (is_array($user)) {
|
||||
|
||||
//
|
||||
$query = [
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id'=> 'google',
|
||||
@ -359,15 +356,7 @@ class LoginController extends BaseController
|
||||
Auth::login($existing_user, true);
|
||||
$existing_user->setCompany($existing_user->account->default_company);
|
||||
|
||||
$timeout = $existing_user->company()->default_password_timeout;
|
||||
|
||||
if($timeout == 0)
|
||||
$timeout = 30*60*1000*1000;
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
|
||||
Cache::put($existing_user->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
$this->setLoginCache($existing_user);
|
||||
|
||||
$cu = CompanyUser::query()
|
||||
->where('user_id', auth()->user()->id);
|
||||
@ -383,10 +372,68 @@ class LoginController extends BaseController
|
||||
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) {
|
||||
|
||||
//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));
|
||||
|
||||
$new_account = [
|
||||
@ -402,22 +449,14 @@ class LoginController extends BaseController
|
||||
|
||||
MultiDB::setDefaultDatabase();
|
||||
|
||||
$account = CreateAccount::dispatchNow($new_account);
|
||||
$account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
|
||||
|
||||
Auth::login($account->default_company->owner(), true);
|
||||
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
||||
$timeout = auth()->user()->company()->default_password_timeout;
|
||||
|
||||
if($timeout == 0)
|
||||
$timeout = 30*60*1000*1000;
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
$this->setLoginCache(auth()->user());
|
||||
|
||||
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
||||
|
||||
@ -437,4 +476,62 @@ class LoginController extends BaseController
|
||||
->header('X-App-Version', config('ninja.app_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()
|
||||
{
|
||||
return abort(404);
|
||||
abort(404, 'Page not found in client portal.');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,10 +189,7 @@ class BaseController extends Controller
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->getCompany()->is_large)
|
||||
$this->manager->parseIncludes($this->mini_load);
|
||||
else
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
|
||||
$this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY;
|
||||
|
||||
@ -312,10 +309,6 @@ class BaseController extends Controller
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('tax_rates.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.vendors'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
||||
@ -326,15 +319,9 @@ class BaseController extends Controller
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('expense_categories.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
@ -366,13 +353,92 @@ class BaseController extends Controller
|
||||
return $this->response($this->manager->createData($resource)->toArray());
|
||||
}
|
||||
|
||||
protected function miniLoadResponse($query)
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
|
||||
$this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY;
|
||||
|
||||
if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) {
|
||||
$this->manager->setSerializer(new JsonApiSerializer());
|
||||
} else {
|
||||
$this->manager->setSerializer(new ArraySerializer());
|
||||
}
|
||||
|
||||
$transformer = new $this->entity_transformer($this->serializer);
|
||||
$created_at = request()->has('created_at') ? request()->input('created_at') : 0;
|
||||
|
||||
$created_at = date('Y-m-d H:i:s', $created_at);
|
||||
|
||||
$query->with(
|
||||
[
|
||||
'company' => function ($query) use ($created_at, $user) {
|
||||
$query->whereNotNull('created_at')->with('documents');
|
||||
},
|
||||
'company.designs'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('company');
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('designs.user_id', $user->id);
|
||||
},
|
||||
'company.documents'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
},
|
||||
'company.groups' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('group_settings.user_id', $user->id);
|
||||
},
|
||||
'company.payment_terms'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('payment_terms.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('tax_rates.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('activities.user_id', $user->id);
|
||||
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
if ($query instanceof Builder) {
|
||||
$limit = request()->input('per_page', 20);
|
||||
|
||||
$paginator = $query->paginate($limit);
|
||||
$query = $paginator->getCollection();
|
||||
$resource = new Collection($query, $transformer, $this->entity_type);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
} else {
|
||||
$resource = new Collection($query, $transformer, $this->entity_type);
|
||||
}
|
||||
|
||||
return $this->response($this->manager->createData($resource)->toArray());
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected function timeConstrainedResponse($query)
|
||||
{
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->getCompany()->is_large)
|
||||
if ($user->getCompany()->is_large){
|
||||
$this->manager->parseIncludes($this->mini_load);
|
||||
return $this->miniLoadResponse($query);
|
||||
}
|
||||
else
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
|
||||
|
@ -15,6 +15,7 @@ use App\Events\Client\ClientWasCreated;
|
||||
use App\Events\Client\ClientWasUpdated;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Filters\ClientFilters;
|
||||
use App\Http\Requests\Client\AdjustClientLedgerRequest;
|
||||
use App\Http\Requests\Client\CreateClientRequest;
|
||||
use App\Http\Requests\Client\DestroyClientRequest;
|
||||
use App\Http\Requests\Client\EditClientRequest;
|
||||
@ -585,4 +586,64 @@ 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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
//@deprecated - not available
|
||||
public function adjustLedger(AdjustClientLedgerRequest $request, Client $client)
|
||||
{
|
||||
// $adjustment = $request->input('adjustment');
|
||||
// $notes = $request->input('notes');
|
||||
|
||||
// $client->service()->updateBalance
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ class ContactHashLoginController extends Controller
|
||||
*/
|
||||
public function login(string $contact_key)
|
||||
{
|
||||
return redirect('/client/login');
|
||||
return redirect('/client/invoices');
|
||||
}
|
||||
|
||||
public function magicLink(string $magic_link)
|
||||
{
|
||||
return redirect('/client/login');
|
||||
return redirect('/client/invoices');
|
||||
}
|
||||
|
||||
public function errorPage()
|
||||
|
@ -22,8 +22,15 @@ class CreditController extends Controller
|
||||
|
||||
public function show(ShowCreditRequest $request, Credit $credit)
|
||||
{
|
||||
return $this->render('credits.show', [
|
||||
'credit' => $credit,
|
||||
]);
|
||||
set_time_limit(0);
|
||||
|
||||
$data = ['credit' => $credit];
|
||||
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
return render('credits.show-fullscreen', $data);
|
||||
}
|
||||
|
||||
return $this->render('credits.show', $data);
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class DocumentController extends Controller
|
||||
|
||||
$documents->map(function ($document) {
|
||||
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)
|
||||
{
|
||||
if (! in_array($entity_type, $this->entity_types)) {
|
||||
abort(404);
|
||||
abort(404, 'Entity not found');
|
||||
}
|
||||
|
||||
$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)
|
||||
{
|
||||
if (! in_array($entity_type, $this->entity_types)) {
|
||||
abort(404);
|
||||
abort(404, 'Entity not found');
|
||||
}
|
||||
|
||||
$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 */
|
||||
|
||||
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) {
|
||||
|
||||
@ -66,7 +66,7 @@ class InvitationController extends Controller
|
||||
return redirect()->route('client.login');
|
||||
|
||||
} else {
|
||||
auth()->guard('contact')->login($invitation->contact, true);
|
||||
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -164,10 +164,10 @@ class InvoiceController extends Controller
|
||||
|
||||
//if only 1 pdf, output to buffer for download
|
||||
if ($invoices->count() == 1) {
|
||||
return response()->streamDownload(function () use ($invoices) {
|
||||
echo file_get_contents($invoices->first()->pdf_file_path());
|
||||
}, basename($invoices->first()->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($invoices->first()->pdf_file_path()), basename($invoices->first()->pdf_file_path()));
|
||||
|
||||
$file = $invoices->first()->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
|
||||
}
|
||||
|
||||
// enable output of HTTP headers
|
||||
|
@ -290,7 +290,8 @@ class PaymentController extends Controller
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_ERROR,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
auth('contact')->user()->client
|
||||
auth('contact')->user()->client,
|
||||
auth('contact')->user()->client->company
|
||||
);
|
||||
|
||||
throw new PaymentFailed($e->getMessage());
|
||||
|
@ -149,6 +149,6 @@ class PaymentMethodController extends Controller
|
||||
return $gateway = auth()->user()->client->getBankTransferGateway();
|
||||
}
|
||||
|
||||
return abort(404);
|
||||
abort(404, 'Gateway not found.');
|
||||
}
|
||||
}
|
||||
|
@ -76,10 +76,9 @@ class QuoteController extends Controller
|
||||
}
|
||||
|
||||
if ($quotes->count() == 1) {
|
||||
return response()->streamDownload(function () use ($quotes) {
|
||||
echo file_get_contents($quotes->first()->pdf_file_path());
|
||||
}, basename($quotes->first()->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($invoices->first()->pdf_file_path()), basename($quotes->first()->pdf_file_path()));
|
||||
|
||||
$file = $quotes->first()->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
// enable output of HTTP headers
|
||||
|
34
app/Http/Controllers/ClientPortal/TaskController.php
Normal file
34
app/Http/Controllers/ClientPortal/TaskController.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Tasks\ShowTasksRequest;
|
||||
|
||||
class TaskController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the tasks in the client portal.
|
||||
*
|
||||
* @param ShowTasksRequest $request
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function index(ShowTasksRequest $request)
|
||||
{
|
||||
\Carbon\Carbon::setLocale(
|
||||
auth('contact')->user()->preferredLocale()
|
||||
);
|
||||
|
||||
return render('tasks.index');
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ use App\Models\CompanyUser;
|
||||
use App\Models\User;
|
||||
use App\Transformers\CompanyUserTransformer;
|
||||
use App\Transformers\UserTransformer;
|
||||
use App\Utils\Traits\User\LoginCache;
|
||||
use Google_Client;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@ -24,6 +25,7 @@ use Illuminate\Support\Str;
|
||||
|
||||
class ConnectedAccountController extends BaseController
|
||||
{
|
||||
use LoginCache;
|
||||
|
||||
protected $entity_type = User::class;
|
||||
|
||||
@ -113,9 +115,8 @@ class ConnectedAccountController extends BaseController
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
||||
$timeout = auth()->user()->company()->default_password_timeout;
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
|
||||
$this->setLoginCache(auth()->user());
|
||||
|
||||
return $this->itemResponse(auth()->user());
|
||||
|
||||
}
|
||||
@ -160,6 +161,9 @@ class ConnectedAccountController extends BaseController
|
||||
'email_verified_at' =>now()
|
||||
];
|
||||
|
||||
if(auth()->user()->email != $google->harvestEmail($user))
|
||||
return response()->json(['message' => 'Primary Email differs to OAuth email. Emails must match.'], 400);
|
||||
|
||||
auth()->user()->update($connected_account);
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
@ -93,11 +93,16 @@ class LoginController extends BaseController
|
||||
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()->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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -535,11 +535,9 @@ class CreditController extends BaseController
|
||||
return $this->itemResponse($credit);
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
return response()->streamDownload(function () use ($credit) {
|
||||
echo file_get_contents($credit->pdf_file_path());
|
||||
}, basename($credit->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($credit->pdf_file_path()), basename($credit->pdf_file_path()));
|
||||
case 'download':
|
||||
$file = $credit->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
break;
|
||||
case 'archive':
|
||||
$this->credit_repository->archive($credit);
|
||||
@ -589,7 +587,7 @@ class CreditController extends BaseController
|
||||
|
||||
$file_path = $credit->service()->getCreditPdf($invitation);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
64
app/Http/Controllers/ExportController.php
Normal file
64
app/Http/Controllers/ExportController.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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\Export\StoreExportRequest;
|
||||
use App\Jobs\Company\CompanyExport;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class ExportController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/export",
|
||||
* operationId="getExport",
|
||||
* tags={"export"},
|
||||
* summary="Export data from the system",
|
||||
* description="Export 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(StoreExportRequest $request)
|
||||
{
|
||||
|
||||
CompanyExport::dispatch(auth()->user()->getCompany(), auth()->user());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
}
|
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
|
||||
}
|
||||
}
|
@ -671,10 +671,10 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
return response()->streamDownload(function () use ($invoice) {
|
||||
echo file_get_contents($invoice->pdf_file_path());
|
||||
}, basename($invoice->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($invoice->pdf_file_path()), basename($invoice->pdf_file_path()));
|
||||
|
||||
$file = $invoice->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
break;
|
||||
case 'restore':
|
||||
$this->invoice_repo->restore($invoice);
|
||||
@ -793,9 +793,10 @@ class InvoiceController extends BaseController
|
||||
$contact = $invitation->contact;
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
$file_path = $invoice->service()->getInvoicePdf($contact);
|
||||
$file = $invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -844,16 +845,11 @@ class InvoiceController extends BaseController
|
||||
*/
|
||||
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
|
||||
{
|
||||
$file_path = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
try {
|
||||
$file = public_path("storage/{$file_path}");
|
||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache']);
|
||||
} catch (\Exception $e) {
|
||||
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,6 +107,12 @@ class LicenseController extends BaseController
|
||||
'errors' => new stdClass,
|
||||
];
|
||||
|
||||
$account->plan_term = Account::PLAN_TERM_YEARLY;
|
||||
$account->plan_paid = null;
|
||||
$account->plan_expires = null;
|
||||
$account->plan = Account::PLAN_FREE;
|
||||
$account->save();
|
||||
|
||||
return response()->json($error, 400);
|
||||
} else {
|
||||
$account = auth()->user()->company()->account;
|
||||
|
@ -25,7 +25,6 @@ class OneTimeTokenController extends BaseController
|
||||
{
|
||||
|
||||
private $contexts = [
|
||||
'stripe_connect' => 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_J2Fh2tZfMlaaItUfbUwBBx4JPss8jCz9&scope=read_write'
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
|
@ -21,13 +21,18 @@ class PaymentWebhookController extends Controller
|
||||
public function __invoke(PaymentWebhookRequest $request, string $company_key, string $company_gateway_id)
|
||||
{
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($company_key);
|
||||
// MultiDB::findAndSetDbByCompanyKey($company_key);
|
||||
|
||||
$payment = $request->getPayment();
|
||||
|
||||
if(!$payment)
|
||||
return response()->json(['message' => 'Payment record not found.'], 400);
|
||||
|
||||
$client = is_null($payment) ? $request->getClient() : $payment->client;
|
||||
|
||||
// $contact= $client->primary_contact()->first();
|
||||
// Auth::guard('contact')->login($contact, true);
|
||||
if(!$client)
|
||||
return response()->json(['message' => 'Client record not found.'], 400);
|
||||
|
||||
|
||||
return $request->getCompanyGateway()
|
||||
->driver($client)
|
||||
|
@ -125,7 +125,13 @@ class PostMarkController extends BaseController
|
||||
$this->invitation->email_status = 'delivered';
|
||||
$this->invitation->save();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_DELIVERY, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
|
||||
SystemLogger::dispatch($request->all(),
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_DELIVERY,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
$this->invitation->contact->client,
|
||||
$this->invitation->company
|
||||
);
|
||||
}
|
||||
|
||||
// {
|
||||
@ -167,7 +173,7 @@ class PostMarkController extends BaseController
|
||||
|
||||
LightLogs::create($bounce)->batch();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
}
|
||||
|
||||
// {
|
||||
@ -209,7 +215,7 @@ class PostMarkController extends BaseController
|
||||
|
||||
LightLogs::create($bounce)->batch();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
}
|
||||
|
||||
private function discoverInvitation($message_id)
|
||||
|
@ -99,9 +99,10 @@ class PreviewController extends BaseController
|
||||
|
||||
$entity_obj->load('client');
|
||||
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
$t = app('translator');
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
$t->replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
|
||||
$html = new HtmlEngine($entity_obj->invitations()->first());
|
||||
|
||||
@ -151,7 +152,8 @@ class PreviewController extends BaseController
|
||||
private function blankEntity()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
|
@ -675,10 +675,10 @@ class QuoteController extends BaseController
|
||||
// code...
|
||||
break;
|
||||
case 'download':
|
||||
return response()->streamDownload(function () use ($quote) {
|
||||
echo file_get_contents($quote->pdf_file_path());
|
||||
}, basename($quote->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($quote->pdf_file_path()), basename($quote->pdf_file_path()));
|
||||
|
||||
$file = $quote->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
break;
|
||||
case 'restore':
|
||||
$this->quote_repo->restore($quote);
|
||||
@ -730,7 +730,7 @@ class QuoteController extends BaseController
|
||||
|
||||
$file_path = $quote->service()->getQuotePdf($contact);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -497,7 +497,7 @@ class RecurringInvoiceController extends BaseController
|
||||
|
||||
$file_path = $recurring_invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,7 +163,7 @@ class SetupController extends Controller
|
||||
|
||||
/* Create the first account. */
|
||||
if (Account::count() == 0) {
|
||||
CreateAccount::dispatchNow($request->all());
|
||||
CreateAccount::dispatchNow($request->all(), $request->getClientIp());
|
||||
}
|
||||
|
||||
VersionCheck::dispatchNow();
|
||||
@ -275,7 +275,9 @@ class SetupController extends Controller
|
||||
|
||||
public function update()
|
||||
{
|
||||
|
||||
// if(Ninja::isHosted())
|
||||
// return redirect('/');
|
||||
|
||||
// if( Ninja::isNinja() || !request()->has('secret') || (request()->input('secret') != config('ninja.update_secret')) )
|
||||
if(!request()->has('secret') || (request()->input('secret') != config('ninja.update_secret')) )
|
||||
return redirect('/');
|
||||
@ -290,6 +292,11 @@ class SetupController extends Controller
|
||||
unlink ($cacheServices);
|
||||
}
|
||||
|
||||
$cacheRoute = base_path('bootstrap/cache/routes-v7.php');
|
||||
if (file_exists($cacheRoute)) {
|
||||
unlink ($cacheRoute);
|
||||
}
|
||||
|
||||
Artisan::call('clear-compiled');
|
||||
Artisan::call('route:clear');
|
||||
Artisan::call('view:clear');
|
||||
|
@ -17,8 +17,11 @@ use App\Factory\CompanyGatewayFactory;
|
||||
use App\Http\Requests\StripeConnect\InitializeStripeConnectRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\Stripe\Connect\Account;
|
||||
use Illuminate\Http\Request;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
|
||||
class StripeConnectController extends BaseController
|
||||
@ -38,6 +41,8 @@ class StripeConnectController extends BaseController
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||
|
||||
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
||||
|
||||
$company_gateway = CompanyGateway::query()
|
||||
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
@ -45,53 +50,84 @@ class StripeConnectController extends BaseController
|
||||
|
||||
if ($company_gateway) {
|
||||
|
||||
$config = decrypt($company_gateway->config);
|
||||
$config = $company_gateway->getConfig();
|
||||
|
||||
if(property_exists($config, 'account_id'))
|
||||
return render('gateways.stripe.connect.existing');
|
||||
|
||||
}
|
||||
else
|
||||
$company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->id);
|
||||
return view('auth.connect.existing');
|
||||
|
||||
/* Set Credit Card To Enabled */
|
||||
$gateway_types = $company_gateway->driver(new Client)->gatewayTypes();
|
||||
$fees_and_limits = new \stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
$company_gateway->fees_and_limits = $fees_and_limits;
|
||||
$company_gateway->save();
|
||||
|
||||
/* Link account if existing account exists */
|
||||
if($account_id = $this->checkAccountAlreadyLinkToEmail($company_gateway, $request->getContact()->email)) {
|
||||
|
||||
$config = json_decode(decrypt($company_gateway->config));
|
||||
|
||||
$config->account_id = $account_id;
|
||||
$company_gateway->config = encrypt(json_encode($config));
|
||||
$company_gateway->save();
|
||||
|
||||
return render('gateways.stripe.connect.existing');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'type' => 'standard',
|
||||
'email' => $request->getContact()->email,
|
||||
'country' => $request->getCompany()->country()->iso_3166_2,
|
||||
];
|
||||
$stripe_client_id = config('ninja.ninja_stripe_client_id');
|
||||
$redirect_uri = 'https://invoicing.co/stripe/completed';
|
||||
$endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}";
|
||||
|
||||
$account = Account::create($data);
|
||||
$link = Account::link($account->id, $token);
|
||||
$company_gateway->config = encrypt(json_encode(['account_id' => $account->id]));
|
||||
$company_gateway->save();
|
||||
// if($email = $request->getContact()->email)
|
||||
// $endpoint .= "&stripe_user[email]={$email}";
|
||||
|
||||
return redirect($link['url']);
|
||||
// $company_name = str_replace(" ", "_", $company->present()->name());
|
||||
// $endpoint .= "&stripe_user[business_name]={$company_name}";
|
||||
|
||||
return redirect($endpoint);
|
||||
}
|
||||
|
||||
public function completed()
|
||||
public function completed(InitializeStripeConnectRequest $request)
|
||||
{
|
||||
return render('gateways.stripe.connect.completed');
|
||||
|
||||
\Stripe\Stripe::setApiKey(config('ninja.ninja_stripe_key'));
|
||||
|
||||
try {
|
||||
$response = \Stripe\OAuth::token([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $request->input('code'),
|
||||
]);
|
||||
|
||||
}catch(\Exception $e)
|
||||
{
|
||||
|
||||
nlog($e->getMessage());
|
||||
|
||||
}
|
||||
|
||||
// nlog($response);
|
||||
|
||||
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
||||
|
||||
$company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id);
|
||||
$fees_and_limits = new \stdClass;
|
||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
$company_gateway->fees_and_limits = $fees_and_limits;
|
||||
$company_gateway->setConfig([]);
|
||||
// $company_gateway->save();
|
||||
|
||||
$payload = [
|
||||
'account_id' => $response->stripe_user_id,
|
||||
"token_type" => 'bearer',
|
||||
"stripe_publishable_key" => $response->stripe_publishable_key,
|
||||
"scope" => $response->scope,
|
||||
"livemode" => $response->livemode,
|
||||
"stripe_user_id" => $response->stripe_user_id,
|
||||
"refresh_token" => $response->refresh_token,
|
||||
"access_token" => $response->access_token
|
||||
];
|
||||
|
||||
/* Link account if existing account exists */
|
||||
// if($account_id = $this->checkAccountAlreadyLinkToEmail($company_gateway, $request->getContact()->email)) {
|
||||
|
||||
// $payload['account_id'] = $account_id;
|
||||
// $payload['stripe_user_id'] = $account_id;
|
||||
// $company_gateway->setConfig($payload);
|
||||
// $company_gateway->save();
|
||||
|
||||
// return view('auth.connect.existing');
|
||||
|
||||
// }
|
||||
|
||||
$company_gateway->setConfig($payload);
|
||||
$company_gateway->save();
|
||||
|
||||
//response here
|
||||
return view('auth.connect.completed');
|
||||
}
|
||||
|
||||
|
||||
@ -111,4 +147,22 @@ class StripeConnectController extends BaseController
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************
|
||||
* Stripe OAuth
|
||||
*/
|
||||
|
||||
// public function initialize(InitializeStripeConnectRequest $request, string $token)
|
||||
// {
|
||||
|
||||
// $stripe_key = config('ninja.ninja_stripe_key');
|
||||
|
||||
// $endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_key}&scope=read_write";
|
||||
|
||||
// return redirect($endpoint);
|
||||
|
||||
// }
|
||||
}
|
||||
|
52
app/Http/Controllers/StripeController.php
Normal file
52
app/Http/Controllers/StripeController.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?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\Jobs\Util\ImportStripeCustomers;
|
||||
use App\Jobs\Util\StripeUpdatePaymentMethods;
|
||||
|
||||
class StripeController extends BaseController
|
||||
{
|
||||
|
||||
public function update()
|
||||
{
|
||||
if(auth()->user()->isAdmin())
|
||||
{
|
||||
|
||||
StripeUpdatePaymentMethods::dispatch(auth()->user()->company());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
|
||||
if(auth()->user()->isAdmin())
|
||||
{
|
||||
|
||||
ImportStripeCustomers::dispatch(auth()->user()->company());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
}
|
@ -41,7 +41,7 @@ class SubdomainController extends BaseController
|
||||
public function index()
|
||||
{
|
||||
|
||||
if(in_array(request()->input('subdomain'), $this->protected) || MultiDB::findAndSetDbByDomain(request()->input('subdomain')))
|
||||
if(in_array(request()->input('subdomain'), $this->protected) || MultiDB::findAndSetDbByDomain(['subdomain' => request()->input('subdomain')]))
|
||||
return response()->json(['message' => 'Domain not available'] , 401);
|
||||
|
||||
return response()->json(['message' => 'Domain available'], 200);
|
||||
|
@ -631,6 +631,7 @@ class TaskController extends BaseController
|
||||
|
||||
$task_status = TaskStatus::where('id', $this->decodePrimaryKey($task_status_hashed_id))
|
||||
->where('company_id', auth()->user()->company()->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
$task_status->status_order = $key;
|
||||
@ -643,18 +644,13 @@ class TaskController extends BaseController
|
||||
|
||||
$sort_status_id = $this->decodePrimaryKey($key);
|
||||
|
||||
// nlog($task_list);
|
||||
|
||||
foreach ($task_list as $key => $task)
|
||||
{
|
||||
|
||||
// nlog($task);
|
||||
|
||||
$task_record = Task::where('id', $this->decodePrimaryKey($task))
|
||||
->where('company_id', auth()->user()->company()->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
// nlog($task_record->id);
|
||||
|
||||
$task_record->status_order = $key;
|
||||
$task_record->status_id = $sort_status_id;
|
||||
@ -663,6 +659,6 @@ class TaskController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Ok'],200);
|
||||
return response()->json(['message' => 'Ok'], 200);
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,11 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function __construct(UserRepository $user_repo)
|
||||
{
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->user_repo = $user_repo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,11 +211,12 @@ class UserController extends BaseController
|
||||
|
||||
$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)));
|
||||
|
||||
return $this->itemResponse($user->fresh());
|
||||
$user->setCompany($company);
|
||||
$user->company_id = $company->id;
|
||||
|
||||
return $this->itemResponse($user);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -376,7 +379,6 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
{
|
||||
|
||||
$old_company_user = $user->company_user;
|
||||
$old_user = json_encode($user);
|
||||
$old_user_email = $user->getOriginal('email');
|
||||
|
@ -16,6 +16,7 @@ use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\CheckClientExistence;
|
||||
use App\Http\Middleware\CheckForMaintenanceMode;
|
||||
use App\Http\Middleware\ClientPortalEnabled;
|
||||
use App\Http\Middleware\ContactAccount;
|
||||
use App\Http\Middleware\ContactKeyLogin;
|
||||
use App\Http\Middleware\ContactRegister;
|
||||
use App\Http\Middleware\ContactSetDb;
|
||||
@ -110,7 +111,6 @@ class Kernel extends HttpKernel
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
//\App\Http\Middleware\StartupCheck::class,
|
||||
QueryLogging::class,
|
||||
],
|
||||
'shop' => [
|
||||
@ -142,6 +142,7 @@ class Kernel extends HttpKernel
|
||||
'api_secret_check' => ApiSecretCheck::class,
|
||||
'contact_token_auth' => ContactTokenAuth::class,
|
||||
'contact_db' => ContactSetDb::class,
|
||||
'contact_account' => ContactAccount::class,
|
||||
'domain_db' => SetDomainNameDb::class,
|
||||
'email_db' => SetEmailDb::class,
|
||||
'invite_db' => SetInviteDb::class,
|
||||
@ -160,4 +161,29 @@ class Kernel extends HttpKernel
|
||||
'check_client_existence' => CheckClientExistence::class,
|
||||
'user_verified' => UserVerified::class,
|
||||
];
|
||||
|
||||
|
||||
protected $middlewarePriority = [
|
||||
SetDomainNameDb::class,
|
||||
SetDb::class,
|
||||
SetWebDb::class,
|
||||
UrlSetDb::class,
|
||||
ContactSetDb::class,
|
||||
SetEmailDb::class,
|
||||
SetInviteDb::class,
|
||||
SetDbByCompanyKey::class,
|
||||
TokenAuth::class,
|
||||
ContactTokenAuth::class,
|
||||
ContactKeyLogin::class,
|
||||
Authenticate::class,
|
||||
ShopTokenAuth::class,
|
||||
ContactRegister::class,
|
||||
PhantomSecret::class,
|
||||
CheckClientExistence::class,
|
||||
ClientPortalEnabled::class,
|
||||
PasswordProtection::class,
|
||||
Locale::class,
|
||||
SubstituteBindings::class,
|
||||
ContactAccount::class,
|
||||
];
|
||||
}
|
||||
|
@ -181,14 +181,16 @@ class BillingPortalPurchase extends Component
|
||||
{
|
||||
$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) {
|
||||
return $this->steps['existing_user'] = true;
|
||||
}
|
||||
|
||||
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
|
||||
? $this->getPaymentMethods($contact)
|
||||
|
@ -9,6 +9,7 @@ class NameWebsiteLogo extends Component
|
||||
public $profile;
|
||||
|
||||
public $name;
|
||||
public $vat_number;
|
||||
public $website;
|
||||
public $phone;
|
||||
|
||||
@ -16,6 +17,7 @@ class NameWebsiteLogo extends Component
|
||||
|
||||
public $rules = [
|
||||
'name' => ['sometimes', 'min:3'],
|
||||
'vat_number' => ['sometimes'],
|
||||
'website' => ['sometimes'],
|
||||
'phone' => ['sometimes', 'string', 'max:255'],
|
||||
];
|
||||
@ -25,6 +27,7 @@ class NameWebsiteLogo extends Component
|
||||
$this->fill([
|
||||
'profile' => auth()->user('contact')->client,
|
||||
'name' => auth()->user('contact')->client->present()->name,
|
||||
'vat_number' => auth()->user('contact')->client->present()->vat_number,
|
||||
'website' => auth()->user('contact')->client->present()->website,
|
||||
'phone' => auth()->user('contact')->client->present()->phone,
|
||||
'saved' => ctrans('texts.save'),
|
||||
@ -41,6 +44,7 @@ class NameWebsiteLogo extends Component
|
||||
$data = $this->validate($this->rules);
|
||||
|
||||
$this->profile->name = $data['name'];
|
||||
$this->profile->vat_number = $data['vat_number'];
|
||||
$this->profile->website = $data['website'];
|
||||
$this->profile->phone = $data['phone'];
|
||||
|
||||
|
39
app/Http/Livewire/TasksTable.php
Normal file
39
app/Http/Livewire/TasksTable.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\Livewire;
|
||||
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
class TasksTable extends Component
|
||||
{
|
||||
use WithSorting;
|
||||
use WithPagination;
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Task::query()
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->whereNotNull('invoice_id')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.tasks-table', [
|
||||
'tasks' => $query,
|
||||
]);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use stdClass;
|
||||
@ -26,7 +27,7 @@ class ApiSecretCheck
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (! config('ninja.api_secret')) {
|
||||
if (! config('ninja.api_secret') || Ninja::isHosted()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ class CheckClientExistence
|
||||
$multiple_contacts = ClientContact::query()
|
||||
->where('email', auth('contact')->user()->email)
|
||||
->whereNotNull('email')
|
||||
->where('email', '<>', '')
|
||||
->whereNull('deleted_at')
|
||||
->distinct('company_id')
|
||||
->distinct('email')
|
||||
@ -38,6 +39,9 @@ class CheckClientExistence
|
||||
->whereHas('client', function ($query) {
|
||||
return $query->whereNull('deleted_at');
|
||||
})
|
||||
->whereHas('client.company', function ($query){
|
||||
return $query->where('account_id', auth('contact')->user()->client->company->account->id);
|
||||
})
|
||||
->get();
|
||||
|
||||
if (count($multiple_contacts) == 0) {
|
||||
|
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 Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ContactKeyLogin
|
||||
{
|
||||
@ -42,13 +43,20 @@ class ContactKeyLogin
|
||||
if (MultiDB::findAndSetDbByContactKey($request->segment(3))) {
|
||||
|
||||
if($client_contact = ClientContact::where('contact_key', $request->segment(3))->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);
|
||||
return redirect()->to('client/dashboard');
|
||||
}
|
||||
|
||||
}
|
||||
} elseif ($request->segment(2) && $request->segment(2) == 'key_login' && $request->segment(3)) {
|
||||
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);
|
||||
return redirect()->to('client/dashboard');
|
||||
}
|
||||
@ -56,19 +64,36 @@ class ContactKeyLogin
|
||||
if (MultiDB::findAndSetDbByClientHash($request->input('client_hash'))) {
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
} elseif ($request->has('client_hash')) {
|
||||
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');
|
||||
}
|
||||
} elseif ($request->segment(2) && $request->segment(2) == 'magic_link' && $request->segment(3)) {
|
||||
$contact_email = Cache::get($request->segment(3));
|
||||
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'))) {
|
||||
return redirect()->to($request->query('redirect'));
|
||||
|
@ -52,6 +52,6 @@ class ContactRegister
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return abort(404);
|
||||
abort(404, 'ContactRegister Middlware');
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class PasswordProtection
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
|
||||
|
||||
$error = [
|
||||
'message' => 'Invalid Password',
|
||||
'errors' => new stdClass,
|
||||
@ -44,9 +44,9 @@ class PasswordProtection
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
if (Cache::get(auth()->user()->hashed_id.'_logged_in')) {
|
||||
if (Cache::get(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in')) {
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||
|
||||
return $next($request);
|
||||
|
||||
@ -68,12 +68,13 @@ class PasswordProtection
|
||||
//If OAuth and user also has a password set - check both
|
||||
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->has_password){
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@ -83,7 +84,7 @@ class PasswordProtection
|
||||
|
||||
}elseif ($request->header('X-API-PASSWORD') && Hash::check($request->header('X-API-PASSWORD'), auth()->user()->password)) {
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||
|
||||
return $next($request);
|
||||
|
||||
|
@ -34,14 +34,16 @@ class RedirectIfAuthenticated
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect()->route('dashboard.index');
|
||||
}
|
||||
Auth::logout();
|
||||
// if (Auth::guard($guard)->check()) {
|
||||
// return redirect()->route('dashboard.index');
|
||||
// }
|
||||
break;
|
||||
default:
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect('/');
|
||||
}
|
||||
Auth::logout();
|
||||
// if (Auth::guard($guard)->check()) {
|
||||
// return redirect('/');
|
||||
// }
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ class SetDb
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
|
||||
$error = [
|
||||
'message' => 'Invalid Token',
|
||||
'errors' => new stdClass,
|
||||
|
@ -34,13 +34,58 @@ class SetDomainNameDb
|
||||
/*
|
||||
* Use the host name to set the active DB
|
||||
**/
|
||||
if ($request->getSchemeAndHttpHost() && config('ninja.db.multi_db_enabled') && ! MultiDB::findAndSetDbByDomain($request->getSchemeAndHttpHost())) {
|
||||
if (request()->json) {
|
||||
return response()->json($error, 403);
|
||||
} else {
|
||||
abort(404);
|
||||
|
||||
if(!config('ninja.db.multi_db_enabled'))
|
||||
return $next($request);
|
||||
|
||||
|
||||
if (strpos($request->getHost(), 'invoicing.co') !== false)
|
||||
{
|
||||
$subdomain = explode('.', $request->getHost())[0];
|
||||
|
||||
$query = [
|
||||
'subdomain' => $subdomain,
|
||||
'portal_mode' => 'subdomain',
|
||||
];
|
||||
|
||||
if($company = MultiDB::findAndSetDbByDomain($query)){
|
||||
$request->attributes->add(['account_id' => $company->account_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($request->json) {
|
||||
return response()->json($error, 403);
|
||||
} else {
|
||||
MultiDB::setDb('db-ninja-01');
|
||||
nlog("I could not set the DB - defaulting to DB1");
|
||||
//abort(400, 'Domain not found');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$query = [
|
||||
'portal_domain' => $request->getSchemeAndHttpHost(),
|
||||
'portal_mode' => 'domain',
|
||||
];
|
||||
|
||||
if($company = MultiDB::findAndSetDbByDomain($query)){
|
||||
$request->attributes->add(['account_id' => $company->account_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($request->json) {
|
||||
return response()->json($error, 403);
|
||||
} else {
|
||||
MultiDB::setDb('db-ninja-01');
|
||||
nlog("I could not set the DB - defaulting to DB1");
|
||||
//abort(400, 'Domain not found');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@ -34,15 +34,10 @@ class SetEmailDb
|
||||
|
||||
if ($request->input('email') && config('ninja.db.multi_db_enabled')) {
|
||||
|
||||
|
||||
if (! MultiDB::userFindAndSetDb($request->input('email')))
|
||||
return response()->json($error, 400);
|
||||
|
||||
|
||||
}
|
||||
// else {
|
||||
// return response()->json($error, 403);
|
||||
// }
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class SetInviteDb
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$error = [
|
||||
'message' => 'Invalid URL',
|
||||
'message' => 'I could not find the database for this object.',
|
||||
'errors' => new stdClass,
|
||||
];
|
||||
/*
|
||||
@ -46,7 +46,7 @@ class SetInviteDb
|
||||
if (request()->json) {
|
||||
return response()->json($error, 403);
|
||||
} else {
|
||||
abort(404);
|
||||
abort(404,'I could not find the database for this object.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,6 @@ class TokenAuth
|
||||
| us to decouple a $user and their attached companies completely.
|
||||
|
|
||||
*/
|
||||
$user->setCompany($company_token->company);
|
||||
|
||||
app('queue')->createPayloadUsing(function () use ($company_token) {
|
||||
return ['db' => $company_token->company->db];
|
||||
@ -67,6 +66,7 @@ class TokenAuth
|
||||
|
||||
//stateless, don't remember the user.
|
||||
auth()->login($user, false);
|
||||
auth()->user()->setCompany($company_token->company);
|
||||
|
||||
} else {
|
||||
$error = [
|
||||
|
@ -40,13 +40,13 @@ class CreateAccountRequest extends Request
|
||||
'password' => 'required|string|min:6',
|
||||
'email' => 'bail|required|email:rfc,dns',
|
||||
'email' => new NewUniqueUserRule(),
|
||||
'privacy_policy' => 'required',
|
||||
'terms_of_service' => 'required',
|
||||
'privacy_policy' => 'required|boolean',
|
||||
'terms_of_service' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
{nlog($this->all());
|
||||
$input = $this->all();
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
abort(404);
|
||||
abort(404, 'Register request not found.');
|
||||
}
|
||||
}
|
||||
|
30
app/Http/Requests/ClientPortal/Tasks/ShowTasksRequest.php
Normal file
30
app/Http/Requests/ClientPortal/Tasks/ShowTasksRequest.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\ClientPortal\Tasks;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ShowTasksRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return (bool)auth()->user('contact')->client->getSetting('enable_client_portal_tasks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
@ -14,8 +14,10 @@ namespace App\Http\Requests\Company;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Company\ValidCompanyQuantity;
|
||||
use App\Http\ValidationRules\Company\ValidSubdomain;
|
||||
use App\Http\ValidationRules\ValidSettingsRule;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class StoreCompanyRequest extends Request
|
||||
@ -45,7 +47,13 @@ class StoreCompanyRequest extends Request
|
||||
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
||||
$rules['portal_domain'] = 'sometimes|url';
|
||||
} else {
|
||||
$rules['subdomain'] = 'nullable|alpha_num';
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$rules['subdomain'] = ['nullable', 'alpha_num', new ValidSubdomain($this->all())];
|
||||
}
|
||||
else
|
||||
$rules['subdomain'] = 'nullable|alpha_num';
|
||||
|
||||
}
|
||||
|
||||
return $rules;
|
||||
@ -55,8 +63,9 @@ class StoreCompanyRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||
$input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
|
||||
//https not sure i should be forcing this.
|
||||
// if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||
// $input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
|
||||
|
||||
if (array_key_exists('google_analytics_url', $input)) {
|
||||
$input['google_analytics_key'] = $input['google_analytics_url'];
|
||||
|
@ -13,7 +13,9 @@ namespace App\Http\Requests\Company;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Company\ValidSubdomain;
|
||||
use App\Http\ValidationRules\ValidSettingsRule;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class UpdateCompanyRequest extends Request
|
||||
@ -46,7 +48,12 @@ class UpdateCompanyRequest extends Request
|
||||
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
||||
$rules['portal_domain'] = 'sometimes|url';
|
||||
} else {
|
||||
$rules['subdomain'] = 'nullable|alpha_num';
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$rules['subdomain'] = ['nullable', 'alpha_num', new ValidSubdomain($this->all())];
|
||||
}
|
||||
else
|
||||
$rules['subdomain'] = 'nullable|alpha_num';
|
||||
}
|
||||
|
||||
// if($this->company->account->isPaidHostedClient()) {
|
||||
@ -60,8 +67,8 @@ class UpdateCompanyRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||
$input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
|
||||
// if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||
// $input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
|
||||
|
||||
if (array_key_exists('settings', $input)) {
|
||||
$input['settings'] = $this->filterSaveableSettings($input['settings']);
|
||||
|
39
app/Http/Requests/Export/StoreExportRequest.php
Normal file
39
app/Http/Requests/Export/StoreExportRequest.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\Export;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class StoreExportRequest extends Request
|
||||
{
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
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',
|
||||
];
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ class PaymentWebhookRequest extends Request
|
||||
|
||||
public function authorize()
|
||||
{
|
||||
MultiDB::findAndSetDbByCompanyKey($this->getCompany()->company_key);
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -45,7 +45,7 @@ class PaymentWebhookRequest extends Request
|
||||
* @param mixed $id
|
||||
* @return null|\App\Models\CompanyGateway
|
||||
*/
|
||||
public function getCompanyGateway(): ?CompanyGateway
|
||||
public function getCompanyGateway()
|
||||
{
|
||||
return CompanyGateway::findOrFail($this->decodePrimaryKey($this->company_gateway_id));
|
||||
}
|
||||
@ -56,13 +56,13 @@ class PaymentWebhookRequest extends Request
|
||||
* @param string $hash
|
||||
* @return null|\App\Models\PaymentHash
|
||||
*/
|
||||
public function getPaymentHash(): ?PaymentHash
|
||||
public function getPaymentHash()
|
||||
{
|
||||
if ($this->query('hash')) {
|
||||
return PaymentHash::where('hash', $this->query('hash'))->firstOrFail();
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,7 +94,7 @@ class PaymentWebhookRequest extends Request
|
||||
|
||||
// If none of previously done logics is correct, we'll just display
|
||||
// not found page.
|
||||
abort(404);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,11 +102,14 @@ class PaymentWebhookRequest extends Request
|
||||
*
|
||||
* @return null|\App\Models\Client
|
||||
*/
|
||||
public function getClient(): ?Client
|
||||
public function getClient()
|
||||
{
|
||||
$hash = $this->getPaymentHash();
|
||||
|
||||
return Client::find($hash->data->client_id)->firstOrFail();
|
||||
if($hash)
|
||||
return Client::find($hash->data->client_id)->firstOrFail();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +49,9 @@ class InitializeStripeConnectRequest extends FormRequest
|
||||
*/
|
||||
public function getTokenContent()
|
||||
{
|
||||
if($this->state)
|
||||
$this->token = $this->state;
|
||||
|
||||
$data = Cache::get($this->token);
|
||||
|
||||
return $data;
|
||||
|
@ -45,9 +45,6 @@ class UpdateUserRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
// if (isset($input['company_user']) && ! auth()->user()->isAdmin()) {
|
||||
// unset($input['company_user']);
|
||||
// }
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\ValidVendorGroupSettingsRule;
|
||||
use App\Models\Vendor;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreVendorRequest extends Request
|
||||
{
|
||||
@ -39,6 +40,9 @@ class StoreVendorRequest extends Request
|
||||
//$rules['settings'] = new ValidVendorGroupSettingsRule();
|
||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id);
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
50
app/Http/ValidationRules/Company/ValidSubdomain.php
Normal file
50
app/Http/ValidationRules/Company/ValidSubdomain.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?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\ValidationRules\Company;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class ValidCompanyQuantity.
|
||||
*/
|
||||
class ValidSubdomain implements Rule
|
||||
{
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
private $input;
|
||||
|
||||
public function __construct($input)
|
||||
{
|
||||
$this->input = $input;
|
||||
}
|
||||
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
if(empty($input['subdomain']))
|
||||
return true;
|
||||
|
||||
return MultiDB::checkDomainAvailable($input['subdomain']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return ctrans('texts.subdomain_taken');
|
||||
}
|
||||
}
|
@ -57,13 +57,13 @@ class InvoiceBalanceSanity implements Rule
|
||||
private function checkIfInvoiceBalanceIsSane() : bool
|
||||
{
|
||||
|
||||
$this->invoice->line_items = $this->input['line_items'];
|
||||
DB::connection(config('database.default'))->beginTransaction();
|
||||
|
||||
DB::beginTransaction();
|
||||
$this->invoice = Invoice::on(config('database.default'))->find($this->invoice->id);
|
||||
$this->invoice->line_items = $this->input['line_items'];
|
||||
$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){
|
||||
$this->message = 'Invoice balance cannot go negative';
|
||||
@ -71,7 +71,7 @@ class InvoiceBalanceSanity implements Rule
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ use Illuminate\Contracts\Validation\Rule;
|
||||
*/
|
||||
class AttachableUser implements Rule
|
||||
{
|
||||
public $message;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -39,7 +40,7 @@ class AttachableUser implements Rule
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return "Cannot add the same user to the same company";
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,15 +58,22 @@ class AttachableUser implements Rule
|
||||
if(!$user)
|
||||
return true;
|
||||
|
||||
$user_already_attached = CompanyUser::query()
|
||||
$user_already_attached = CompanyUser::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('account_id',$user->account_id)
|
||||
->where('company_id', auth()->user()->company()->id)
|
||||
->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;
|
||||
}
|
||||
|
||||
if($user->account_id != auth()->user()->account_id){
|
||||
$this->message = ctrans('texts.user_cross_linked_error');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -34,7 +34,9 @@ class PortalComposer
|
||||
$view->with($this->portalData());
|
||||
|
||||
if (auth()->user()) {
|
||||
Lang::replace(Ninja::transformTranslations(auth()->user()->client->getMergedSettings()));
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations(auth()->user()->client->getMergedSettings()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,9 +84,7 @@ class PortalComposer
|
||||
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
||||
|
||||
if (auth()->user('contact')->client->getSetting('enable_client_portal_tasks')) {
|
||||
$data[] = ['title' => ctrans('texts.tasks'), 'url' => 'client.dashboard', 'icon' => 'clock'];
|
||||
|
||||
// TODO: Update when 'tasks' module is available in client portal.
|
||||
$data[] = ['title' => ctrans('texts.tasks'), 'url' => 'client.tasks.index', 'icon' => 'clock'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
@ -25,11 +25,13 @@ use App\Jobs\Util\VersionCheck;
|
||||
use App\Mail\Admin\AccountCreatedObject;
|
||||
use App\Mail\Admin\VerifyUserObject;
|
||||
use App\Models\Account;
|
||||
use App\Models\Timezone;
|
||||
use App\Notifications\Ninja\NewAccountCreated;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
@ -40,9 +42,12 @@ class CreateAccount
|
||||
|
||||
protected $request;
|
||||
|
||||
public function __construct(array $sp660339)
|
||||
protected $client_ip;
|
||||
|
||||
public function __construct(array $sp660339, $client_ip)
|
||||
{
|
||||
$this->request = $sp660339;
|
||||
$this->client_ip = $client_ip;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
@ -74,6 +79,7 @@ class CreateAccount
|
||||
|
||||
$sp035a66 = CreateCompany::dispatchNow($this->request, $sp794f3f);
|
||||
$sp035a66->load('account');
|
||||
$sp035a66->settings = $this->processSettings($sp035a66->settings);
|
||||
$sp794f3f->default_company_id = $sp035a66->id;
|
||||
$sp794f3f->save();
|
||||
|
||||
@ -107,4 +113,53 @@ class CreateAccount
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
509
app/Jobs/Company/CompanyExport.php
Normal file
509
app/Jobs/Company/CompanyExport.php
Normal file
@ -0,0 +1,509 @@
|
||||
<?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\Company;
|
||||
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadBackup;
|
||||
use App\Mail\DownloadInvoices;
|
||||
use App\Models\Company;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Models\User;
|
||||
use App\Models\VendorContact;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use ZipStream\Option\Archive;
|
||||
use ZipStream\ZipStream;
|
||||
|
||||
class CompanyExport implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
|
||||
|
||||
public $company;
|
||||
|
||||
private $export_format;
|
||||
|
||||
private $export_data = [];
|
||||
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Company $company
|
||||
* @param User $user
|
||||
* @param string $custom_token_name
|
||||
*/
|
||||
public function __construct(Company $company, User $user, $export_format = 'json')
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->user = $user;
|
||||
$this->export_format = $export_format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return CompanyToken|null
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->company = Company::where('company_key', $this->company->company_key)->first();
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$this->export_data['app_version'] = config('ninja.app_version');
|
||||
|
||||
$this->export_data['activities'] = $this->company->all_activities->map(function ($activity){
|
||||
|
||||
$activity = $this->transformArrayOfKeys($activity, [
|
||||
'user_id',
|
||||
'company_id',
|
||||
'client_id',
|
||||
'client_contact_id',
|
||||
'account_id',
|
||||
'project_id',
|
||||
'vendor_id',
|
||||
'payment_id',
|
||||
'invoice_id',
|
||||
'credit_id',
|
||||
'invitation_id',
|
||||
'task_id',
|
||||
'expense_id',
|
||||
'token_id',
|
||||
'quote_id',
|
||||
'subscription_id',
|
||||
'recurring_invoice_id'
|
||||
]);
|
||||
|
||||
return $activity;
|
||||
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
$this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){
|
||||
|
||||
$backup = $activity->backup;
|
||||
|
||||
if(!$backup)
|
||||
return;
|
||||
|
||||
$backup->activity_id = $this->encodePrimaryKey($backup->activity_id);
|
||||
|
||||
return $backup;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['users'] = $this->company->users()->withTrashed()->cursor()->map(function ($user){
|
||||
|
||||
$user->account_id = $this->encodePrimaryKey($user->account_id);
|
||||
// $user->id = $this->encodePrimaryKey($user->id);
|
||||
|
||||
return $user->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact){
|
||||
|
||||
$client_contact = $this->transformArrayOfKeys($client_contact, ['company_id', 'user_id', 'client_id']);
|
||||
|
||||
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',
|
||||
]);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token){
|
||||
|
||||
$client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['company_id', 'client_id']);
|
||||
|
||||
return $client_gateway_token->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['clients'] = $this->company->clients->map(function ($client){
|
||||
|
||||
$client = $this->transformArrayOfKeys($client, ['company_id', 'user_id', 'assigned_user_id', 'group_settings_id']);
|
||||
|
||||
return $client->makeVisible(['id','private_notes','user_id','company_id','last_login','hashed_id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['company'] = $this->company->toArray();
|
||||
|
||||
$this->export_data['company_gateways'] = $this->company->company_gateways->map(function ($company_gateway){
|
||||
|
||||
$company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']);
|
||||
$company_gateway->config = decrypt($company_gateway->config);
|
||||
|
||||
return $company_gateway->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['company_tokens'] = $this->company->tokens->map(function ($token){
|
||||
|
||||
$token = $this->transformArrayOfKeys($token, ['company_id', 'account_id', 'user_id']);
|
||||
|
||||
return $token;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['company_ledger'] = $this->company->ledger->map(function ($ledger){
|
||||
|
||||
$ledger = $this->transformArrayOfKeys($ledger, ['activity_id', 'client_id', 'company_id', 'account_id', 'user_id','company_ledgerable_id']);
|
||||
|
||||
return $ledger;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['company_users'] = $this->company->company_users->map(function ($company_user){
|
||||
|
||||
$company_user = $this->transformArrayOfKeys($company_user, ['company_id', 'account_id', 'user_id']);
|
||||
|
||||
return $company_user;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['credits'] = $this->company->credits->map(function ($credit){
|
||||
|
||||
$credit = $this->transformBasicEntities($credit);
|
||||
$credit = $this->transformArrayOfKeys($credit, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','invoice_id']);
|
||||
|
||||
return $credit->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$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', 'credit_id']);
|
||||
|
||||
return $credit->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->all();
|
||||
|
||||
$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','documentable_id']);
|
||||
$document->hashed_id = $this->encodePrimaryKey($document->id);
|
||||
|
||||
return $document->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['expense_categories'] = $this->company->expense_categories->map(function ($expense_category){
|
||||
|
||||
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
|
||||
|
||||
return $expense_category->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['expenses'] = $this->company->expenses->map(function ($expense){
|
||||
|
||||
$expense = $this->transformBasicEntities($expense);
|
||||
$expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id','project_id']);
|
||||
|
||||
return $expense->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs){
|
||||
|
||||
$gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
|
||||
|
||||
return $gs->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['invoices'] = $this->company->invoices->map(function ($invoice){
|
||||
|
||||
$invoice = $this->transformBasicEntities($invoice);
|
||||
$invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||
|
||||
return $invoice->makeVisible(['id',
|
||||
'private_notes',
|
||||
'user_id',
|
||||
'client_id',
|
||||
'company_id',]);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$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', 'invoice_id']);
|
||||
|
||||
return $invoice->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['payment_terms'] = $this->company->user_payment_terms->map(function ($term){
|
||||
|
||||
$term = $this->transformArrayOfKeys($term, ['user_id', 'company_id']);
|
||||
|
||||
return $term;
|
||||
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
|
||||
$this->export_data['payments'] = $this->company->payments->map(function ($payment){
|
||||
|
||||
$payment = $this->transformBasicEntities($payment);
|
||||
$payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
|
||||
|
||||
$payment->paymentables = $this->transformPaymentable($payment);
|
||||
|
||||
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){
|
||||
|
||||
$project = $this->transformBasicEntities($project);
|
||||
$project = $this->transformArrayOfKeys($project, ['client_id']);
|
||||
|
||||
return $project->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['quotes'] = $this->company->quotes->map(function ($quote){
|
||||
|
||||
$quote = $this->transformBasicEntities($quote);
|
||||
$quote = $this->transformArrayOfKeys($quote, ['invoice_id','recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||
|
||||
return $quote->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$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', 'quote_id']);
|
||||
|
||||
return $quote->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices->makeVisible(['id'])->map(function ($ri){
|
||||
|
||||
$ri = $this->transformBasicEntities($ri);
|
||||
$ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||
|
||||
return $ri->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['recurring_invoice_invitations'] = RecurringInvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($ri){
|
||||
|
||||
$ri = $this->transformArrayOfKeys($ri, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
||||
|
||||
return $ri;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription){
|
||||
|
||||
$subscription = $this->transformBasicEntities($subscription);
|
||||
$subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
|
||||
|
||||
return $subscription->makeVisible([ 'id',
|
||||
'user_id',
|
||||
'assigned_user_id',
|
||||
'company_id',
|
||||
'product_ids',
|
||||
'recurring_product_ids',
|
||||
'group_id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['system_logs'] = $this->company->system_logs->map(function ($log){
|
||||
|
||||
$log->client_id = $this->encodePrimaryKey($log->client_id);
|
||||
$log->company_id = $this->encodePrimaryKey($log->company_id);
|
||||
|
||||
return $log;
|
||||
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
$this->export_data['tasks'] = $this->company->tasks->map(function ($task){
|
||||
|
||||
$task = $this->transformBasicEntities($task);
|
||||
$task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']);
|
||||
|
||||
return $task->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['task_statuses'] = $this->company->task_statuses->map(function ($status){
|
||||
|
||||
$status->id = $this->encodePrimaryKey($status->id);
|
||||
$status->user_id = $this->encodePrimaryKey($status->user_id);
|
||||
$status->company_id = $this->encodePrimaryKey($status->company_id);
|
||||
|
||||
return $status;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['tax_rates'] = $this->company->tax_rates->map(function ($rate){
|
||||
|
||||
$rate->company_id = $this->encodePrimaryKey($rate->company_id);
|
||||
$rate->user_id = $this->encodePrimaryKey($rate->user_id);
|
||||
|
||||
return $rate;
|
||||
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
$this->export_data['vendors'] = $this->company->vendors->map(function ($vendor){
|
||||
|
||||
return $this->transformBasicEntities($vendor)->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['vendor_contacts'] = VendorContact::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($vendor){
|
||||
|
||||
$vendor = $this->transformBasicEntities($vendor);
|
||||
$vendor->vendor_id = $this->encodePrimaryKey($vendor->vendor_id);
|
||||
|
||||
return $vendor->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['webhooks'] = $this->company->webhooks->map(function ($hook){
|
||||
|
||||
$hook->user_id = $this->encodePrimaryKey($hook->user_id);
|
||||
$hook->company_id = $this->encodePrimaryKey($hook->company_id);
|
||||
|
||||
return $hook;
|
||||
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
//write to tmp and email to owner();
|
||||
|
||||
$this->zipAndSend();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function transformBasicEntities($model)
|
||||
{
|
||||
|
||||
return $this->transformArrayOfKeys($model, ['user_id', 'assigned_user_id', 'company_id']);
|
||||
|
||||
}
|
||||
|
||||
private function transformArrayOfKeys($model, $keys)
|
||||
{
|
||||
|
||||
foreach($keys as $key){
|
||||
$model->{$key} = $this->encodePrimaryKey($model->{$key});
|
||||
}
|
||||
|
||||
return $model;
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||
|
||||
$zip_path = public_path('storage/backups/'.$file_name);
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
if ($zip->open($zip_path, \ZipArchive::CREATE)!==TRUE) {
|
||||
nlog("cannot open {$zip_path}");
|
||||
}
|
||||
|
||||
$zip->addFromString("backup.json", json_encode($this->export_data));
|
||||
$zip->close();
|
||||
|
||||
if(Ninja::isHosted()) {
|
||||
Storage::disk(config('filesystems.default'))->put('backups/'.$file_name, file_get_contents($zip_path));
|
||||
}
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url('backups/'.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->company = $this->company;
|
||||
$nmo->settings = $this->company->settings;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||
UnlinkFile::dispatch('public', 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||
}
|
||||
|
||||
}
|
1103
app/Jobs/Company/CompanyImport.php
Normal file
1103
app/Jobs/Company/CompanyImport.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,9 @@
|
||||
namespace App\Jobs\Company;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Request;
|
||||
@ -60,6 +62,12 @@ class CreateCompany
|
||||
$company->subdomain = isset($this->request['subdomain']) ? $this->request['subdomain'] : '';
|
||||
$company->custom_fields = new \stdClass;
|
||||
$company->default_password_timeout = 1800000;
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$company->subdomain = MultiDB::randomSubdomainGenerator();
|
||||
else
|
||||
$company->subdomain = '';
|
||||
|
||||
$company->save();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@ class CreateEntityPdf implements ShouldQueue
|
||||
*
|
||||
* @param $invitation
|
||||
*/
|
||||
public function __construct($invitation)
|
||||
public function __construct($invitation, $disk = 'public')
|
||||
{
|
||||
$this->invitation = $invitation;
|
||||
|
||||
@ -86,22 +86,26 @@ class CreateEntityPdf implements ShouldQueue
|
||||
|
||||
$this->contact = $invitation->contact;
|
||||
|
||||
$this->disk = $disk ?? config('filesystems.default');
|
||||
$this->disk = $disk;
|
||||
|
||||
// $this->disk = $disk ?? config('filesystems.default');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->contact->preferredLocale());
|
||||
|
||||
/* Forget the singleton*/
|
||||
App::forgetInstance('translator');
|
||||
|
||||
/* Init a new copy of the translator*/
|
||||
$t = app('translator');
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->contact->preferredLocale());
|
||||
|
||||
// nlog($this->entity->client->getMergedSettings());
|
||||
|
||||
/* Set customized translations _NOW_ */
|
||||
Lang::replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||
$t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||
|
||||
$this->entity->service()->deletePdf();
|
||||
|
||||
@ -192,12 +196,12 @@ class CreateEntityPdf implements ShouldQueue
|
||||
try{
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
|
||||
throw new FilePermissionsFailure('Could not write the PDF, permission issues!');
|
||||
throw new FilePermissionsFailure($e->getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
@ -209,8 +213,5 @@ class CreateEntityPdf implements ShouldQueue
|
||||
{
|
||||
|
||||
}
|
||||
// public function failed(\Exception $exception)
|
||||
// {
|
||||
// nlog("help!");
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -96,8 +96,8 @@ class CSVImport implements ShouldQueue {
|
||||
MultiDB::setDb( $this->company->db );
|
||||
|
||||
Auth::login( $this->company->owner(), true );
|
||||
|
||||
$this->company->owner()->setCompany( $this->company );
|
||||
|
||||
auth()->user()->setCompany($this->company);
|
||||
|
||||
$this->buildMaps();
|
||||
|
||||
@ -293,10 +293,14 @@ class CSVImport implements ShouldQueue {
|
||||
],
|
||||
];
|
||||
|
||||
$payment_repository->save(
|
||||
$payment_data,
|
||||
PaymentFactory::create( $this->company->id, $invoice->user_id, $invoice->client_id )
|
||||
);
|
||||
/* Make sure we don't apply any payments to invoices with a Zero Amount*/
|
||||
if($invoice->amount > 0)
|
||||
{
|
||||
$payment_repository->save(
|
||||
$payment_data,
|
||||
PaymentFactory::create( $this->company->id, $invoice->user_id, $invoice->client_id )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,23 +83,24 @@ class ZipInvoices implements ShouldQueue
|
||||
$zip = new ZipStream($file_name, $options);
|
||||
|
||||
foreach ($this->invoices as $invoice) {
|
||||
$zip->addFileFromPath(basename($invoice->pdf_file_path()), TempFile::path($invoice->pdf_file_path()));
|
||||
//$zip->addFileFromPath(basename($invoice->pdf_file_path()), TempFile::path($invoice->pdf_file_path()));
|
||||
$zip->addFileFromPath(basename($invoice->pdf_file_path()), $invoice->pdf_file_path());
|
||||
}
|
||||
|
||||
$zip->finish();
|
||||
|
||||
Storage::disk(config('filesystems.default'))->put($path.$file_name, $tempStream);
|
||||
Storage::disk('public')->put($path.$file_name, $tempStream);
|
||||
|
||||
fclose($tempStream);
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadInvoices(Storage::disk(config('filesystems.default'))->url($path.$file_name), $this->company);
|
||||
$nmo->mailable = new DownloadInvoices(Storage::disk('public')->url($path.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->settings = $this->settings;
|
||||
$nmo->company = $this->company;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
||||
UnlinkFile::dispatch('public', $path.$file_name)->delay(now()->addHours(1));
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use App\Libraries\Google\Google;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\TemplateEmail;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
@ -46,30 +47,38 @@ class NinjaMailerJob implements ShouldQueue
|
||||
{
|
||||
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 $nmo;
|
||||
|
||||
public function __construct(NinjaMailerObject $nmo)
|
||||
public $override;
|
||||
|
||||
public $company;
|
||||
|
||||
public function __construct(NinjaMailerObject $nmo, bool $override = false)
|
||||
{
|
||||
|
||||
$this->nmo = $nmo;
|
||||
$this->override = $override;
|
||||
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
/*If we are migrating data we don't want to fire any emails*/
|
||||
if ($this->nmo->company->is_disabled)
|
||||
if ($this->nmo->company->is_disabled && !$this->override)
|
||||
return true;
|
||||
|
||||
/*Set the correct database*/
|
||||
MultiDB::setDb($this->nmo->company->db);
|
||||
|
||||
/* Serializing models from other jobs wipes the primary key */
|
||||
$this->company = Company::where('company_key', $this->nmo->company->company_key)->first();
|
||||
|
||||
/* Set the email driver */
|
||||
$this->setMailDriver();
|
||||
|
||||
@ -83,6 +92,10 @@ class NinjaMailerJob implements ShouldQueue
|
||||
$this->nmo->mailable->replyTo($this->nmo->settings->reply_to_email, $reply_to_name);
|
||||
|
||||
}
|
||||
else {
|
||||
$this->nmo->mailable->replyTo($this->company->owner()->email, $this->company->owner()->present()->name());
|
||||
}
|
||||
|
||||
|
||||
if (strlen($this->nmo->settings->bcc_email) > 1) {
|
||||
nlog('bcc list available');
|
||||
@ -93,7 +106,7 @@ class NinjaMailerJob implements ShouldQueue
|
||||
|
||||
//send email
|
||||
try {
|
||||
nlog("trying to send");
|
||||
nlog("trying to send to {$this->nmo->to_user->email} ". now()->toDateTimeString());
|
||||
|
||||
Mail::to($this->nmo->to_user->email)
|
||||
->send($this->nmo->mailable);
|
||||
@ -104,10 +117,12 @@ class NinjaMailerJob implements ShouldQueue
|
||||
} catch (\Exception $e) {
|
||||
|
||||
nlog("error failed with {$e->getMessage()}");
|
||||
// nlog($e);
|
||||
|
||||
if($this->nmo->entity)
|
||||
$this->entityEmailFailed($e->getMessage());
|
||||
|
||||
if(Ninja::isHosted())
|
||||
app('sentry')->captureException($e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,9 +154,9 @@ class NinjaMailerJob implements ShouldQueue
|
||||
App::forgetInstance('mail.manager'); //singletons must be destroyed!
|
||||
App::forgetInstance('mailer');
|
||||
App::forgetInstance('laravelgmail');
|
||||
|
||||
$t = app('translator');
|
||||
/* Inject custom translations if any exist */
|
||||
Lang::replace(Ninja::transformTranslations($this->nmo->settings));
|
||||
$t->replace(Ninja::transformTranslations($this->nmo->settings));
|
||||
|
||||
switch ($this->nmo->settings->email_sending_method) {
|
||||
case 'default':
|
||||
@ -169,7 +184,15 @@ class NinjaMailerJob implements ShouldQueue
|
||||
nlog("Sending via {$user->name()}");
|
||||
|
||||
$google = (new Google())->init();
|
||||
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
||||
|
||||
try{
|
||||
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
||||
$this->nmo->settings->email_sending_method = 'default';
|
||||
return $this->setMailDriver();
|
||||
}
|
||||
|
||||
if ($google->getClient()->isAccessTokenExpired()) {
|
||||
$google->refreshToken($user);
|
||||
@ -202,7 +225,8 @@ class NinjaMailerJob implements ShouldQueue
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_SEND,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
$recipient_object
|
||||
$recipient_object,
|
||||
$this->nmo->company
|
||||
);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user