Merge pull request #7035 from turbo124/v5-stable

v5.3.35
This commit is contained in:
David Bomba 2021-12-13 20:33:12 +11:00 committed by GitHub
commit bdb16d9d24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 37218 additions and 36796 deletions

View File

@ -1 +1 @@
5.3.34
5.3.35

View File

@ -37,7 +37,6 @@ class ExpenseFilters extends QueryFilters
return $this->builder->where(function ($query) use ($filter) {
$query->where('expenses.public_notes', 'like', '%'.$filter.'%')
->orWhere('expenses.id_number', 'like', '%'.$filter.'%')
->orWhere('expenses.custom_value1', 'like', '%'.$filter.'%')
->orWhere('expenses.custom_value2', 'like', '%'.$filter.'%')
->orWhere('expenses.custom_value3', 'like', '%'.$filter.'%')

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,17 @@ 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();
if(Ninja::isHosted() && $company = Company::where('company_key', $request->input('company_key'))->first())
{
$contact = ClientContact::where(['email' => $request->input('email'), 'company_id' => $company->id])->first();
}
else
$contact = ClientContact::where(['email' => $request->input('email')])->first();
$response = false;
@ -108,20 +112,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

@ -22,12 +22,14 @@ use Auth;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Route;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Hash;
class ContactLoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = '/client/dashboard';
protected $redirectTo = '/client/invoices';
public function __construct()
{
@ -40,8 +42,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();
}
@ -93,7 +95,16 @@ class ContactLoginController extends Controller
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
if(Ninja::isHosted() && $request->has('password') && $company = Company::where('company_key', $request->input('company_key'))->first()){
$contact = ClientContact::where(['email' => $request->input('email'), 'company_id' => $company->id])->first();
if(Hash::check($request->input('password'), $contact->password))
return $this->authenticated($request, $contact);
}
elseif ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
@ -104,9 +115,24 @@ class ContactLoginController extends Controller
return $this->sendFailedLoginResponse($request);
}
protected function sendLoginResponse(Request $request)
{
$request->session()->regenerate();
$this->clearLoginAttempts($request);
if ($response = $this->authenticated($request, $this->guard()->user())) {
return $response;
}
return $request->wantsJson()
? new JsonResponse([], 204)
: redirect()->intended($this->redirectPath());
}
public function authenticated(Request $request, ClientContact $client)
{
Auth::guard('contact')->loginUsingId($client->id, true);
auth()->guard('contact')->loginUsingId($client->id, true);
event(new ContactLoggedIn($client, $client->company, Ninja::eventVars()));

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

@ -154,6 +154,10 @@ class QuoteController extends Controller
}
}
if(count($ids) == 1){
return redirect()->route('client.quote.show', $quotes->first()->hashed_id);
}
return redirect()
->route('client.quotes.index')
->withSuccess('Quote(s) approved successfully.');

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

@ -132,6 +132,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

@ -42,8 +42,8 @@ class CreditsTable extends Component
->where('is_deleted', 0)
->where(function ($query){
$query->whereDate('due_date', '>=', now())
->orWhereNull('due_date')
->orWhere('due_date', '=', '');
->orWhereNull('due_date');
//->orWhere('due_date', '=', '');
})
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()

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

@ -28,8 +28,9 @@ class CheckClientExistence
*/
public function handle(Request $request, Closure $next)
{
$multiple_contacts = ClientContact::query()
->with('client.gateway_tokens')
->with('client.gateway_tokens','company')
->where('email', auth('contact')->user()->email)
->whereNotNull('email')
->where('email', '<>', '')
@ -38,9 +39,9 @@ class CheckClientExistence
->distinct('email')
->whereNotNull('company_id')
->whereHas('client', function ($query) {
return $query->whereNull('deleted_at');
return $query->where('is_deleted', false);
})
->whereHas('client.company', function ($query){
->whereHas('company', function ($query){
return $query->where('account_id', auth('contact')->user()->client->company->account->id);
})
->get();

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,14 +37,14 @@ class ContactRegister
if(! $company->client_can_register)
abort(400, 'Registration disabled');
$request->merge(['key' => $company->company_key]);
session()->put('key', $company->company_key);
return $next($request);
}
}
$query = [
$query = [
'portal_domain' => $request->getSchemeAndHttpHost(),
'portal_mode' => 'domain',
];
@ -55,7 +55,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 +70,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 +84,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

@ -1335,12 +1335,14 @@ class CompanyImport implements ShouldQueue
/* New to convert product ids from old hashes to new hashes*/
if($class == 'App\Models\Subscription'){
//$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
//$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
// $obj_array['webhook_configuration'] = json_encode($obj_array['webhook_configuration']);
$obj_array['webhook_configuration'] = '';
if(array_key_exists('company', $obj_array))
unset($obj_array['company']);
$obj_array['webhook_configuration'] = (array)$obj_array['webhook_configuration'];
$obj_array['recurring_product_ids'] = '';
$obj_array['product_ids'] = '';
nlog($obj_array);
}
/* Expenses that don't have a number will not be inserted - so need to override here*/

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();
}
@ -171,7 +172,7 @@ class Statement
$item->tax_rate1 = 5;
}
$product = Product::all()->random();
$product = Product::first();
$item->cost = (float) $product->cost;
$item->product_key = $product->product_key;
@ -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

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

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.34',
'app_tag' => '5.3.34',
'app_version' => '5.3.35',
'app_tag' => '5.3.35',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -8,9 +8,9 @@ const RESOURCES = {
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"main.dart.js": "03d6faff8d468318196cd0bef8a4c9a6",
"main.dart.js": "10ea07f0ae16476a4df24fff70c8de76",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"/": "c2e105029a9aed730c6480128daf2ced",
"/": "b7b8f565eedff3f4bd0a5f8ae67f8caf",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",

3121
public/main.dart.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

33476
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2891
public/main.html.dart.js vendored

File diff suppressed because it is too large Load Diff

33656
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -280570,7 +280570,11 @@
return false;
if (this.entityType === B.EntityType_invoice) {
var t1 = this.statusId;
if (t1 === "5" || t1 === "6")
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
if (t1)
return false;
}
return true;
@ -280789,8 +280793,15 @@
actions.push(B.EntityAction_download);
}
if (userCompany.canEditEntity$1(_this)) {
t3 = _this.statusId;
t3 = !(t3 === "5" || t3 === "6");
if (_this.entityType === B.EntityType_invoice) {
t3 = _this.statusId;
if (t3 !== "5")
t3 = t3 === "6";
else
t3 = true;
} else
t3 = false;
t3 = !t3;
} else
t3 = false;
if (t3) {
@ -280871,8 +280882,15 @@
}
if (userCompany.canEditEntity$1(_this))
if (t1) {
t1 = _this.statusId;
t1 = !(t1 === "5" || t1 === "6");
if (_this.entityType === B.EntityType_invoice) {
t1 = _this.statusId;
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
} else
t1 = false;
t1 = !t1;
} else
t1 = false;
else
@ -280880,7 +280898,7 @@
if (t1) {
t1 = _this.entityType;
if (t1 !== B.EntityType_quote && t1 !== B.EntityType_credit && t1 !== B.EntityType_recurringInvoice && _this.statusId !== "1")
if (_this.statusId !== "4")
if (!(t1 === B.EntityType_invoice && _this.statusId === "4"))
actions.push(B.EntityAction_cancel);
}
B.JSArray_methods.addAll$1(actions, _this.super$BaseEntity$getActions(null, false, false, userCompany));
@ -280923,25 +280941,32 @@
return J.compareTo$1$ns(startDate, t1) <= 0 && J.compareTo$1$ns(endDate, t1) >= 0;
},
get$isUnpaid() {
var t1 = this.statusId;
if (this.entityType === B.EntityType_quote)
return !B.JSArray_methods.contains$1(A._setArrayType(["3", "4"], type$.JSArray_legacy_String), t1);
var t1 = this.entityType;
if (t1 === B.EntityType_quote)
return !B.JSArray_methods.contains$1(A._setArrayType(["3", "4"], type$.JSArray_legacy_String), this.statusId);
else
return t1 !== "4";
return !(t1 === B.EntityType_invoice && this.statusId === "4");
},
get$isPayable() {
var t2,
t1 = this.statusId;
if (t1 !== "4") {
t2 = this.entityType;
if (t2 !== B.EntityType_quote)
if (t2 !== B.EntityType_recurringInvoice)
t1 = !(t1 === "5" || t1 === "6");
else
var t1 = this.entityType,
t2 = t1 === B.EntityType_invoice;
if (!(t2 && this.statusId === "4"))
if (t1 !== B.EntityType_quote)
if (t1 !== B.EntityType_recurringInvoice) {
if (t2) {
t1 = this.statusId;
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
} else
t1 = false;
t1 = !t1;
} else
t1 = false;
else
t1 = false;
} else
else
t1 = false;
return t1;
},
@ -280950,10 +280975,9 @@
},
get$isUpcoming() {
var t1, _this = this;
if (!_this.get$isArchived() && !_this.isDeleted) {
t1 = _this.statusId;
t1 = t1 !== "4" && !_this.get$isPastDue() && t1 !== "1";
} else
if (!_this.get$isArchived() && !_this.isDeleted)
t1 = !(_this.entityType === B.EntityType_invoice && _this.statusId === "4") && !_this.get$isPastDue() && _this.statusId !== "1";
else
t1 = false;
return t1;
},
@ -280973,13 +280997,27 @@
return "-1";
} else {
if (_this.get$isPastDue()) {
t1 = _this.statusId;
t1 = !(t1 === "5" || t1 === "6");
if (t2 === B.EntityType_invoice) {
t1 = _this.statusId;
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
} else
t1 = false;
t1 = !t1;
} else
t1 = false;
if (t1)
return "-1";
if (_this.get$isViewed() && _this.get$isUnpaid())
if (_this.get$isViewed())
if (_this.get$isUnpaid())
t1 = !(t2 === B.EntityType_invoice && _this.statusId === "3");
else
t1 = false;
else
t1 = false;
if (t1)
return t2 === B.EntityType_invoice ? "-3" : "-2";
}
return _this.statusId;
@ -308479,7 +308517,7 @@
t2 = t1.userCompanyStates;
t3 = t1.uiState.selectedCompanyIndex;
t1 = A.getClientSettings(t1, t2._list[t3].clientState.$get$1(0, invoice.clientId)).lockInvoices;
if (t1 === "when_paid" && invoice.statusId === "4") {
if (t1 === "when_paid" && invoice.entityType === B.EntityType_invoice && invoice.statusId === "4") {
t1 = J.$index$asx($.LocalizationsProvider__localizedValues.$index(0, _this.localization.localeCode), "paid_invoices_are_locked");
if (t1 == null)
t1 = "";
@ -318440,9 +318478,16 @@
client = A.ClientEntity_ClientEntity(null, t1, null, null);
t1 = invoice.statusId;
if (t1 !== "1")
if (!invoice.isDeleted)
t1 = t1 === "5" || t1 === "6" || client.isDeleted;
else
if (!invoice.isDeleted) {
if (invoice.entityType === B.EntityType_invoice)
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
else
t1 = false;
t1 = t1 || client.isDeleted;
} else
t1 = true;
else
t1 = true;
@ -318698,7 +318743,11 @@
if (t1.matchesCurrency$1(client.settings.currencyId)) {
B.JSArray_methods.forEach$1(task.getTaskTimes$0(), new A.chartTasks__closure(_this.totals, _this.loggedData, _this.invoicedData, _this.paidData, t5, project, client, task, group, t1, invoice, _this.currencyMap, t2));
if (t3.length !== 0) {
t1 = t4.containsKey$1(0, t3) && t4.$index(0, t3).statusId === "4";
if (t4.containsKey$1(0, t3)) {
t1 = t4.$index(0, t3);
t1 = t1.entityType === B.EntityType_invoice && t1.statusId === "4";
} else
t1 = false;
t2 = _this.counts;
if (t1)
t2.$indexSet(0, "paid", t2.$index(0, "paid") + 1);
@ -318758,7 +318807,11 @@
t2 = t4.invoiceId;
if (t2.length !== 0) {
t3 = _this.invoiceMap._map$_map;
t2 = t3.containsKey$1(0, t2) && t3.$index(0, t2).statusId === "4";
if (t3.containsKey$1(0, t2)) {
t2 = t3.$index(0, t2);
t2 = t2.entityType === B.EntityType_invoice && t2.statusId === "4";
} else
t2 = false;
t4 = t4.id;
if (t2) {
t1 = t1.$index(0, "paid");
@ -318833,18 +318886,19 @@
invoice = _this.invoiceMap._map$_map.$index(0, t1);
if (invoice == null)
invoice = A.InvoiceEntity_InvoiceEntity(_null, _null, _null, _null, _null);
t1 = _this.counts;
t2 = expense.id;
if (invoice.statusId === "4") {
t3 = t3.$index(0, _s4_);
t3.$indexSet(0, date, t3.$index(0, date) + amount);
t1.$indexSet(0, _s4_, t1.$index(0, _s4_) + 1);
J.add$1$ax(_this.paidData.entityMap.$index(0, date), t2);
t1 = invoice.entityType === B.EntityType_invoice && invoice.statusId === "4";
t2 = _this.counts;
t4 = expense.id;
if (t1) {
t1 = t3.$index(0, _s4_);
t1.$indexSet(0, date, t1.$index(0, date) + amount);
t2.$indexSet(0, _s4_, t2.$index(0, _s4_) + 1);
J.add$1$ax(_this.paidData.entityMap.$index(0, date), t4);
} else {
t3 = t3.$index(0, _s8_);
t3.$indexSet(0, date, t3.$index(0, date) + amount);
t1.$indexSet(0, _s8_, t1.$index(0, _s8_) + 1);
J.add$1$ax(_this.invoicedData.entityMap.$index(0, date), t2);
t1 = t3.$index(0, _s8_);
t1.$indexSet(0, date, t1.$index(0, date) + amount);
t2.$indexSet(0, _s8_, t2.$index(0, _s8_) + 1);
J.add$1$ax(_this.invoicedData.entityMap.$index(0, date), t4);
}
} else {
t1 = expense.get$isPending();
@ -318892,8 +318946,15 @@
if (client == null)
client = A.ClientEntity_ClientEntity(null, t1, null, null);
if (!(invoice.get$isArchived() || invoice.isDeleted)) {
t1 = invoice.statusId;
t1 = t1 === "5" || t1 === "6" || client.get$isArchived() || client.isDeleted;
if (invoice.entityType === B.EntityType_invoice) {
t1 = invoice.statusId;
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
} else
t1 = false;
t1 = t1 || client.get$isArchived() || client.isDeleted;
} else
t1 = true;
if (!t1)
@ -318921,8 +318982,15 @@
if (client == null)
client = A.ClientEntity_ClientEntity(null, t1, null, null);
if (!(invoice.get$isArchived() || invoice.isDeleted)) {
t1 = invoice.statusId;
t1 = t1 === "5" || t1 === "6" || client.get$isArchived() || client.isDeleted;
if (invoice.entityType === B.EntityType_invoice) {
t1 = invoice.statusId;
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
} else
t1 = false;
t1 = t1 || client.get$isArchived() || client.isDeleted;
} else
t1 = true;
if (!t1)
@ -326295,7 +326363,8 @@
};
A.handleInvoiceAction__closure.prototype = {
call$1(invoice) {
return type$.legacy_InvoiceEntity._as(invoice).statusId !== "4";
type$.legacy_InvoiceEntity._as(invoice);
return !(invoice.entityType === B.EntityType_invoice && invoice.statusId === "4");
},
$signature: 179
};
@ -327724,8 +327793,15 @@
return false;
if (!invoice.get$isArchived() && !invoice.isDeleted)
if (invoice.get$isUnpaid()) {
t1 = invoice.statusId;
t1 = !(t1 === "5" || t1 === "6");
if (invoice.entityType === B.EntityType_invoice) {
t1 = invoice.statusId;
if (t1 !== "5")
t1 = t1 === "6";
else
t1 = true;
} else
t1 = false;
t1 = !t1;
} else
t1 = false;
else
@ -358927,15 +359003,20 @@
};
A._EntityDropdownState_build_closure1.prototype = {
call$4(context, textEditingController, focusNode, onFieldSubmitted) {
var t4, t5, _null = null,
var t4, t5, t6, _null = null,
t1 = this.$this,
t2 = t1._widget,
t3 = t2.validator;
t2 = t2.allowClearing && t1.get$hasValue();
t4 = t1._widget;
t5 = t4.labelText;
t4 = t4.autofocus;
return A.DecoratedFormField$(false, _null, t4 === true, false, textEditingController, _null, true, focusNode, _null, _null, _null, false, false, _null, B.TextInputType_0_null_null, t5, _null, _null, false, new A._EntityDropdownState_build__closure3(t1), new A._EntityDropdownState_build__closure4(onFieldSubmitted), _null, t2, _null, this.iconButton, B.TextAlign_4, t3);
t6 = t4.autofocus;
if (t6 === true) {
t4 = t4.entityId;
t4 = (t4 == null ? "" : t4).length === 0;
} else
t4 = false;
return A.DecoratedFormField$(false, _null, t4, false, textEditingController, _null, true, focusNode, _null, _null, _null, false, false, _null, B.TextInputType_0_null_null, t5, _null, _null, false, new A._EntityDropdownState_build__closure3(t1), new A._EntityDropdownState_build__closure4(onFieldSubmitted), _null, t2, _null, this.iconButton, B.TextAlign_4, t3);
},
$signature: 377
};
@ -380264,7 +380345,7 @@
return this._invoice_edit$_onSavePressed$2(context, null);
},
build$1(_, context) {
var t2, t3, t4, t5, t6, t7, t8, t9, _this = this, _null = null,
var t2, t3, t4, t5, t6, t7, t8, t9, t10, _this = this, _null = null,
t1 = A.Localizations_of(context, B.Type_AppLocalization_KyD, type$.legacy_AppLocalization),
viewModel = _this._widget.viewModel,
invoice = viewModel.invoice,
@ -380277,27 +380358,28 @@
t2 = "";
}
t3 = A._setArrayType([B.EntityAction_viewPdf, B.EntityAction_emailInvoice], type$.JSArray_legacy_EntityAction);
t4 = invoice.statusId;
t5 = t4 !== "4";
if (t5)
t4 = invoice.entityType;
t5 = t4 === B.EntityType_invoice;
if (!(t5 && invoice.statusId === "4"))
t3.push(B.EntityAction_newPayment);
if (t4 === "1")
t6 = invoice.statusId;
if (t6 === "1")
t3.push(B.EntityAction_markSent);
if (t5)
if (!(t5 && t6 === "4"))
t3.push(B.EntityAction_markPaid);
t4 = type$.JSArray_legacy_Widget;
t5 = A.TabBar$(_this._invoice_edit$_controller, _null, true, _null, _null, A._setArrayType([A.Tab$(_null, t1.get$details(t1)), A.Tab$(_null, t1.get$contacts()), A.Tab$(_null, t1.get$items(t1)), A.Tab$(_null, t1.get$notes()), A.Tab$(_null, t1.get$pdf())], t4));
t6 = $.$get$_InvoiceEditState__formKey();
t5 = type$.JSArray_legacy_Widget;
t6 = A.TabBar$(_this._invoice_edit$_controller, _null, true, _null, _null, A._setArrayType([A.Tab$(_null, t1.get$details(t1)), A.Tab$(_null, t1.get$contacts()), A.Tab$(_null, t1.get$items(t1)), A.Tab$(_null, t1.get$notes()), A.Tab$(_null, t1.get$pdf())], t5));
t7 = $.$get$_InvoiceEditState__formKey();
if (isFullscreen)
t4 = new A.InvoiceEditDetailsScreen(_this._widget.viewModel, _null);
else {
t7 = "__invoice_" + invoice.id + "_" + invoice.updatedAt + "__";
t8 = _this._invoice_edit$_controller;
t9 = _this._widget.viewModel;
t7 = A.TabBarView$(A._setArrayType([new A.InvoiceEditDetailsScreen(t9, _null), new A.InvoiceEditContactsScreen(invoice.entityType, _null), new A.InvoiceEditItemsScreen(t9, false, _null), new A.InvoiceEditNotesScreen(_null), new A.InvoiceEditPDFScreen(_null)], t4), t8, new A.ValueKey(t7, type$.ValueKey_legacy_String));
t4 = t7;
t8 = "__invoice_" + invoice.id + "_" + invoice.updatedAt + "__";
t9 = _this._invoice_edit$_controller;
t10 = _this._widget.viewModel;
t8 = A.TabBarView$(A._setArrayType([new A.InvoiceEditDetailsScreen(t10, _null), new A.InvoiceEditContactsScreen(t4, _null), new A.InvoiceEditItemsScreen(t10, false, _null), new A.InvoiceEditNotesScreen(_null), new A.InvoiceEditPDFScreen(_null)], t5), t9, new A.ValueKey(t8, type$.ValueKey_legacy_String));
t4 = t8;
}
return A.EditScaffold$(t3, t5, A.Form$(false, t4, t6), new A.InvoiceEditFooter(invoice, _null), invoice, A.FloatingActionButton$(A.Theme_of(context).primaryColorDark, B.Icon_yXb, "invoice_edit_fab", false, new A._InvoiceEditState_build_closure(_this, context, invoice, viewModel, isFullscreen), t1.get$addItem()), isFullscreen, new A._InvoiceEditState_build_closure0(_this), new A._InvoiceEditState_build_closure1(viewModel), new A._InvoiceEditState_build_closure2(_this), _null, t2);
return A.EditScaffold$(t3, t6, A.Form$(false, t4, t7), new A.InvoiceEditFooter(invoice, _null), invoice, A.FloatingActionButton$(A.Theme_of(context).primaryColorDark, B.Icon_yXb, "invoice_edit_fab", false, new A._InvoiceEditState_build_closure(_this, context, invoice, viewModel, isFullscreen), t1.get$addItem()), isFullscreen, new A._InvoiceEditState_build_closure0(_this), new A._InvoiceEditState_build_closure1(viewModel), new A._InvoiceEditState_build_closure2(_this), _null, t2);
}
};
A._InvoiceEditState__onSavePressed_closure.prototype = {
@ -385953,7 +386035,7 @@
viewModel.onChanged.call$1(t1);
},
build$1(_, context) {
var t5, creditPaymentables, amountPlaceholder, t6, t7, t8, t9, t10, t11, t12, index, t13, body, _this = this, _null = null,
var t5, creditPaymentables, amountPlaceholder, t6, t7, t8, t9, t10, t11, t12, t13, index, body, _this = this, _null = null,
_s17_ = "__payment_amount_",
_box_0 = {},
viewModel = _this._widget.viewModel,
@ -386001,14 +386083,15 @@
t7 = type$.JSArray_legacy_Widget;
t8 = A._setArrayType([], t7);
if (payment.get$isNew()) {
t9 = payment.clientId;
t1 = A.Localizations_of(context, B.Type_AppLocalization_KyD, t1);
t1 = t1.get$client(t1);
t9 = _this.autoValidate;
t10 = $.$get$memoizedDropdownClientList();
t11 = state.uiState.selectedCompanyIndex;
t11 = state.userCompanyStates._list[t11];
t12 = t11.clientState;
t1 = A._setArrayType([A.EntityDropdown$(true, t9, true, payment.clientId, t10.call$4(t12.map, t12.list, t11.userState.map, state.staticState), _null, B.EntityType_client, t1, _null, new A._PaymentEditState_build_closure3(viewModel, payment), _null, _null, new A._PaymentEditState_build_closure4(context))], t7);
t10 = _this.autoValidate;
t11 = $.$get$memoizedDropdownClientList();
t12 = state.uiState.selectedCompanyIndex;
t12 = state.userCompanyStates._list[t12];
t13 = t12.clientState;
t1 = A._setArrayType([A.EntityDropdown$(true, t10, t9.length === 0, t9, t11.call$4(t13.map, t13.list, t12.userState.map, state.staticState), _null, B.EntityType_client, t1, _null, new A._PaymentEditState_build_closure3(viewModel, payment), _null, _null, new A._PaymentEditState_build_closure4(context))], t7);
if (!t3 && payment.isForCredit !== true) {
t9 = _box_0.paymentTotal === 0 ? t2.get$amount() : amountPlaceholder;
t1.push(A.DecoratedFormField$(false, _null, false, false, _this._payment_edit$_amountController, _null, true, _null, _null, _null, _null, false, false, _null, new A.TextInputType(2, true, true), t9, _null, _null, false, _null, _null, viewModel.onSavePressed, true, _null, _null, B.TextAlign_4, _null));

View File

@ -11,6 +11,10 @@
<p class="block text-center text-gray-600">{{ ctrans('texts.register_label') }}</p>
<form action="{{ route('client.register', request()->route('company_key')) }}" method="POST" x-data="{ more: false }">
@if($company)
<input type="hidden" name="company_key" value="{{ $company->company_key }}">
@endif
@csrf
<div class="grid grid-cols-12 gap-4 mt-10">

View File

@ -25,6 +25,8 @@
<div class="mb-4">
@include('portal.ninja2020.quotes.includes.actions', ['quote' => $quote])
</div>
@elseif($quote->status_id === \App\Models\Quote::STATUS_APPROVED)
<p class="text-right text-gray-900 text-sm mb-4">{{ ctrans('texts.approved') }}</p>
@else
<p class="text-right text-gray-900 text-sm mb-4">{{ ctrans('texts.quotes_with_status_sent_can_be_approved') }}</p>
@endif

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