1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-29 04:37:11 +02:00

Merge pull request #5771 from turbo124/v5-stable

v5.1.63
This commit is contained in:
David Bomba 2021-05-21 18:57:00 +10:00 committed by GitHub
commit a340ce8e8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
200 changed files with 198754 additions and 195188 deletions

View File

@ -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:

View File

@ -1 +1 @@
5.1.62
5.1.63

View File

@ -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;

View File

@ -105,6 +105,8 @@ 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',
]);
$account->default_company_id = $company->id;

View File

@ -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();
}

View File

@ -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([

View File

@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class NonExistingBackupFile extends Exception
{
// ..
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -30,7 +30,7 @@ function isActive($page, bool $boolean = false)
}
if ($page == $current_page) {
return 'bg-primary-darken';
return 'bg-gray-200';
}
return false;

View File

@ -72,7 +72,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 +84,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);

View File

@ -104,9 +104,9 @@ 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')]);
// $user = MultiDB::hasUser(['email' => $request->input('email')]);
$this->validateEmail($request);
@ -118,6 +118,10 @@ class ForgotPasswordController 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);

View File

@ -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;
@ -209,7 +210,7 @@ class LoginController extends BaseController
else
$timeout = $timeout/1000;
Cache::put($user->hashed_id.'_logged_in', Str::random(64), $timeout);
Cache::put($user->hashed_id.'_'.$user->account_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::query()
->where('user_id', auth()->user()->id);
@ -236,11 +237,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);
@ -366,8 +368,7 @@ class LoginController extends BaseController
else
$timeout = $timeout/1000;
Cache::put($existing_user->hashed_id.'_logged_in', Str::random(64), $timeout);
Cache::put($existing_user->hashed_id.'_'.$existing_user->account_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::query()
->where('user_id', auth()->user()->id);
@ -416,8 +417,7 @@ class LoginController extends BaseController
else
$timeout = $timeout/1000;
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);
$cu = CompanyUser::whereUserId(auth()->user()->id);

View File

@ -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;
@ -366,13 +363,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);

View File

@ -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()

View File

@ -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);
}
}

View File

@ -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

View File

@ -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());

View File

@ -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

View 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');
}
}

View File

@ -114,7 +114,7 @@ class ConnectedAccountController extends BaseController
auth()->user()->save();
$timeout = auth()->user()->company()->default_password_timeout;
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 $this->itemResponse(auth()->user());
@ -160,6 +160,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();

View File

@ -536,10 +536,8 @@ class CreditController extends BaseController
}
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()));
$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);
}
/**

View 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);
}
}

View File

@ -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,13 +845,10 @@ class InvoiceController extends BaseController
*/
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
{
$file_path = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
try {
$file = public_path("storage/{$file_path}");
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache']);
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
} catch (\Exception $e) {
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
}

View File

@ -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;

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -275,6 +275,8 @@ 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')) )
@ -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');

View File

@ -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');
return view('auth.connect.existing');
}
else
$company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->id);
/* 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;
$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}";
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
$company_gateway->fees_and_limits = $fees_and_limits;
$company_gateway->save();
// if($email = $request->getContact()->email)
// $endpoint .= "&stripe_user[email]={$email}";
/* Link account if existing account exists */
if($account_id = $this->checkAccountAlreadyLinkToEmail($company_gateway, $request->getContact()->email)) {
// $company_name = str_replace(" ", "_", $company->present()->name());
// $endpoint .= "&stripe_user[business_name]={$company_name}";
$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,
];
$account = Account::create($data);
$link = Account::link($account->id, $token);
$company_gateway->config = encrypt(json_encode(['account_id' => $account->id]));
$company_gateway->save();
return redirect($link['url']);
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);
// }
}

View 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);
}
}

View File

@ -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);

View File

@ -63,9 +63,11 @@ class UserController extends BaseController
*/
public function __construct(UserRepository $user_repo)
{
parent::__construct();
$this->user_repo = $user_repo;
}
/**
@ -376,7 +378,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');

View File

@ -110,7 +110,6 @@ class Kernel extends HttpKernel
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
//\App\Http\Middleware\StartupCheck::class,
QueryLogging::class,
],
'shop' => [
@ -160,4 +159,28 @@ 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,
];
}

View File

@ -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'];

View 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,
]);
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -42,7 +42,7 @@ 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);
Auth::guard('contact')->login($client_contact, true);
return redirect()->to('client/dashboard');
}

View File

@ -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);

View File

@ -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;
}

View File

@ -27,6 +27,7 @@ class SetDb
*/
public function handle($request, Closure $next)
{
$error = [
'message' => 'Invalid Token',
'errors' => new stdClass,

View File

@ -34,13 +34,50 @@ 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(!MultiDB::findAndSetDbByDomain($query)){
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(!MultiDB::findAndSetDbByDomain($query)){
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);
}

View File

@ -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);
}

View File

@ -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.');
}
}

View 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 [
//
];
}
}

View File

@ -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'];

View File

@ -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']);

View 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 [];
}
}

View File

@ -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;
}
/**

View File

@ -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;

View File

@ -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;
}

View 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');
}
}

View File

@ -82,9 +82,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;

View File

@ -0,0 +1,467 @@
<?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\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'])->toArray();
$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;
})->toArray();
$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;
})->toArray();
$this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact){
$client_contact = $this->transformArrayOfKeys($client_contact, ['id', 'company_id', 'user_id',' client_id']);
return $client_contact;
})->toArray();
$this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token){
$client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['id', 'company_id', 'client_id']);
return $client_gateway_token;
})->toArray();
$this->export_data['clients'] = $this->company->clients->map(function ($client){
$client = $this->transformArrayOfKeys($client, ['id', 'company_id', 'user_id',' assigned_user_id', 'group_settings_id']);
return $client;
})->toArray();
$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;
})->toArray();
$this->export_data['company_tokens'] = $this->company->tokens->map(function ($token){
$token = $this->transformArrayOfKeys($token, ['company_id', 'account_id', 'user_id']);
return $token;
})->toArray();
$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;
})->toArray();
$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;
})->toArray();
$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;
})->toArray();
$this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit){
$credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
return $credit;
})->toArray();
$this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->toArray();
$this->export_data['documents'] = $this->company->documents->map(function ($document){
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id']);
return $document;
})->toArray();
$this->export_data['expense_categories'] = $this->company->expenses->map(function ($expense_category){
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
return $expense_category;
})->toArray();
$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;
})->toArray();
$this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs){
$gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
return $gs;
})->toArray();
$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;
})->toArray();
$this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice){
$invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
return $invoice;
})->toArray();
$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'])->toArray();
$this->export_data['paymentables'] = $this->company->payments()->with('paymentables')->cursor()->map(function ($paymentable){
$paymentable = $this->transformArrayOfKeys($paymentable, ['payment_id','paymentable_id']);
return $paymentable;
})->toArray();
$this->export_data['payments'] = $this->company->payments->map(function ($payment){
$payment = $this->transformBasicEntities($payment);
$payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
return $payment;
})->toArray();
$this->export_data['projects'] = $this->company->projects->map(function ($project){
$project = $this->transformBasicEntities($project);
$project = $this->transformArrayOfKeys($project, ['client_id']);
return $project;
})->toArray();
$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;
})->toArray();
$this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote){
$quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
return $quote;
})->toArray();
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices->map(function ($ri){
$ri = $this->transformBasicEntities($ri);
$ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
return $ri;
})->toArray();
$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;
})->toArray();
$this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription){
$subscription = $this->transformBasicEntities($subscription);
$subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
return $subscription;
})->toArray();
$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'])->toArray();
$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;
})->toArray();
$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;
})->toArray();
$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'])->toArray();
$this->export_data['vendors'] = $this->company->vendors->map(function ($vendor){
return $this->transformBasicEntities($vendor);
})->toArray();
$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;
})->toArray();
$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'])->toArray();
//write to tmp and email to owner();
$this->zipAndSend();
return true;
}
private function transformBasicEntities($model)
{
return $this->transformArrayOfKeys($model, ['id', '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 zipAndSend()
{
$tempStream = fopen('php://memory', 'w+');
$options = new Archive();
$options->setOutputStream($tempStream);
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
$zip = new ZipStream($file_name, $options);
$fp = tmpfile();
fwrite($fp, json_encode($this->export_data));
rewind($fp);
$zip->addFileFromStream('backup.json', $fp);
$zip->finish();
$path = 'backups/';
Storage::disk(config('filesystems.default'))->put($path.$file_name, $tempStream);
fclose($tempStream);
$nmo = new NinjaMailerObject;
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url($path.$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'), $path.$file_name)->delay(now()->addHours(1));
}
}

View File

@ -0,0 +1,199 @@
<?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\Exceptions\NonExistingMigrationFile;
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\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 Illuminate\Support\Str;
use ZipArchive;
use ZipStream\Option\Archive;
use ZipStream\ZipStream;
class CompanyImport implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
protected $current_app_version;
public $company;
private $account;
public $file_path;
private $backup_file;
public $import_company;
public $ids = [];
private $options = '';
private $importables = [
'company',
'users',
// 'payment_terms',
// 'tax_rates',
// 'clients',
// 'company_gateways',
// 'client_gateway_tokens',
// 'vendors',
// 'projects',
// 'products',
// 'credits',
// 'invoices',
// 'recurring_invoices',
// 'quotes',
// 'payments',
// 'expense_categories',
// 'task_statuses',
// 'expenses',
// 'tasks',
// 'documents',
];
/**
* Create a new job instance.
*
* @param Company $company
* @param User $user
* @param string $custom_token_name
*/
public function __construct(Company $company, string $file_path, array $options)
{
$this->company = $company;
$this->file_path = $file_path;
$this->options = $options;
$this->current_app_version = config('ninja.app_version');
}
public function handle()
{
MultiDB::setDb($this->company->db);
$this->company =Company::where('company_key', $this->company->company_key)->firstOrFail();
$this->account = $this->company->account;
$this->unzipFile()
->preFlightChecks();
foreach($this->importables as $import){
$method = Str::ucfirst(Str::camel($import));
$this->{$method}();
}
}
//check if this is a complete company import OR if it is selective
/*
Company and settings only
Data
*/
private function preFlightChecks()
{
//check the file version and perform any necessary adjustments to the file in order to proceed - needed when we change schema
if($this->current_app_version != $this->backup_file->app_version)
{
//perform some magic here
}
return $this;
}
private function unzipFile()
{
$zip = new ZipArchive();
$archive = $zip->open(public_path("storage/backups/{$this->file_path}"));
$filename = pathinfo($this->filepath, 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.');
}
$this->backup_file = json_decode(file_get_contents($file_location));
return $this;
}
private function importCompany()
{
//$this->import_company = ..
return $this;
}
private function importUsers()
{
User::unguard();
foreach ($this->backup_file->users as $user)
{
$new_user = User::firstOrNew(
['email' => $user->email],
(array)$user,
);
$new_user->account_id = $this->account->id;
$new_user->save(['timestamps' => false]);
$this->ids['users']["{$user->id}"] = $new_user->id;
}
Expense::reguard();
}
public function transformId(string$resource, string $old): int
{
if (! array_key_exists($resource, $this->ids)) {
throw new \Exception("Resource {$resource} not available.");
}
if (! array_key_exists("{$old}", $this->ids[$resource])) {
throw new \Exception("Missing resource key: {$old}");
}
return $this->ids[$resource]["{$old}"];
}
}

View File

@ -64,7 +64,7 @@ class CreateEntityPdf implements ShouldQueue
*
* @param $invitation
*/
public function __construct($invitation)
public function __construct($invitation, $disk = 'public')
{
$this->invitation = $invitation;
@ -86,7 +86,9 @@ 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()
@ -197,7 +199,7 @@ class CreateEntityPdf implements ShouldQueue
catch(\Exception $e)
{
throw new FilePermissionsFailure('Could not write the PDF, permission issues!');
throw new FilePermissionsFailure($e->getMessage());
}
}
@ -209,8 +211,5 @@ class CreateEntityPdf implements ShouldQueue
{
}
// public function failed(\Exception $exception)
// {
// nlog("help!");
// }
}

View File

@ -97,7 +97,7 @@ class CSVImport implements ShouldQueue {
Auth::login( $this->company->owner(), true );
$this->company->owner()->setCompany( $this->company );
auth()->user()->setCompany($this->company);
$this->buildMaps();

View File

@ -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));
}
}

View File

@ -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;
@ -54,7 +55,11 @@ class NinjaMailerJob implements ShouldQueue
public $nmo;
public function __construct(NinjaMailerObject $nmo)
public $override;
public $company;
public function __construct(NinjaMailerObject $nmo, bool $override = false)
{
$this->nmo = $nmo;
@ -64,12 +69,15 @@ class NinjaMailerJob implements ShouldQueue
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 +91,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');
@ -104,10 +116,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);
}
}
@ -169,7 +183,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 +224,8 @@ class NinjaMailerJob implements ShouldQueue
SystemLog::CATEGORY_MAIL,
SystemLog::EVENT_MAIL_SEND,
SystemLog::TYPE_FAILURE,
$recipient_object
$recipient_object,
$this->nmo->company
);
}

View File

@ -56,9 +56,6 @@ class UserEmailChanged implements ShouldQueue
{
nlog("notifying user of email change");
if ($this->company->is_disabled)
return true;
//Set DB
MultiDB::setDb($this->company->db);
@ -78,7 +75,7 @@ class UserEmailChanged implements ShouldQueue
$nmo->company = $this->company;
$nmo->to_user = $this->old_user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
// $nmo->to_user = $this->new_user;
// NinjaMailerJob::dispatch($nmo);

View File

@ -17,6 +17,7 @@ use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration;
use App\Factory\ClientContactFactory;
use App\Factory\ClientFactory;
use App\Factory\CompanyLedgerFactory;
use App\Factory\CreditFactory;
@ -209,6 +210,9 @@ class Import implements ShouldQueue
$this->{$method}($data[$import]);
}
if(Ninja::isHosted())
$this->processNinjaTokens($data['ninja_tokens']);
$this->setInitialCompanyLedgerBalances();
// $this->fixClientBalances();
@ -1261,7 +1265,7 @@ class Import implements ShouldQueue
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
}
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
else if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
$modified['fees_and_limits'] = [];
}
@ -1293,6 +1297,8 @@ class Import implements ShouldQueue
$modified['company_id'] = $this->company->id;
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
$modified['company_gateway_id'] = $this->transformId('company_gateways', $resource['company_gateway_id']);
//$modified['user_id'] = $this->processUserId($resource);
$cgt = ClientGatewayToken::Create($modified);
@ -1612,6 +1618,9 @@ class Import implements ShouldQueue
->batch();
info(print_r($exception->getMessage(), 1));
if(Ninja::isHosted())
app('sentry')->captureException($exception);
}
@ -1636,6 +1645,64 @@ class Import implements ShouldQueue
return $response->getBody();
}
private function buildNewUserPlan()
{
$local_company = Company::find($this->company->id);
$owner = $local_company->owner();
$ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id'));
/* If we already have a record of this user - move along. */
if($client_contact = ClientContact::on('db-ninja-01')->where(['email' => $owner->email, 'company_id' => $ninja_company->id])->exists())
return $client_contact->client;
$ninja_client = ClientFactory::create($ninja_company->id, $ninja_company->owner()->id);
$ninja_client->setConnection('db-ninja-01');
$ninja_client->name = $owner->present()->name();
$ninja_client->address1 = $local_company->settings->address1;
$ninja_client->address2 = $local_company->settings->address2;
$ninja_client->city = $local_company->settings->city;
$ninja_client->postal_code = $local_company->settings->postal_code;
$ninja_client->state = $local_company->settings->state;
$ninja_client->country_id = $local_company->settings->country_id;
$ninja_client->save();
$ninja_client_contact = ClientContactFactory::create($ninja_company->id, $ninja_company->owner()->id);
$ninja_client_contact->setConnection('db-ninja-01');
$ninja_client_contact->first_name = $owner->first_name;
$ninja_client_contact->last_name = $owner->last_name;
$ninja_client_contact->client_id = $ninja_client->id;
$ninja_client_contact->email = $owner->email;
$ninja_client_contact->phone = $owner->phone;
$ninja_client_contact->save();
return $ninja_client;
}
private function processNinjaTokens(array $data)
{
if(count($data) == 0)
$ninja_client = $this->buildNewUserPlan();
foreach($data as $token)
{
//get invoiceninja company_id
$ninja_company = Company::on('db-ninja-01')->where('id', config('ninja.ninja_default_company_id'))->first();
$token['company_id'] = $ninja_client->company_id;
$token['client_id'] = $ninja_client->id;
$token['user_id'] = $ninja_client->user_id;
$token['company_gateway_id'] = config('ninja.ninja_default_company_gateway_id');
//todo
ClientGatewayToken::unguard();
$cgt = ClientGatewayToken::Create($token);
ClientGatewayToken::reguard();
}
}
/* In V4 we use negative invoices (credits) and add then into the client balance. In V5, these sit off ledger and are applied later.
This next section will check for credit balances and reduce the client balance so that the V5 balances are correct

View File

@ -0,0 +1,68 @@
<?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\Util;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\CompanyGateway;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ImportStripeCustomers implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $company;
private $stripe_keys = ['d14dd26a47cecc30fdd65700bfb67b34', 'd14dd26a37cecc30fdd65700bfb55b23'];
/**
* Create a new job instance.
*
* @param $event_id
* @param $entity
*/
public function __construct($company)
{
$this->company = $company;
}
/**
* Execute the job.
*
* @return bool
*/
public function handle()
{
MultiDB::setDb($this->company->db);
$cgs = CompanyGateway::where('company_id', $this->company->id)
->whereIn('gateway_key', $this->stripe_keys)
->get();
$cgs->each(function ($company_gateway){
$company_gateway->driver(new Client)->importCustomers();
});
}
public function failed($exception)
{
nlog("Stripe import customer methods exception");
nlog($exception->getMessage());
}
}

View File

@ -12,6 +12,7 @@
namespace App\Jobs\Util;
use App\Events\Invoice\InvoiceWasEmailed;
use App\Jobs\Entity\EmailEntity;
use App\Libraries\MultiDB;
use App\Models\Invoice;
use App\Utils\Ninja;

View File

@ -139,7 +139,7 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag;
$this->company->save();
Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
Mail::to($this->user->email, $this->user->name())->send(new MigrationFailed($e, $e->getMessage()));
if (app()->environment() !== 'production') {
info($e->getMessage());

View File

@ -0,0 +1,68 @@
<?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\Util;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\CompanyGateway;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class StripeUpdatePaymentMethods implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $company;
private $stripe_keys = ['d14dd26a47cecc30fdd65700bfb67b34', 'd14dd26a37cecc30fdd65700bfb55b23'];
/**
* Create a new job instance.
*
* @param $event_id
* @param $entity
*/
public function __construct($company)
{
$this->company = $company;
}
/**
* Execute the job.
*
* @return bool
*/
public function handle()
{
MultiDB::setDb($this->company->db);
$cgs = CompanyGateway::where('company_id', $this->company->id)
->whereIn('gateway_key', $this->stripe_keys)
->get();
$cgs->each(function ($company_gateway){
$company_gateway->driver(new Client)->updateAllPaymentMethods();
});
}
public function failed($exception)
{
nlog("Stripe update payment methods exception");
nlog($exception->getMessage());
}
}

View File

@ -11,7 +11,9 @@
namespace App\Jobs\Util;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Models\SystemLog;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -33,24 +35,32 @@ class SystemLogger implements ShouldQueue
protected $client;
public function __construct($log, $category_id, $event_id, $type_id, ?Client $client)
protected $company;
public function __construct($log, $category_id, $event_id, $type_id, ?Client $client, Company $company)
{
$this->log = $log;
$this->category_id = $category_id;
$this->event_id = $event_id;
$this->type_id = $type_id;
$this->client = $client;
$this->company = $company;
}
public function handle() :void
{
if(!$this->client)
if(!$this->company)
return;
MultiDB::setDb($this->company->db);
$client_id = $this->client ? $this->client->id : null;
$user_id = $this->client ? $this->client->user_id : $this->company->owner()->id;
$sl = [
'client_id' => $this->client->id,
'company_id' => $this->client->company->id,
'user_id' => $this->client->user_id,
'client_id' => $client_id,
'company_id' => $this->company->id,
'user_id' => $user_id,
'log' => $this->log,
'category_id' => $this->category_id,
'event_id' => $this->event_id,

View File

@ -39,8 +39,6 @@ class UnlinkFile implements ShouldQueue
*/
public function handle()
{
// nlog("deleting");
// nlog($this->file_path);
Storage::disk($this->disk)->delete($this->file_path);
}
}

View File

@ -123,6 +123,7 @@ class WebhookHandler implements ShouldQueue
SystemLog::EVENT_WEBHOOK_RESPONSE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
$this->company->clients->first(),
$this->company
);
}
@ -136,6 +137,7 @@ class WebhookHandler implements ShouldQueue
SystemLog::EVENT_WEBHOOK_RESPONSE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
$this->company->clients->first(),
$this->company,
);
}

View File

@ -56,6 +56,7 @@ class MultiDB
return Company::whereSubdomain($subdomain)->get()->count() == 0;
}
//multi-db active
foreach (self::$dbs as $db) {
if (Company::on($db)->whereSubdomain($subdomain)->get()->count() >= 1) {
@ -63,7 +64,7 @@ class MultiDB
}
}
self::setDefaultDatabase();
//self::setDefaultDatabase();
return true;
}
@ -129,13 +130,12 @@ class MultiDB
}
foreach (self::$dbs as $db) {
self::setDB($db);
$user = User::where($data)->withTrashed()->first();
if ($user) {
if ($user = User::where($data)->withTrashed()->first())
return $user;
}
}
self::setDefaultDatabase();
@ -147,18 +147,18 @@ class MultiDB
* @param array $data
* @return User|null
*/
public static function hasContact(array $data) : ?ClientContact
public static function hasContact(string $email) : ?ClientContact
{
if (! config('ninja.db.multi_db_enabled')) {
return ClientContact::where($data)->withTrashed()->first();
return ClientContact::where('email', $email)->withTrashed()->first();
}
foreach (self::$dbs as $db) {
self::setDB($db);
$user = ClientContacts::where($data)->withTrashed()->first();
$user = ClientContact::on($db)->where('email', $email)->withTrashed()->first();
if ($user) {
self::setDB($db);
return $user;
}
}
@ -189,12 +189,15 @@ class MultiDB
//multi-db active
foreach (self::$dbs as $db) {
if (User::on($db)->where(['email' => $email])->count() >= 1)
if (User::on($db)->where('email', $email)->count() >= 1){
nlog("setting db {$db}");
self::setDb($db);
return true;
}
}
self::setDefaultDatabase();
self::setDefaultDatabase();
return false;
}
@ -203,7 +206,6 @@ class MultiDB
foreach (self::$dbs as $db) {
if ($ct = CompanyToken::on($db)->whereRaw('BINARY `token`= ?', [$token])->first()) {
self::setDb($ct->company->db);
return true;
}
}
@ -252,14 +254,14 @@ class MultiDB
return false;
}
public static function findAndSetDbByDomain($subdomain) :bool
public static function findAndSetDbByDomain($query_array) :bool
{
if (! config('ninja.db.multi_db_enabled'))
return (Company::whereSubdomain($subdomain)->exists() === true);
return (Company::where($query_array)->exists() === true);
foreach (self::$dbs as $db) {
if ($company = Company::on($db)->whereSubdomain($subdomain)->first()) {
if ($company = Company::on($db)->where($query_array)->first()) {
self::setDb($company->db);
return true;
}

View File

@ -43,6 +43,9 @@ class InvoiceEmailFailedActivity implements ShouldQueue
MultiDB::setDb($event->company->db);
if(strpos($event->message, 'shared/public') !== false)
$event->message = "Unable to open attachment file for reading";
$fields = new stdClass;
$fields->invoice_id = $event->invitation->invoice->id;

View File

@ -67,12 +67,15 @@ class UpdateUserLastLogin implements ShouldQueue
$user->save();
}
$arr = json_encode(['ip' => $ip]);
SystemLogger::dispatch(
$ip,
$arr,
SystemLog::CATEGORY_SECURITY,
SystemLog::EVENT_USER,
SystemLog::TYPE_LOGIN_SUCCESS,
$event->company->clients()->first(),
null,
$event->company,
);
}

View File

@ -27,8 +27,6 @@ class ContactPasswordlessLogin extends Mailable
*/
public $email;
public $url = 'https://google.com';
/**
* Create a new message instance.
*
@ -49,6 +47,8 @@ class ContactPasswordlessLogin extends Mailable
*/
public function build()
{
return $this->view('email.billing.passwordless-login');
return $this
->subject(ctrans('texts.account_passwordless_login'))
->view('email.billing.passwordless-login');
}
}

View File

@ -0,0 +1,53 @@
<?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\Mail;
use App\Models\Company;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class DownloadBackup extends Mailable
{
public $file_path;
public $company;
public function __construct($file_path, Company $company)
{
$this->file_path = $file_path;
$this->company = $company;
}
/**
* Build the message.
*/
public function build()
{
$company = Company::where('company_key', $this->company->company_key)->first();
return $this->from(config('mail.from.address'), config('mail.from.name'))
->subject(ctrans('texts.download_backup_subject', ['company' => $company->present()->name()]))
->markdown(
'email.admin.download_files',
[
'url' => $this->file_path,
'logo' => $company->present()->logo,
'whitelabel' => $company->account->isPaid() ? true : false,
'settings' => $company->settings,
'greeting' => $company->present()->name(),
]
);
}
}

View File

@ -95,9 +95,12 @@ class CreditEmailEngine extends BaseEmailEngine
->setInvitation($this->invitation);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$this->setAttachments([$this->credit->pdf_file_path()]);
// $this->setAttachments(['path' => $this->credit->pdf_file_path(), 'name' => basename($this->credit->pdf_file_path())]);
if(Ninja::isHosted())
$this->setAttachments([$this->credit->pdf_file_path(null, 'url', true)]);
else
$this->setAttachments([$this->credit->pdf_file_path()]);
}
//attach third party documents

View File

@ -106,7 +106,12 @@ class InvoiceEmailEngine extends BaseEmailEngine
->setInvitation($this->invitation);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$this->setAttachments([$this->invoice->pdf_file_path()]);
if(Ninja::isHosted())
$this->setAttachments([$this->invoice->pdf_file_path(null, 'url', true)]);
else
$this->setAttachments([$this->invoice->pdf_file_path()]);
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
}

View File

@ -12,6 +12,7 @@
namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults;
use App\Models\Account;
use App\Utils\Helpers;
use App\Utils\Number;
use App\Utils\Traits\MakesDates;
@ -72,6 +73,16 @@ class PaymentEmailEngine extends BaseEmailEngine
->setViewLink('')
->setViewText('');
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$this->payment->invoices->each(function ($invoice){
$this->setAttachments([$invoice->pdf_file_path()]);
});
}
return $this;
}

View File

@ -97,8 +97,11 @@ class QuoteEmailEngine extends BaseEmailEngine
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$this->setAttachments([$this->quote->pdf_file_path()]);
//$this->setAttachments(['path' => $this->quote->pdf_file_path(), 'name' => basename($this->quote->pdf_file_path())]);
if(Ninja::isHosted())
$this->setAttachments([$this->quote->pdf_file_path(null, 'url', true)]);
else
$this->setAttachments([$this->quote->pdf_file_path()]);
}

View File

@ -107,7 +107,7 @@ class TemplateEmail extends Mailable
});
//conditionally attach files
if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) {
// if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) {
//hosted | plan check here
foreach ($this->build_email->getAttachments() as $file) {
@ -118,7 +118,7 @@ class TemplateEmail extends Mailable
$this->attach($file['path'], ['as' => $file['name'], 'mime' => $file['mime']]);
}
}
// }
return $this;
}

View File

@ -13,12 +13,14 @@ namespace App\Models;
use App\DataMapper\ClientSettings;
use App\DataMapper\CompanySettings;
use App\Jobs\Entity\CreateEntityPdf;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
/**
@ -199,4 +201,5 @@ class BaseModel extends Model
return $formatted_number;
}
}

View File

@ -12,6 +12,7 @@
namespace App\Models;
use App\Models\Presenters\CompanyPresenter;
use App\Models\User;
use App\Services\Notification\NotificationService;
use App\Utils\Ninja;
use App\Utils\Traits\CompanySettingsSaver;
@ -20,8 +21,8 @@ use App\Utils\Traits\ThrottlesEmail;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Notifications\Notification;
use Laracasts\Presenter\PresentableTrait;
use Illuminate\Support\Facades\Cache;
use Laracasts\Presenter\PresentableTrait;
class Company extends BaseModel
{
@ -150,6 +151,11 @@ class Company extends BaseModel
return $this->belongsTo(Account::class);
}
public function client_contacts()
{
return $this->hasMany(ClientContact::class)->withTrashed();
}
public function users()
{
return $this->hasManyThrough(User::class, CompanyUser::class, 'company_id', 'id', 'id', 'user_id');
@ -203,6 +209,12 @@ class Company extends BaseModel
return $this->hasMany(Vendor::class)->withTrashed();
}
public function all_activities()
{
return $this->hasMany(Activity::class);
}
public function activities()
{
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300);
@ -301,11 +313,21 @@ class Company extends BaseModel
return $this->hasMany(Design::class)->whereCompanyId($this->id)->orWhere('company_id', null);
}
public function user_designs()
{
return $this->hasMany(Design::class);
}
public function payment_terms()
{
return $this->hasMany(PaymentTerm::class)->whereCompanyId($this->id)->orWhere('company_id', null);
}
public function user_payment_terms()
{
return $this->hasMany(PaymentTerm::class);
}
/**
* @return BelongsTo
*/
@ -411,9 +433,7 @@ class Company extends BaseModel
public function owner()
{
$c = $this->company_users->where('is_owner', true)->first();
return User::find($c->user_id);
return $this->company_users->where('is_owner', true)->first()->user;
}
public function resolveRouteBinding($value, $field = null)
@ -423,12 +443,12 @@ class Company extends BaseModel
public function domain()
{
if (Ninja::isNinja()) {
if (Ninja::isHosted()) {
if($this->portal_mode == 'domain')
return $this->portal_domain;
return "https://{$this->subdomain}" . config('ninja.app_domain');
return "https://{$this->subdomain}." . config('ninja.app_domain');
}
return config('ninja.app_url');

View File

@ -309,14 +309,14 @@ class CompanyGateway extends BaseModel
$fees_and_limits = $this->getFeesAndLimits($gateway_type_id);
if (! $fees_and_limits) {
return 0;
return false;
}
$fee = 0;
if ($fees_and_limits->fee_amount) {
$fee += $fees_and_limits->fee_amount;
// info("fee after adding fee amount = {$fee}");
nlog("fee after adding fee amount = {$fee}");
}
if ($fees_and_limits->fee_percent) {
@ -325,7 +325,7 @@ class CompanyGateway extends BaseModel
} else {
$fee += round(($amount * $fees_and_limits->fee_percent / 100), 2);
}
// info("fee after adding fee percent = {$fee}");
nlog("fee after adding fee percent = {$fee}");
}
/* Cap fee if we have to here. */
@ -334,6 +334,7 @@ class CompanyGateway extends BaseModel
}
$pre_tax_fee = $fee;
nlog("fee after adding fee percent = {$fee}");
/**/
if ($include_taxes) {
@ -352,6 +353,7 @@ class CompanyGateway extends BaseModel
// info("fee after adding fee tax 3 = {$fee}");
}
}
nlog("fee after adding fee percent = {$fee}");
return $fee;
}

View File

@ -251,23 +251,37 @@ class Credit extends BaseModel
$this->save();
}
public function pdf_file_path($invitation = null)
public function pdf_file_path($invitation = null, string $type = 'path', bool $portal = false)
{
$storage_path = Storage::url($this->client->credit_filepath().$this->numberFormatter().'.pdf');
if (Storage::exists($this->client->credit_filepath().$this->numberFormatter().'.pdf')) {
return $storage_path;
}
if (! $invitation) {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($this->invitations->first());
} else {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($invitation);
if($this->invitations()->exists())
$invitation = $this->invitations()->first();
else{
$this->service()->createInvitations();
$invitation = $this->invitations()->first();
}
}
return $storage_path;
if(!$invitation)
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
$file_path = $this->client->credit_filepath().$this->numberFormatter().'.pdf';
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
elseif(Ninja::isHosted() && $portal){
$file_path = CreateEntityPdf::dispatchNow($invitation,config('filesystems.default'));
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
if(Storage::disk('public')->exists($file_path))
return Storage::disk('public')->{$type}($file_path);
$file_path = CreateEntityPdf::dispatchNow($invitation);
return Storage::disk('public')->{$type}($file_path);
}
public function markInvitationsSent()

View File

@ -96,4 +96,9 @@ class Expense extends BaseModel
{
return $this->belongsTo(Vendor::class);
}
public function client()
{
return $this->belongsTo(Client::class);
}
}

View File

@ -392,7 +392,7 @@ class Invoice extends BaseModel
return $invoice_calc->build();
}
public function pdf_file_path($invitation = null, string $type = 'url')
public function pdf_file_path($invitation = null, string $type = 'path', bool $portal = false)
{
if (! $invitation) {
@ -408,19 +408,23 @@ class Invoice extends BaseModel
if(!$invitation)
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
$storage_path = Storage::$type($this->client->invoice_filepath().$this->numberFormatter().'.pdf');
$file_path = $this->client->invoice_filepath().$this->numberFormatter().'.pdf';
if (! Storage::exists($this->client->invoice_filepath().$this->numberFormatter().'.pdf')) {
event(new InvoiceWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($invitation);
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
elseif(Ninja::isHosted() && $portal){
$file_path = CreateEntityPdf::dispatchNow($invitation,config('filesystems.default'));
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
return $storage_path;
if(Storage::disk('public')->exists($file_path))
return Storage::disk('public')->{$type}($file_path);
$file_path = CreateEntityPdf::dispatchNow($invitation);
return Storage::disk('public')->{$type}($file_path);
}
/**
* Updates Invites to SENT.
*/
public function markInvitationsSent()
{
$this->invitations->each(function ($invitation) {

View File

@ -207,26 +207,39 @@ class Quote extends BaseModel
}
public function pdf_file_path($invitation = null, string $type = 'url')
public function pdf_file_path($invitation = null, string $type = 'path', bool $portal = false)
{
if (! $invitation) {
$invitation = $this->invitations->first();
if($this->invitations()->exists())
$invitation = $this->invitations()->first();
else{
$this->service()->createInvitations();
$invitation = $this->invitations()->first();
}
}
$storage_path = Storage::$type($this->client->quote_filepath().$this->numberFormatter().'.pdf');
if(!$invitation)
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
nlog($storage_path);
$file_path = $this->client->quote_filepath().$this->numberFormatter().'.pdf';
if (! Storage::exists($this->client->quote_filepath().$this->numberFormatter().'.pdf')) {
event(new QuoteWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($invitation);
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
elseif(Ninja::isHosted() && $portal){
$file_path = CreateEntityPdf::dispatchNow($invitation,config('filesystems.default'));
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
}
return $storage_path;
if(Storage::disk('public')->exists($file_path))
return Storage::disk('public')->{$type}($file_path);
$file_path = CreateEntityPdf::dispatchNow($invitation);
return Storage::disk('public')->{$type}($file_path);
}
/**
* @param int $status
* @return string

View File

@ -159,7 +159,7 @@ class User extends Authenticatable implements MustVerifyEmail
*/
public function setCompany($company)
{
config(['ninja.company_id' => $company->id]);
// config(['ninja.company_id' => $company->id]);
$this->company = $company;
}
@ -169,16 +169,29 @@ class User extends Authenticatable implements MustVerifyEmail
*/
public function getCompany()
{
if ($this->company) {
return $this->company;
}
if (request()->header('X-API-TOKEN')) {
$company_token = CompanyToken::whereRaw('BINARY `token`= ?', [request()->header('X-API-TOKEN')])->first();
$company_token = CompanyToken::with(['company'])->whereRaw('BINARY `token`= ?', [request()->header('X-API-TOKEN')])->first();
return $company_token->company;
}
elseif ($this->company){
return Company::find(config('ninja.company_id'));
return $this->company;
}
// return false;
throw new \Exception('No Company Found');
//return Company::find(config('ninja.company_id'));
}
public function companyIsSet()
{
if($this->company)
return true;
return false;
}
/**

View File

@ -136,7 +136,7 @@ class AuthorizeCreditCard
PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $amount);
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
return false;
}
@ -192,7 +192,8 @@ class AuthorizeCreditCard
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_AUTHORIZE,
$this->authorize->client
$this->authorize->client,
$this->authorize->client->company,
);
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);

View File

@ -86,7 +86,7 @@ class RefundTransaction
'amount' => $amount,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
return $data;
@ -105,7 +105,7 @@ class RefundTransaction
'amount' => $amount,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
return $data;
}
@ -125,7 +125,7 @@ class RefundTransaction
'amount' => $amount,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
return $data;
@ -141,7 +141,7 @@ class RefundTransaction
'amount' => $amount,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
return $data;
}
@ -158,7 +158,7 @@ class RefundTransaction
'amount' => $amount,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
return $data;
}
@ -173,7 +173,7 @@ class RefundTransaction
'amount' => $amount,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
}
}

View File

@ -394,6 +394,7 @@ class BaseDriver extends AbstractPaymentDriver
SystemLog::EVENT_GATEWAY_ERROR,
$gateway::SYSTEM_LOG_TYPE,
$gateway->client,
$gateway->client->company,
);
throw new PaymentFailed($error, $e->getCode());
@ -527,6 +528,7 @@ class BaseDriver extends AbstractPaymentDriver
SystemLog::EVENT_GATEWAY_SUCCESS,
$gateway_const,
$this->client,
$this->client->company,
);
}
}

View File

@ -149,7 +149,8 @@ class CreditCard
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_BRAINTREE,
$this->braintree->client
$this->braintree->client,
$this->braintree->client->company,
);
return redirect()->route('client.payments.show', ['payment' => $this->braintree->encodePrimaryKey($payment->id)]);
@ -179,7 +180,8 @@ class CreditCard
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_BRAINTREE,
$this->braintree->client
$this->braintree->client,
$this->braintree->client->company,
);
throw new PaymentFailed($response->transaction->additionalProcessorResponse, $response->transaction->processorResponseCode);

View File

@ -76,7 +76,8 @@ trait Utilities
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_CHECKOUT,
$this->getParent()->client
$this->getParent()->client,
$this->getParent()->client->company
);
return redirect()->route('client.payments.show', ['payment' => $this->getParent()->encodePrimaryKey($payment->id)]);
@ -101,7 +102,8 @@ trait Utilities
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_CHECKOUT,
$this->getParent()->client
$this->getParent()->client,
$this->getParent()->client->company,
);
if ($throw_exception) {

View File

@ -282,7 +282,7 @@ class CheckoutComPaymentDriver extends BaseDriver
'message' => $message,
];
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client);
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client, $this->client->company);
}
}

Some files were not shown because too many files have changed in this diff Show More