Merge pull request #7025 from turbo124/v5-develop

Duplicate Quote Approved Notification fix
This commit is contained in:
David Bomba 2021-12-11 21:13:18 +11:00 committed by GitHub
commit a541207438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 371 additions and 175 deletions

View File

@ -94,6 +94,12 @@ class PaymentFilters extends QueryFilters
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
public function number(string $number) : Builder
{
return $this->builder->where('number', $number);
}
/**
* Returns the base query.
*

View File

@ -58,16 +58,20 @@ class ContactForgotPasswordController extends Controller
*/
public function showLinkRequestForm(Request $request)
{
$account_id = $request->has('account_id') ? $request->get('account_id') : 1;
$account = Account::find($account_id);
if($request->has('company_key'))
$company = Company::where('company_key', $request->input('company_key'))->first();
else
$company = $account->companies->first();
$account = false;
if(!$account)
if(Ninja::isHosted() && $request->session()->has('company_key'))
{
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$company = Company::where('company_key', $request->session()->get('company_key'))->first();
$account = $company->account;
}
if(!$account){
$account = Account::first();
$company = $account->companies->first();
}
return $this->render('auth.passwords.request', [
'title' => 'Client Password Reset',
@ -89,17 +93,15 @@ class ContactForgotPasswordController extends Controller
public function sendResetLinkEmail(ContactPasswordResetRequest $request)
{
if(Ninja::isHosted() && $request->has('company_key'))
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
$this->validateEmail($request);
// $user = MultiDB::hasContact($request->input('email'));
$company = Company::where('company_key', $request->input('company_key'))->first();
//$contact = MultiDB::findContact(['company_id' => $company->id, 'email' => $request->input('email')]);
nlog(['company_id' => $company->id, 'email' => $request->input('email')]);
$contact = ClientContact::where(['company_id' => $company->id, 'email' => $request->input('email')])->first();
// $company = Company::where('company_key', $request->input('company_key'))->first();
// $contact = ClientContact::where(['company_id' => $company->id, 'email' => $request->input('email')])->first();
$contact = ClientContact::where(['email' => $request->input('email')])->first();
$response = false;
@ -108,20 +110,13 @@ class ContactForgotPasswordController extends Controller
/* Update all instances of the client */
$token = Str::random(60);
ClientContact::where('email', $contact->email)->update(['token' => $token]);
$contact->sendPasswordResetNotification($token);
$response = Password::RESET_LINK_SENT;
}
else
return $this->sendResetLinkFailedResponse($request, Password::INVALID_USER);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
// $response = $this->broker()->sendResetLink(
// $this->credentials($request)
// );
if ($request->ajax()) {
if($response == Password::RESET_THROTTLED)

View File

@ -40,8 +40,8 @@ class ContactLoginController extends Controller
$company = false;
$account = false;
if($request->has('company_key')){
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
if($request->session()->has('company_key')){
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$company = Company::where('company_key', $request->input('company_key'))->first();
}
@ -80,8 +80,8 @@ class ContactLoginController extends Controller
{
Auth::shouldUse('contact');
if(Ninja::isHosted() && $request->has('company_key'))
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
if(Ninja::isHosted() && $request->session()->has('company_key'))
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle

View File

@ -29,7 +29,7 @@ class ContactRegisterController extends Controller
public function showRegisterForm(string $company_key = '')
{
$key = request()->has('key') ? request('key') : $company_key;
$key = request()->session()->has('key') ? request()->session()->get('key') : $company_key;
$company = Company::where('company_key', $key)->firstOrFail();

View File

@ -15,6 +15,7 @@ use App\Http\Controllers\Controller;
use App\Libraries\MultiDB;
use App\Models\Account;
use App\Models\ClientContact;
use App\Models\Company;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\View\Factory;
use Illuminate\Foundation\Auth\ResetsPasswords;
@ -68,20 +69,44 @@ class ContactResetPasswordController extends Controller
*/
public function showResetForm(Request $request, $token = null)
{
$account_id = $request->has('account_id') ? $request->get('account_id') : 1;
$account = Account::find($account_id);
$db = $account->companies->first()->db;
$company = $account->companies->first();
if($request->session()->has('company_key')){
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$company = Company::where('company_key', $request->session()->get('company_key'))->first();
$db = $company->db;
$account = $company->account;
}
else {
$account_key = $request->session()->has('account_key') ? $request->session()->get('account_key') : false;
if($account_key){
MultiDB::findAndSetDbByAccountKey($account_key);
$account = Account::where('key', $account_key)->first();
$db = $account->companies->first()->db;
$company = $account->companies->first();
}
else{
$account = Account::first();
$db = $account->companies->first()->db;
$company = $account->companies->first();
}
}
return $this->render('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email, 'account' => $account, 'db' => $db, 'company' => $company]
);
}
public function reset(Request $request)
{
if($request->has('company_key'))
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
if($request->session()->has('company_key'))
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$request->validate($this->rules(), $this->validationErrorMessages());

View File

@ -79,8 +79,15 @@ class ForgotPasswordController extends Controller
public function showLinkRequestForm(Request $request)
{
$account_id = $request->get('account_id');
$account = Account::find($account_id);
if($request->has('company_key')){
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
$company = Company::where('company_key', $request->input('company_key'))->first();
$account = $company->account;
}
else{
$account_id = $request->get('account_id');
$account = Account::find($account_id);
}
return $this->render('auth.passwords.request', ['root' => 'themes', 'account' => $account]);
}

View File

@ -102,7 +102,6 @@ class LoginController extends BaseController
*
* @return Response|User Process user login.
*
*
* @throws \Illuminate\Validation\ValidationException
* @OA\Post(
* path="/api/v1/login",
@ -176,7 +175,7 @@ class LoginController extends BaseController
LightLogs::create(new LoginSuccess())
->increment()
->batch();
->queue();
$user = $this->guard()->user();
@ -236,15 +235,6 @@ class LoginController extends BaseController
}
//method above override this
// $user->company_users->each(function ($company_user) use($request){
// if($company_user->tokens->count() == 0){
// CreateCompanyToken::dispatchNow($company_user->company, $company_user->user, $request->server('HTTP_USER_AGENT'));
// }
// });
/*On the hosted platform, only owners can login for free/pro accounts*/
if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
@ -258,7 +248,7 @@ class LoginController extends BaseController
LightLogs::create(new LoginFailure())
->increment()
->batch();
->queue();
// SystemLogger::dispatch(
// json_encode(['ip' => request()->getClientIp()]),
@ -395,13 +385,31 @@ class LoginController extends BaseController
$cu = CompanyUser::query()
->where('user_id', auth()->user()->id);
$cu->first()->account->companies->each(function ($company) use($cu){
// $cu->first()->account->companies->each(function ($company) use($cu){
if($company->tokens()->where('is_system', true)->count() == 0)
// if($company->tokens()->where('is_system', true)->count() == 0)
// {
// CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
// }
// });
if($existing_user->company_users()->count() != $existing_user->tokens()->count())
{
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
$existing_user->companies->each(function($company) use($existing_user){
if(!CompanyToken::where('user_id', $existing_user->id)->where('company_id', $company->id)->exists()){
CreateCompanyToken::dispatchNow($company, $existing_user, "Google_O_Auth");
}
});
}
});
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
@ -429,13 +437,30 @@ class LoginController extends BaseController
$cu = CompanyUser::query()
->where('user_id', auth()->user()->id);
$cu->first()->account->companies->each(function ($company) use($cu){
// $cu->first()->account->companies->each(function ($company) use($cu){
if($company->tokens()->where('is_system', true)->count() == 0)
// if($company->tokens()->where('is_system', true)->count() == 0)
// {
// CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
// }
// });
if($existing_login_user->company_users()->count() != $existing_login_user->tokens()->count())
{
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
$existing_login_user->companies->each(function($company) use($existing_login_user){
if(!CompanyToken::where('user_id', $existing_login_user->id)->where('company_id', $company->id)->exists()){
CreateCompanyToken::dispatchNow($company, $existing_login_user, "Google_O_Auth");
}
});
}
});
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
@ -467,13 +492,30 @@ class LoginController extends BaseController
$cu = CompanyUser::query()
->where('user_id', auth()->user()->id);
$cu->first()->account->companies->each(function ($company) use($cu){
// $cu->first()->account->companies->each(function ($company) use($cu){
if($company->tokens()->where('is_system', true)->count() == 0)
// if($company->tokens()->where('is_system', true)->count() == 0)
// {
// CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
// }
// });
if($existing_login_user->company_users()->count() != $existing_login_user->tokens()->count())
{
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
$existing_login_user->companies->each(function($company) use($existing_login_user){
if(!CompanyToken::where('user_id', $existing_login_user->id)->where('company_id', $company->id)->exists()){
CreateCompanyToken::dispatchNow($company, $existing_login_user, "Google_O_Auth");
}
});
}
});
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
@ -511,13 +553,30 @@ class LoginController extends BaseController
$cu = CompanyUser::whereUserId(auth()->user()->id);
$cu->first()->account->companies->each(function ($company) use($cu){
// $cu->first()->account->companies->each(function ($company) use($cu){
// if($company->tokens()->where('is_system', true)->count() == 0)
// {
// CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
// }
// });
if(auth()->user()->company_users()->count() != auth()->user()->tokens()->count())
{
auth()->user()->companies->each(function($company) {
if(!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()){
CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth");
}
});
}
if($company->tokens()->where('is_system', true)->count() == 0)
{
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
}
});
if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);

View File

@ -12,7 +12,9 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Libraries\MultiDB;
use App\Models\Account;
use App\Models\Company;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
@ -53,8 +55,14 @@ class ResetPasswordController extends Controller
public function showResetForm(Request $request, $token = null)
{
$account_id = $request->get('account_id');
$account = Account::find($account_id);
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
$company = Company::where('company_key', $request->session()->get('company_key'))->first();
if($company)
$account = $company->account;
else
$account = Account::first();
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account]);
}

View File

@ -42,6 +42,7 @@ class InvoiceController extends Controller
*/
public function index(ShowInvoicesRequest $request)
{
return $this->render('invoices.index');
}
@ -77,7 +78,7 @@ class InvoiceController extends Controller
if ($request->query('mode') === 'fullscreen') {
return render('invoices.show-fullscreen', $data);
}
// $request->fullUrlWithQuery(['q' => null]);
return $this->render('invoices.show', $data);
}
@ -189,9 +190,9 @@ class InvoiceController extends Controller
if ($invoices->count() == 1) {
$invoice = $invoices->first();
$invitation = $invoice->invitations->first();
//$file = $invoice->pdf_file_path($invitation);
$file = $invoice->service()->getInvoicePdf(auth()->user());
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
return response()->streamDownload(function () use($file) {
echo Storage::get($file);
}, basename($file), ['Content-Type' => 'application/pdf']);

View File

@ -504,7 +504,7 @@ class CompanyController extends BaseController
LightLogs::create(new AccountDeleted())
->increment()
->batch();
->queue();
} else {
$company_id = $company->id;

View File

@ -170,7 +170,7 @@ class PostMarkController extends BaseController
$request->input('MessageID')
);
LightLogs::create($bounce)->batch();
LightLogs::create($bounce)->queue();
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
}
@ -212,7 +212,7 @@ class PostMarkController extends BaseController
$request->input('MessageID')
);
LightLogs::create($spam)->batch();
LightLogs::create($spam)->queue();
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
}

View File

@ -294,7 +294,7 @@ class PreviewController extends BaseController
{
LightLogs::create(new LivePreview())
->increment()
->batch();
->queue();
}

View File

@ -72,7 +72,7 @@ class Kernel extends HttpKernel
ConvertEmptyStringsToNull::class,
TrustProxies::class,
// \Fruitcake\Cors\HandleCors::class,
Cors::class,
// Cors::class,
];
@ -95,6 +95,7 @@ class Kernel extends HttpKernel
'api' => [
'throttle:300,1',
'bindings',
'cors',
'query_logging',
],
'contact' => [
@ -116,6 +117,7 @@ class Kernel extends HttpKernel
'throttle:120,1',
'bindings',
'query_logging',
'cors',
],
];
@ -132,6 +134,7 @@ class Kernel extends HttpKernel
'bindings' => SubstituteBindings::class,
'cache.headers' => SetCacheHeaders::class,
'can' => Authorize::class,
'cors' => Cors::class,
'guest' => RedirectIfAuthenticated::class,
'signed' => ValidateSignature::class,
'throttle' => ThrottleRequests::class,

View File

@ -40,9 +40,9 @@ class ApiSecretCheck
];
return response()
->json($error, 403)
->header('X-App-Version', config('ninja.app_version'))
->header('X-Minimum-Client-Version', config('ninja.minimum_client_version'));
->json($error, 403)
->header('X-App-Version', config('ninja.app_version'))
->header('X-Minimum-Client-Version', config('ninja.minimum_client_version'));
}
}
}

View File

@ -31,9 +31,9 @@ class ContactAccount
if(!Ninja::isHosted()) {
$account_id = Account::first()->id;
$request->request->add(['account_id' => $account_id]);
$account = Account::first();
session()->put('account_key', $account->key);
}
return $next($request);

View File

@ -37,7 +37,8 @@ class ContactRegister
if(! $company->client_can_register)
abort(400, 'Registration disabled');
$request->merge(['key' => $company->company_key]);
// $request->merge(['key' => $company->company_key]);
session()->put('key', $company->company_key);
return $next($request);
}
@ -55,7 +56,8 @@ class ContactRegister
if(! $company->client_can_register)
abort(400, 'Registration disabled');
$request->merge(['key' => $company->company_key]);
// $request->merge(['key' => $company->company_key]);
session()->put('key', $company->company_key);
return $next($request);
}
@ -69,7 +71,8 @@ class ContactRegister
if(! (bool)$company->client_can_register);
abort(400, 'Registration disabled');
$request->merge(['key' => $company->company_key]);
//$request->merge(['key' => $company->company_key]);
session()->put('key', $company->company_key);
return $next($request);
}
@ -82,7 +85,8 @@ class ContactRegister
if(! $company->client_can_register)
abort(400, 'Registration disabled');
$request->merge(['key' => $company->company_key]);
//$request->merge(['key' => $company->company_key]);
session()->put('key', $company->company_key);
return $next($request);
}

View File

@ -1,4 +1,13 @@
<?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://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Middleware;

View File

@ -68,7 +68,7 @@ class QueryLogging
}
LightLogs::create(new DbQuery($request->method(), urldecode($request->url()), $count, $time, $ip))
->batch();
->queue();
}

View File

@ -50,7 +50,8 @@ class SetDomainNameDb
];
if($company = MultiDB::findAndSetDbByDomain($query)){
$request->request->add(['account_id' => $company->account_id, 'company_key' => $company->company_key]);
//$request->merge(['company_key' => $company->company_key]);
session()->put('company_key', $company->company_key);
}
else
{
@ -72,7 +73,8 @@ class SetDomainNameDb
];
if($company = MultiDB::findAndSetDbByDomain($query)){
$request->request->add(['account_id' => $company->account_id, 'company_key' => $company->company_key]);
//$request->merge(['company_key' => $company->company_key]);
session()->put('company_key', $company->company_key);
}
else
{
@ -81,7 +83,6 @@ class SetDomainNameDb
} else {
MultiDB::setDb('db-ninja-01');
nlog("I could not set the DB - defaulting to DB1");
//abort(400, 'Domain not found');
}
}

View File

@ -20,6 +20,7 @@ use App\Http\ValidationRules\ValidCreditsPresentRule;
use App\Http\ValidationRules\ValidPayableInvoicesRule;
use App\Models\Payment;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class StorePaymentRequest extends Request
{
@ -100,7 +101,8 @@ class StorePaymentRequest extends Request
'credits.*.credit_id' => new ValidCreditsRules($this->all()),
'credits.*.amount' => ['required', new CreditsSumRule($this->all())],
'invoices' => new ValidPayableInvoicesRule(),
'number' => 'bail|nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id,
'number' => ['nullable', Rule::unique('payments')->where('company_id', auth()->user()->company()->id)],
];
if ($this->input('documents') && is_array($this->input('documents'))) {

View File

@ -14,6 +14,7 @@ namespace App\Import\Transformers\Freshbooks;
use App\Import\ImportException;
use App\Import\Transformers\BaseTransformer;
use App\Models\Invoice;
use App\Utils\Number;
/**
* Class InvoiceTransformer.
@ -53,16 +54,16 @@ class InvoiceTransformer extends BaseTransformer {
$line_items[] = [
'product_key' => $this->getString( $record, 'Item Name' ),
'notes' => $this->getString( $record, 'Item Description' ),
'cost' => $this->getFloat( $record, 'Rate' ),
'quantity' => $this->getFloat( $record, 'Quantity' ),
'discount' => $this->getFloat( $record, 'Discount Percentage' ),
'cost' => $this->getFreshbookQuantityFloat( $record, 'Rate' ),
'quantity' => $this->getFreshbookQuantityFloat( $record, 'Quantity' ),
'discount' => $this->getFreshbookQuantityFloat( $record, 'Discount Percentage' ),
'is_amount_discount' => false,
'tax_name1' => $this->getString( $record, 'Tax 1 Type' ),
'tax_rate1' => $this->getFloat( $record, 'Tax 1 Amount' ),
'tax_rate1' => $this->getFreshbookQuantityFloat( $record, 'Tax 1 Amount' ),
'tax_name2' => $this->getString( $record, 'Tax 2 Type' ),
'tax_rate2' => $this->getFloat( $record, 'Tax 2 Amount' ),
'tax_rate2' => $this->getFreshbookQuantityFloat( $record, 'Tax 2 Amount' ),
];
$transformed['amount'] += $this->getFloat( $record, 'Line Total' );
$transformed['amount'] += $this->getFreshbookQuantityFloat( $record, 'Line Total' );
}
$transformed['line_items'] = $line_items;
@ -75,4 +76,11 @@ class InvoiceTransformer extends BaseTransformer {
return $transformed;
}
/** @return float */
public function getFreshbookQuantityFloat($data, $field)
{
return $data[$field];
}
}

View File

@ -136,7 +136,7 @@ class CreateAccount
LightLogs::create(new AnalyticsAccountCreated())
->increment()
->batch();
->queue();

View File

@ -440,63 +440,52 @@ class CSVImport implements ShouldQueue {
'tax_names' => [],
];
$clients = Client::scope()->get();
foreach ( $clients as $client ) {
Client::where('company_id', $this->company->id)->cursor()->each(function ($client){
$this->addClientToMaps( $client );
}
});
$contacts = ClientContact::scope()->get();
foreach ( $contacts as $contact ) {
ClientContact::where('company_id', $this->company->id)->cursor()->each(function ($contact){
$this->addContactToMaps( $contact );
}
});
$invoices = Invoice::scope()->get();
foreach ( $invoices as $invoice ) {
Invoice::where('company_id', $this->company->id)->cursor()->each(function ($invoice){
$this->addInvoiceToMaps( $invoice );
}
});
$products = Product::scope()->get();
foreach ( $products as $product ) {
Product::where('company_id', $this->company->id)->cursor()->each(function ($product){
$this->addProductToMaps( $product );
}
});
$projects = Project::scope()->get();
foreach ( $projects as $project ) {
Project::where('company_id', $this->company->id)->cursor()->each(function ($project){
$this->addProjectToMaps( $project );
}
});
$countries = Country::all();
foreach ( $countries as $country ) {
Country::all()->each(function ($country){
$this->maps['countries'][ strtolower( $country->name ) ] = $country->id;
$this->maps['countries2'][ strtolower( $country->iso_3166_2 ) ] = $country->id;
}
});
$currencies = Currency::all();
foreach ( $currencies as $currency ) {
Currency::all()->each(function ($currency){
$this->maps['currencies'][ strtolower( $currency->code ) ] = $currency->id;
}
});
$payment_types = PaymentType::all();
foreach ( $payment_types as $payment_type ) {
PaymentType::all()->each(function ($payment_type){
$this->maps['payment_types'][ strtolower( $payment_type->name ) ] = $payment_type->id;
}
});
$vendors = Vendor::scope()->get();
foreach ( $vendors as $vendor ) {
Vendor::where('company_id', $this->company->id)->cursor()->each(function ($vendor){
$this->addVendorToMaps( $vendor );
}
});
$expenseCaegories = ExpenseCategory::scope()->get();
foreach ( $expenseCaegories as $category ) {
ExpenseCategory::where('company_id', $this->company->id)->cursor()->each(function ($category){
$this->addExpenseCategoryToMaps( $category );
}
});
$taxRates = TaxRate::scope()->get();
foreach ( $taxRates as $taxRate ) {
TaxRate::where('company_id', $this->company->id)->cursor()->each(function ($taxRate){
$name = trim( strtolower( $taxRate->name ) );
$this->maps['tax_rates'][ $name ] = $taxRate->rate;
$this->maps['tax_names'][ $name ] = $taxRate->name;
}
});
}
/**

View File

@ -110,7 +110,7 @@ class NinjaMailerJob implements ShouldQueue
->send($this->nmo->mailable);
LightLogs::create(new EmailSuccess($this->nmo->company->company_key))
->batch();
->queue();
/* Count the amount of emails sent across all the users accounts */
Cache::increment($this->company->account->key);
@ -279,7 +279,7 @@ class NinjaMailerJob implements ShouldQueue
$job_failure->string_metric6 = substr($errors, 0, 150);
LightLogs::create($job_failure)
->batch();
->queue();
}
public function failed($exception = null)

View File

@ -192,7 +192,7 @@ class SendRecurring implements ShouldQueue
$job_failure->string_metric6 = $exception->getMessage();
LightLogs::create($job_failure)
->batch();
->queue();
nlog(print_r($exception->getMessage(), 1));
}

View File

@ -1105,7 +1105,7 @@ class Import implements ShouldQueue
throw new MigrationValidatorFailed(json_encode($validator->errors()));
}
$quote_repository = new QuoteRepository();
$quote_repository = new InvoiceMigrationRepository();
foreach ($data as $resource) {
$modified = $resource;
@ -1144,7 +1144,7 @@ class Import implements ShouldQueue
$resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id;
$resource['invitations'][$key]['email_status'] = '';
unset($resource['invitations'][$key]['invoice_id']);
unset($resource['invitations'][$key]['quote_id']);
unset($resource['invitations'][$key]['id']);
}
@ -1832,7 +1832,7 @@ class Import implements ShouldQueue
$job_failure->string_metric6 = $exception->getMessage();
LightLogs::create($job_failure)
->batch();
->queue();
info(print_r($exception->getMessage(), 1));

View File

@ -57,9 +57,6 @@ class QuoteApprovedNotification implements ShouldQueue
if(!$user)
continue;
/* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'quote');
/* Returns an array of notification methods */
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_approved', 'quote_approved_all']);
@ -67,7 +64,6 @@ class QuoteApprovedNotification implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]);
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
@ -76,11 +72,6 @@ class QuoteApprovedNotification implements ShouldQueue
$first_notification_sent = false;
}
/* Override the methods in the Notification Class */
// $notification->method = $methods;
// Notify on the alternate channels
// $user->notify($notification);
}
}
}

View File

@ -283,6 +283,9 @@ class Client extends BaseModel implements HasLocalePreference
{
$date_formats = Cache::get('date_formats');
if(!$date_formats)
$this->buildCache(true);
return $date_formats->filter(function ($item) {
return $item->id == $this->getSetting('date_format_id');
})->first()->format;

View File

@ -14,10 +14,10 @@ namespace App\PaymentDrivers\GoCardless;
use App\Exceptions\PaymentFailed;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use Illuminate\Http\Request;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
@ -27,6 +27,7 @@ use App\Utils\Traits\MakesHash;
use Exception;
use GoCardlessPro\Resources\Payment as ResourcesPayment;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
@ -160,17 +161,24 @@ class ACH implements MethodInterface
*/
public function paymentResponse(PaymentResponseRequest $request)
{
// $token = ClientGatewayToken::find(
// $this->decodePrimaryKey($request->source)
// )->firstOrFail();
$this->go_cardless->ensureMandateIsReady($request->source);
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->go_cardless->payment_hash->invoices(), 'invoice_id')))
->withTrashed()
->first();
if($invoice)
$description = "Invoice {$invoice->number} for {$request->amount} for client {$this->go_cardless->client->present()->name()}";
else
$description = "Amount {$request->amount} from client {$this->go_cardless->client->present()->name()}";
try {
$payment = $this->go_cardless->gateway->payments()->create([
'params' => [
'amount' => $request->amount,
'currency' => $request->currency,
'description' => $description,
'metadata' => [
'payment_hash' => $this->go_cardless->payment_hash->hash,
],

View File

@ -14,11 +14,11 @@ namespace App\PaymentDrivers\GoCardless;
use App\Exceptions\PaymentFailed;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use Illuminate\Http\Request;
use App\Jobs\Mail\PaymentFailureMailer;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
@ -26,6 +26,7 @@ use App\PaymentDrivers\Common\MethodInterface;
use App\PaymentDrivers\GoCardlessPaymentDriver;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
@ -155,11 +156,21 @@ class DirectDebit implements MethodInterface
$this->go_cardless->ensureMandateIsReady($request->source);
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->go_cardless->payment_hash->invoices(), 'invoice_id')))
->withTrashed()
->first();
if($invoice)
$description = "Invoice {$invoice->number} for {$request->amount} for client {$this->go_cardless->client->present()->name()}";
else
$description = "Amount {$request->amount} from client {$this->go_cardless->client->present()->name()}";
try {
$payment = $this->go_cardless->gateway->payments()->create([
'params' => [
'amount' => $request->amount,
'currency' => $request->currency,
'description' => $description,
'metadata' => [
'payment_hash' => $this->go_cardless->payment_hash->hash,
],

View File

@ -14,10 +14,10 @@ namespace App\PaymentDrivers\GoCardless;
use App\Exceptions\PaymentFailed;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
use Illuminate\Http\Request;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
@ -26,6 +26,7 @@ use App\PaymentDrivers\GoCardlessPaymentDriver;
use App\Utils\Traits\MakesHash;
use Exception;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
@ -162,11 +163,21 @@ class SEPA implements MethodInterface
{
$this->go_cardless->ensureMandateIsReady($request->source);
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->go_cardless->payment_hash->invoices(), 'invoice_id')))
->withTrashed()
->first();
if($invoice)
$description = "Invoice {$invoice->number} for {$request->amount} for client {$this->go_cardless->client->present()->name()}";
else
$description = "Amount {$request->amount} from client {$this->go_cardless->client->present()->name()}";
try {
$payment = $this->go_cardless->gateway->payments()->create([
'params' => [
'amount' => $request->amount,
'currency' => $request->currency,
'description' => $description,
'metadata' => [
'payment_hash' => $this->go_cardless->payment_hash->hash,
],

View File

@ -28,6 +28,7 @@ use App\Utils\Ninja;
use App\Utils\Number;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\Pdf\PdfMaker as PdfMakerTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;
@ -123,7 +124,7 @@ class Statement
*/
protected function setupEntity(): self
{
if (count($this->getInvoices()) >= 1) {
if ($this->getInvoices()->count() >= 1) {
$this->entity = $this->getInvoices()->first();
}
@ -218,7 +219,7 @@ class Statement
*
* @return Invoice[]|\Illuminate\Database\Eloquent\Collection
*/
protected function getInvoices(): LazyCollection
protected function getInvoices(): Builder
{
return Invoice::withTrashed()
->where('is_deleted', false)
@ -226,8 +227,7 @@ class Statement
->where('client_id', $this->client->id)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID])
->whereBetween('date', [$this->options['start_date'], $this->options['end_date']])
->orderBy('number', 'ASC')
->cursor();
->orderBy('number', 'ASC');
}
/**
@ -235,7 +235,7 @@ class Statement
*
* @return Payment[]|\Illuminate\Database\Eloquent\Collection
*/
protected function getPayments(): LazyCollection
protected function getPayments(): Builder
{
return Payment::withTrashed()
->with('client.country','invoices')
@ -244,8 +244,7 @@ class Statement
->where('client_id', $this->client->id)
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
->whereBetween('date', [$this->options['start_date'], $this->options['end_date']])
->orderBy('number', 'ASC')
->cursor();
->orderBy('number', 'ASC');
}
/**

View File

@ -17,6 +17,8 @@ use App\Models\CompanyGateway;
use App\Models\Invoice;
use App\Models\Payment;
use App\Services\AbstractService;
use App\Utils\Ninja;
use Illuminate\Support\Facades\App;
class AddGatewayFee extends AbstractService
{
@ -72,6 +74,10 @@ class AddGatewayFee extends AbstractService
private function processGatewayFee($gateway_fee)
{
App::forgetInstance('translator');
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->invoice->company->settings));
$invoice_item = new InvoiceItem;
$invoice_item->type_id = '3';
$invoice_item->product_key = ctrans('texts.surcharge');
@ -98,6 +104,10 @@ class AddGatewayFee extends AbstractService
private function processGatewayDiscount($gateway_fee)
{
App::forgetInstance('translator');
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->invoice->company->settings));
$invoice_item = new InvoiceItem;
$invoice_item->type_id = '3';
$invoice_item->product_key = ctrans('texts.discount');

View File

@ -359,7 +359,7 @@ class Design extends BaseDesign
$tbody = [];
foreach ($this->invoices as $invoice) {
foreach ($this->invoices->cursor() as $invoice) {
$element = ['element' => 'tr', 'elements' => []];
$element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
@ -407,7 +407,7 @@ class Design extends BaseDesign
$tbody = [];
foreach ($this->payments as $payment) {
foreach ($this->payments->cursor() as $payment) {
foreach ($payment->invoices as $invoice) {
$element = ['element' => 'tr', 'elements' => []];

View File

@ -41,7 +41,7 @@ trait DesignHelpers
if (isset($this->context['invoices'])) {
$this->invoices = $this->context['invoices'];
if (\count($this->invoices) >= 1) {
if ($this->invoices->count() >= 1) {
$this->entity = $this->invoices->first();
}
}

View File

@ -110,7 +110,7 @@ class QuoteService
$contact = $this->quote->invitations->first()->contact;
}
event(new QuoteWasApproved($contact, $this->quote, $this->quote->company, Ninja::eventVars()));
// event(new QuoteWasApproved($contact, $this->quote, $this->quote->company, Ninja::eventVars()));
if ($this->quote->client->getSetting('auto_convert_quote')) {
$this->convert();
@ -123,12 +123,6 @@ class QuoteService
}
// if ($this->quote->client->getSetting('auto_archive_quote')) {
// $quote_repo = new QuoteRepository();
// $quote_repo->archive($this->quote);
// }
return $this;
}

View File

@ -295,19 +295,26 @@ trait GeneratesCounter
*/
public function getNextProjectNumber(Project $project) :string
{
$this->resetCompanyCounters($project->company);
// 08/12/2021 - allows projects to have client counters.
$counter = $project->company->settings->project_number_counter;
$setting_entity = $project->company->settings->project_number_counter;
// $this->resetCompanyCounters($project->company);
$project_number = $this->checkEntityNumber(Project::class, $project, $counter, $project->company->settings->counter_padding, $project->company->settings->project_number_pattern);
// $counter = $project->company->settings->project_number_counter;
// $setting_entity = $project->company->settings->project_number_counter;
$this->incrementCounter($project->company, 'project_number_counter');
// $project_number = $this->checkEntityNumber(Project::class, $project, $counter, $project->company->settings->counter_padding, $project->company->settings->project_number_pattern);
$entity_number = $project_number;
// $this->incrementCounter($project->company, 'project_number_counter');
// $entity_number = $project_number;
// return $this->replaceUserVars($project, $entity_number);
$entity_number = $this->getNextEntityNumber(Project::class, $project->client, false);
return $this->replaceUserVars($project, $entity_number);
}

View File

@ -207,7 +207,7 @@ return [
['options' => [
'replication' => 'sentinel',
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
'sentinel_timeout' => 2.0,
'sentinel_timeout' => 3.0,
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => env('REDIS_DB', 0),
@ -226,7 +226,7 @@ return [
['options' => [
'replication' => 'sentinel',
'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'),
'sentinel_timeout' => 2.0,
'sentinel_timeout' => 3.0,
'parameters' => [
'password' => env('REDIS_PASSWORD', null),
'database' => env('REDIS_CACHE_DB', 1),

View File

@ -1480,5 +1480,49 @@ class PaymentTest extends TestCase
$this->assertEquals(10, $this->invoice->fresh()->balance);
$this->assertEquals(10, $this->invoice->fresh()->balance);
}
public function testUniquePaymentNumbers()
{
$data = [
'amount' => $this->invoice->amount,
'client_id' => $this->client->hashed_id,
'date' => '2020/12/12',
'number' => 'duplicate',
];
try {
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/payments', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
$arr = $response->json();
$response->assertStatus(200);
$response = false;
try {
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/payments', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
}
if($response)
$response->assertStatus(302);
}
}

View File

@ -297,6 +297,7 @@ trait MockAccountData
$this->project = Project::factory()->create([
'user_id' => $user_id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
]);
$this->expense = Expense::factory()->create([