Merge pull request #5188 from turbo124/v5-stable

Small fixes for 2FA
This commit is contained in:
David Bomba 2021-03-20 11:43:12 +11:00 committed by GitHub
commit 3b253cf38d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 429 additions and 71 deletions

View File

@ -7,15 +7,7 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
# Invoice Ninja version 5.1 RC2! # Invoice Ninja version 5!
Invoice Ninja version 5.1 has now reached Release Candidate 2!
What does this mean exactly? We consider this version _almost_ stable. There may be some remaining small issues which we would love to get feedback on. We would really appreciate the community booting up this version and attempting the migration from their Invoice Ninja V4 application and inspect the migrated data.
We'd also like feedback on any issues that you can see, and help us nail down the few remaining issues before Version 5 graduates to Stable Gold Release.
Please note we do not consider this version ready for production use, please stick with your V4 installation for your production clients!
## Quick Start ## Quick Start

View File

@ -171,7 +171,7 @@ class LoginController extends BaseController
//if user has 2fa enabled - lets check this now: //if user has 2fa enabled - lets check this now:
if($user->google_2fa_secret && $request->has('one_time_password')) if($user->google_2fa_secret && $request->has('one_time_password') && strlen($request->input('one_time_password')) >= 1)
{ {
$google2fa = new Google2FA(); $google2fa = new Google2FA();
@ -194,6 +194,7 @@ class LoginController extends BaseController
$user->setCompany($user->account->default_company); $user->setCompany($user->account->default_company);
$timeout = auth()->user()->company()->default_password_timeout; $timeout = auth()->user()->company()->default_password_timeout;
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout); Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::query() $cu = CompanyUser::query()
@ -322,33 +323,34 @@ class LoginController extends BaseController
if ($user) { if ($user) {
$client = new Google_Client(); // we are no longer accessing the permissions for gmail - email permissions here
$client->setClientId(config('ninja.auth.google.client_id'));
$client->setClientSecret(config('ninja.auth.google.client_secret'));
$client->setRedirectUri(config('ninja.app_url'));
$token = false; // $client = new Google_Client();
// $client->setClientId(config('ninja.auth.google.client_id'));
// $client->setClientSecret(config('ninja.auth.google.client_secret'));
// $client->setRedirectUri(config('ninja.app_url'));
try{ // $token = false;
$token = $client->authenticate(request()->input('server_auth_code'));
}
catch(\Exception $e) {
return response() // try{
->json(['message' => ctrans('texts.invalid_credentials')], 401) // $token = $client->authenticate(request()->input('server_auth_code'));
->header('X-App-Version', config('ninja.app_version')) // }
->header('X-Api-Version', config('ninja.minimum_client_version')); // catch(\Exception $e) {
} // return response()
// ->json(['message' => ctrans('texts.invalid_credentials')], 401)
// ->header('X-App-Version', config('ninja.app_version'))
// ->header('X-Api-Version', config('ninja.minimum_client_version'));
$refresh_token = ''; // }
if (array_key_exists('refresh_token', $token)) { // $refresh_token = '';
$refresh_token = $token['refresh_token'];
}
//$access_token = $token['access_token']; // if (array_key_exists('refresh_token', $token)) {
// $refresh_token = $token['refresh_token'];
// }
$name = OAuth::splitName($google->harvestName($user)); $name = OAuth::splitName($google->harvestName($user));
$new_account = [ $new_account = [

View File

@ -54,7 +54,7 @@ class QueryLogging
nlog($request->method().' - '.$request->url().": $count queries - ".$time); nlog($request->method().' - '.$request->url().": $count queries - ".$time);
// if($count > 50) // if($count > 50)
// Log::nlog($queries); //nlog($queries);
} }
} }

View File

@ -19,6 +19,7 @@ use App\Models\Client;
use App\Models\GroupSetting; use App\Models\GroupSetting;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\Rule;
class StoreClientRequest extends Request class StoreClientRequest extends Request
{ {
@ -48,7 +49,6 @@ class StoreClientRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/ /* Ensure we have a client name, and that all emails are unique*/
//$rules['name'] = 'required|min:1'; //$rules['name'] = 'required|min:1';
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
$rules['settings'] = new ValidClientGroupSettingsRule(); $rules['settings'] = new ValidClientGroupSettingsRule();
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email'; $rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
$rules['contacts.*.password'] = [ $rules['contacts.*.password'] = [
@ -66,6 +66,10 @@ class StoreClientRequest extends Request
$rules['hosted_clients'] = new CanStoreClientsRule($this->company_id); $rules['hosted_clients'] = new CanStoreClientsRule($this->company_id);
} }
$rules['number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
$rules['id_number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
return $rules; return $rules;
} }
@ -122,7 +126,7 @@ class StoreClientRequest extends Request
public function messages() public function messages()
{ {
return [ return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']), // 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
//'required' => trans('validation.required', ['attribute' => 'email']), //'required' => trans('validation.required', ['attribute' => 'email']),
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
]; ];

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Request;
use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Http\ValidationRules\ValidClientGroupSettingsRule;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateClientRequest extends Request class UpdateClientRequest extends Request
{ {
@ -52,7 +53,14 @@ class UpdateClientRequest extends Request
$rules['country_id'] = 'integer|nullable'; $rules['country_id'] = 'integer|nullable';
$rules['shipping_country_id'] = 'integer|nullable'; $rules['shipping_country_id'] = 'integer|nullable';
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id; //$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id; //$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id;
if($this->id_number)
$rules['id_number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id)->ignore($this->client->id);
if($this->number)
$rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id)->ignore($this->client->id);
$rules['settings'] = new ValidClientGroupSettingsRule(); $rules['settings'] = new ValidClientGroupSettingsRule();
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email'; $rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
$rules['contacts.*.password'] = [ $rules['contacts.*.password'] = [
@ -72,7 +80,6 @@ class UpdateClientRequest extends Request
public function messages() public function messages()
{ {
return [ return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
'email' => ctrans('validation.email', ['attribute' => 'email']), 'email' => ctrans('validation.email', ['attribute' => 'email']),
'name.required' => ctrans('validation.required', ['attribute' => 'name']), 'name.required' => ctrans('validation.required', ['attribute' => 'name']),
'required' => ctrans('validation.required', ['attribute' => 'email']), 'required' => ctrans('validation.required', ['attribute' => 'email']),

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateCreditRequest extends Request class UpdateCreditRequest extends Request
{ {
@ -52,9 +53,8 @@ class UpdateCreditRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'; $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
} }
if ($this->input('number')) { if($this->number)
$rules['number'] = 'unique:credits,number,'.$this->id.',id,company_id,'.$this->credit->company_id; $rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id);
}
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';

View File

@ -35,10 +35,9 @@ class StoreExpenseRequest extends Request
{ {
$rules = []; $rules = [];
if (isset($this->number)) { if ($this->number)
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id); $rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
}
if(!empty($this->client_id)) if(!empty($this->client_id))
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;

View File

@ -16,6 +16,7 @@ use App\Http\ValidationRules\Invoice\LockedInvoiceRule;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateInvoiceRequest extends Request class UpdateInvoiceRequest extends Request
{ {
@ -49,10 +50,8 @@ class UpdateInvoiceRequest extends Request
$rules['id'] = new LockedInvoiceRule($this->invoice); $rules['id'] = new LockedInvoiceRule($this->invoice);
// if ($this->input('number') && strlen($this->input('number')) >= 1) { if($this->number)
if ($this->input('number')) { $rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id);
$rules['number'] = 'unique:invoices,number,'.$this->id.',id,company_id,'.$this->invoice->company_id;
}
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';

View File

@ -16,6 +16,7 @@ use App\Http\ValidationRules\PaymentAppliedValidAmount;
use App\Http\ValidationRules\ValidCreditsPresentRule; use App\Http\ValidationRules\ValidCreditsPresentRule;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdatePaymentRequest extends Request class UpdatePaymentRequest extends Request
{ {
@ -35,12 +36,14 @@ class UpdatePaymentRequest extends Request
public function rules() public function rules()
{ {
$rules = [ $rules = [
'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->payment->company_id,
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule], 'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
'invoices.*.invoice_id' => 'distinct', 'invoices.*.invoice_id' => 'distinct',
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx', 'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
]; ];
if($this->number)
$rules['number'] = Rule::unique('payments')->where('company_id', auth()->user()->company()->id)->ignore($this->payment->id);
if ($this->input('documents') && is_array($this->input('documents'))) { if ($this->input('documents') && is_array($this->input('documents'))) {
$documents = count($this->input('documents')); $documents = count($this->input('documents'));

View File

@ -15,6 +15,7 @@ use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateQuoteRequest extends Request class UpdateQuoteRequest extends Request
{ {
@ -46,9 +47,8 @@ class UpdateQuoteRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'; $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
} }
if ($this->input('number')) { if($this->number)
$rules['number'] = 'unique:quotes,number,'.$this->id.',id,company_id,'.$this->quote->company_id; $rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
}
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';

View File

@ -16,6 +16,7 @@ use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Validation\Rule;
class UpdateRecurringInvoiceRequest extends Request class UpdateRecurringInvoiceRequest extends Request
{ {
@ -47,9 +48,9 @@ class UpdateRecurringInvoiceRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'; $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
} }
if ($this->input('number')) { if($this->number)
$rules['number'] = 'unique:recurring_invoices,number,'.$this->recurring_invoice->id.',id,company_id,'.$this->recurring_invoice->company_id; $rules['number'] = Rule::unique('recurring_invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_invoice->id);
}
return $rules; return $rules;
} }

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\RecurringQuote;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use Illuminate\Validation\Rule;
class UpdateRecurringQuoteRequest extends Request class UpdateRecurringQuoteRequest extends Request
{ {
@ -47,6 +48,9 @@ class UpdateRecurringQuoteRequest extends Request
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
if($this->number)
$rules['number'] = Rule::unique('recurring_quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_quote->id);
$this->replace($input); $this->replace($input);
} }
} }

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\TaxRate; namespace App\Http\Requests\TaxRate;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use Illuminate\Validation\Rule;
class UpdateTaxRateRequest extends Request class UpdateTaxRateRequest extends Request
{ {
@ -27,10 +28,14 @@ class UpdateTaxRateRequest extends Request
public function rules() public function rules()
{ {
return [ $rules = [];
// 'name' => 'unique:tax_rates,name,'.$this->tax_rate->name.',id,company_id,'.auth()->user()->companyId(),
'name' => 'unique:tax_rates,name,'.$this->id.',id,company_id,'.$this->company_id, $rules['rate'] = 'numeric';
'rate' => 'numeric',
]; if($this->number)
$rules['number'] = Rule::unique('tax_rates')->where('company_id', auth()->user()->company()->id)->ignore($this->tax_rate->id);
return $rules;
} }
} }

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\Vendor;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateVendorRequest extends Request class UpdateVendorRequest extends Request
{ {
@ -35,18 +36,15 @@ class UpdateVendorRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/ /* Ensure we have a client name, and that all emails are unique*/
$rules['country_id'] = 'integer|nullable'; $rules['country_id'] = 'integer|nullable';
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
$rules['id_number'] = 'unique:clients,id_number,'.$this->id.',id,company_id,'.$this->company_id; if($this->number)
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
if($this->id_number)
$rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
$rules['contacts.*.email'] = 'nullable|distinct'; $rules['contacts.*.email'] = 'nullable|distinct';
$contacts = request('contacts');
if (is_array($contacts)) {
// for ($i = 0; $i < count($contacts); $i++) {
// // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id);
// //$rules['contacts.' . $i . '.email'] = 'nullable|email';
// }
}
return $rules; return $rules;
} }
@ -54,7 +52,6 @@ class UpdateVendorRequest extends Request
public function messages() public function messages()
{ {
return [ return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
'email' => ctrans('validation.email', ['attribute' => 'email']), 'email' => ctrans('validation.email', ['attribute' => 'email']),
'name.required' => ctrans('validation.required', ['attribute' => 'name']), 'name.required' => ctrans('validation.required', ['attribute' => 'name']),
'required' => ctrans('validation.required', ['attribute' => 'email']), 'required' => ctrans('validation.required', ['attribute' => 'email']),

View File

@ -37,6 +37,7 @@ class Project extends BaseModel
'custom_value4', 'custom_value4',
'assigned_user_id', 'assigned_user_id',
'color', 'color',
'number',
]; ];
public function getEntityType() public function getEntityType()

View File

@ -0,0 +1,65 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddUniqueConstraintsOnAllEntities extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('expenses', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('tasks', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('vendors', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('payments', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('projects', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('clients', function (Blueprint $table) {
$table->unique(['company_id', 'number']);
});
Schema::table('payment_hashes', function (Blueprint $table) {
$table->unique(['hash']);
});
Schema::table('recurring_invoices', function (Blueprint $table) {
$table->string('number')->change();
$table->unique(['company_id', 'number']);
});
Schema::table('recurring_invoice_invitations', function (Blueprint $table) {
$table->unique(['client_contact_id', 'recurring_invoice_id'],'recur_invoice_client_unique');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -54,6 +54,28 @@ class ClientApiTest extends TestCase
$response->assertStatus(200); $response->assertStatus(200);
} }
public function testDuplicateNumberCatch()
{
$data = [
'name' => $this->faker->firstName,
'number' => 'iamaduplicate',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients', $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients', $data);
$response->assertStatus(302);
}
public function testClientPut() public function testClientPut()
{ {
$data = [ $data = [
@ -67,6 +89,20 @@ class ClientApiTest extends TestCase
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data); ])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
$response->assertStatus(200); $response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/clients/'.$this->encodePrimaryKey($this->client->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/clients/', $data);
$response->assertStatus(302);
} }
public function testClientGet() public function testClientGet()

View File

@ -131,4 +131,80 @@ class CreditTest extends TestCase
$response->assertStatus(200); $response->assertStatus(200);
} }
public function testDuplicateNumberCatch()
{
$data = [
'status_id' => 1,
'number' => 'dfdfd',
'discount' => 0,
'is_amount_discount' => 1,
'number' => '3434343',
'public_notes' => 'notes',
'is_deleted' => 0,
'custom_value1' => 0,
'custom_value2' => 0,
'custom_value3' => 0,
'custom_value4' => 0,
'status' => 1,
'client_id' => $this->encodePrimaryKey($this->client->id),
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/credits', $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/credits', $data);
$response->assertStatus(302);
}
public function testCreditPut()
{
$data = [
'status_id' => 1,
'number' => 'dfdfd',
'discount' => 0,
'is_amount_discount' => 1,
'number' => '3434343',
'public_notes' => 'notes',
'is_deleted' => 0,
'custom_value1' => 0,
'custom_value2' => 0,
'custom_value3' => 0,
'custom_value4' => 0,
'status' => 1,
'client_id' => $this->encodePrimaryKey($this->client->id),
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/credits/'.$this->encodePrimaryKey($this->credit->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/credits/'.$this->encodePrimaryKey($this->credit->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/credits/', $data);
$response->assertStatus(302);
}
} }

View File

@ -57,11 +57,34 @@ class ExpenseApiTest extends TestCase
$this->assertNotEmpty($arr['data']['number']); $this->assertNotEmpty($arr['data']['number']);
} }
public function testDuplicateNumberCatch()
{
$data = [
'public_notes' => $this->faker->firstName,
'number' => 'iamaduplicate',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/expenses', $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/expenses', $data);
$response->assertStatus(302);
}
public function testExpensePut() public function testExpensePut()
{ {
$data = [ $data = [
'public_notes' => $this->faker->firstName, 'public_notes' => $this->faker->firstName,
'id_number' => 'Coolio', 'number' => 'Coolio',
]; ];
$response = $this->withHeaders([ $response = $this->withHeaders([
@ -70,6 +93,22 @@ class ExpenseApiTest extends TestCase
])->put('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id), $data); ])->put('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id), $data);
$response->assertStatus(200); $response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/expenses/', $data);
$response->assertStatus(302);
} }
public function testExpenseGet() public function testExpenseGet()

View File

@ -125,8 +125,15 @@ class InvoiceTest extends TestCase
])->post('/api/v1/invoices/', $invoice) ])->post('/api/v1/invoices/', $invoice)
->assertStatus(200); ->assertStatus(200);
//test that the same request should produce a validation error due
//to duplicate number being used. $arr = $response->json();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/invoices/'.$arr['data']['id'], $invoice)
->assertStatus(200);
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,

View File

@ -104,6 +104,15 @@ class PaymentTest extends TestCase
$response->assertStatus(200); $response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id), $Payment->toArray());
$response->assertStatus(200);
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,

View File

@ -16,6 +16,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
use Illuminate\Validation\ValidationException;
/** /**
* @test * @test
@ -56,6 +57,7 @@ class ProjectApiTest extends TestCase
$data = [ $data = [
'name' => $this->faker->firstName, 'name' => $this->faker->firstName,
'client_id' => $this->client->hashed_id, 'client_id' => $this->client->hashed_id,
'number' => 'duplicate',
]; ];
$response = $this->withHeaders([ $response = $this->withHeaders([
@ -64,6 +66,26 @@ class ProjectApiTest extends TestCase
])->post('/api/v1/projects', $data); ])->post('/api/v1/projects', $data);
$response->assertStatus(200); $response->assertStatus(200);
$arr = $response->json();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/projects/'.$arr['data']['id'], $data)->assertStatus(200);
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/projects', $data);
}
catch(ValidationException $e){
$response->assertStatus(302);
}
} }
public function testProjectPut() public function testProjectPut()

View File

@ -86,7 +86,8 @@ class QuoteTest extends TestCase
$quote_update = [ $quote_update = [
'status_id' => Quote::STATUS_APPROVED, 'status_id' => Quote::STATUS_APPROVED,
// 'client_id' => $this->encodePrimaryKey($quote->client_id), 'client_id' => $this->encodePrimaryKey($this->quote->client_id),
'number' => 'Rando',
]; ];
$this->assertNotNull($this->quote); $this->assertNotNull($this->quote);
@ -98,6 +99,22 @@ class QuoteTest extends TestCase
$response->assertStatus(200); $response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/quotes/'.$this->encodePrimaryKey($this->quote->id), $quote_update);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/quotes/', $quote_update);
$response->assertStatus(302);
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,

View File

@ -117,6 +117,7 @@ class RecurringInvoiceTest extends TestCase
$RecurringInvoice_update = [ $RecurringInvoice_update = [
'status_id' => RecurringInvoice::STATUS_DRAFT, 'status_id' => RecurringInvoice::STATUS_DRAFT,
'client_id' => $this->encodePrimaryKey($RecurringInvoice->client_id), 'client_id' => $this->encodePrimaryKey($RecurringInvoice->client_id),
'number' => 'customnumber'
]; ];
$this->assertNotNull($RecurringInvoice); $this->assertNotNull($RecurringInvoice);
@ -127,6 +128,26 @@ class RecurringInvoiceTest extends TestCase
])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update) ])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update)
->assertStatus(200); ->assertStatus(200);
$arr = $response->json();
$this->assertEquals('customnumber', $arr['data']['number']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update)
->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/recurring_invoices/', $RecurringInvoice_update)
->assertStatus(302);
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,

View File

@ -14,6 +14,7 @@ use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
@ -44,6 +45,7 @@ class TaskApiTest extends TestCase
{ {
$data = [ $data = [
'description' => $this->faker->firstName, 'description' => $this->faker->firstName,
'number' => 'taskynumber'
]; ];
$response = $this->withHeaders([ $response = $this->withHeaders([
@ -54,6 +56,27 @@ class TaskApiTest extends TestCase
$arr = $response->json(); $arr = $response->json();
$response->assertStatus(200); $response->assertStatus(200);
$this->assertEquals('taskynumber', $arr['data']['number']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/tasks/'.$arr['data']['id'], $data);
$response->assertStatus(200);
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/tasks', $data);
$arr = $response->json();
}catch(ValidationException $e){
$response->assertStatus(302);
}
$this->assertNotEmpty($arr['data']['number']); $this->assertNotEmpty($arr['data']['number']);
} }

View File

@ -14,6 +14,7 @@ use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
@ -59,6 +60,7 @@ class VendorApiTest extends TestCase
$data = [ $data = [
'name' => $this->faker->firstName, 'name' => $this->faker->firstName,
'id_number' => 'Coolio', 'id_number' => 'Coolio',
'number' => 'wiggles'
]; ];
$response = $this->withHeaders([ $response = $this->withHeaders([
@ -67,6 +69,33 @@ class VendorApiTest extends TestCase
])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data); ])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data);
$response->assertStatus(200); $response->assertStatus(200);
$arr = $response->json();
$this->assertEquals('Coolio', $arr['data']['id_number']);
$this->assertEquals('wiggles', $arr['data']['number']);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data);
$response->assertStatus(200);
try{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/vendors/', $data);
}catch(ValidationException $e){
$response->assertStatus(302);
}
} }
public function testVendorGet() public function testVendorGet()